summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-02-08 16:39:37 +0100
committerLaurenz <laurmaedje@gmail.com>2022-02-09 12:34:19 +0100
commite089b6ea40015e012302dc55ac5d6cb42ca4876e (patch)
treedbb66237cb996bc880560dfd94ac9b682e1ac985 /src/library
parent68503b9a07b00bce3f4d377bcfe945452de815ea (diff)
Set rules for everything
Diffstat (limited to 'src/library')
-rw-r--r--src/library/align.rs4
-rw-r--r--src/library/columns.rs7
-rw-r--r--src/library/container.rs2
-rw-r--r--src/library/deco.rs76
-rw-r--r--src/library/flow.rs34
-rw-r--r--src/library/grid.rs8
-rw-r--r--src/library/heading.rs78
-rw-r--r--src/library/hide.rs2
-rw-r--r--src/library/image.rs5
-rw-r--r--src/library/link.rs34
-rw-r--r--src/library/list.rs57
-rw-r--r--src/library/math.rs32
-rw-r--r--src/library/mod.rs20
-rw-r--r--src/library/pad.rs4
-rw-r--r--src/library/page.rs296
-rw-r--r--src/library/par.rs2
-rw-r--r--src/library/place.rs4
-rw-r--r--src/library/raw.rs117
-rw-r--r--src/library/shape.rs10
-rw-r--r--src/library/spacing.rs6
-rw-r--r--src/library/stack.rs4
-rw-r--r--src/library/table.rs19
-rw-r--r--src/library/text.rs125
-rw-r--r--src/library/transform.rs7
24 files changed, 531 insertions, 422 deletions
diff --git a/src/library/align.rs b/src/library/align.rs
index 5d89dcb6..5dc12938 100644
--- a/src/library/align.rs
+++ b/src/library/align.rs
@@ -9,14 +9,14 @@ pub struct AlignNode {
/// How to align the node horizontally and vertically.
pub aligns: Spec<Option<Align>>,
/// The node to be aligned.
- pub child: PackedNode,
+ pub child: LayoutNode,
}
#[class]
impl AlignNode {
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
let aligns: Spec<_> = args.find().unwrap_or_default();
- let body: PackedNode = args.expect("body")?;
+ let body: LayoutNode = args.expect("body")?;
Ok(Template::block(body.aligned(aligns)))
}
}
diff --git a/src/library/columns.rs b/src/library/columns.rs
index 9e696b49..98989c5a 100644
--- a/src/library/columns.rs
+++ b/src/library/columns.rs
@@ -10,7 +10,7 @@ pub struct ColumnsNode {
pub columns: NonZeroUsize,
/// The child to be layouted into the columns. Most likely, this should be a
/// flow or stack node.
- pub child: PackedNode,
+ pub child: LayoutNode,
}
#[class]
@@ -24,11 +24,6 @@ impl ColumnsNode {
child: args.expect("body")?,
}))
}
-
- fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
- styles.set_opt(Self::GUTTER, args.named("gutter")?);
- Ok(())
- }
}
impl Layout for ColumnsNode {
diff --git a/src/library/container.rs b/src/library/container.rs
index d2a4f481..143bddfc 100644
--- a/src/library/container.rs
+++ b/src/library/container.rs
@@ -10,7 +10,7 @@ impl BoxNode {
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
let width = args.named("width")?;
let height = args.named("height")?;
- let body: PackedNode = args.find().unwrap_or_default();
+ let body: LayoutNode = args.find().unwrap_or_default();
Ok(Template::inline(body.sized(Spec::new(width, height))))
}
}
diff --git a/src/library/deco.rs b/src/library/deco.rs
index ec2235bc..40ef4e73 100644
--- a/src/library/deco.rs
+++ b/src/library/deco.rs
@@ -4,57 +4,67 @@ use super::prelude::*;
use super::TextNode;
/// Typeset underline, striken-through or overlined text.
-pub struct DecoNode<L: LineKind>(pub L);
+#[derive(Debug, Hash)]
+pub struct DecoNode<L: LineKind> {
+ /// The kind of line.
+ pub kind: L,
+ /// The decorated contents.
+ pub body: Template,
+}
#[class]
impl<L: LineKind> DecoNode<L> {
+ /// Stroke color of the line, defaults to the text color if `None`.
+ #[shorthand]
+ pub const STROKE: Option<Paint> = None;
+ /// Thickness of the line's strokes (dependent on scaled font size), read
+ /// from the font tables if `None`.
+ #[shorthand]
+ pub const THICKNESS: Option<Linear> = None;
+ /// Position of the line relative to the baseline (dependent on scaled font
+ /// size), read from the font tables if `None`.
+ pub const OFFSET: Option<Linear> = None;
+ /// Amount that the line will be longer or shorter than its associated text
+ /// (dependent on scaled font size).
+ pub const EXTENT: Linear = Linear::zero();
+ /// Whether the line skips sections in which it would collide
+ /// with the glyphs. Does not apply to strikethrough.
+ pub const EVADE: bool = true;
+
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
- let deco = Decoration {
+ Ok(Template::show(Self {
+ kind: L::default(),
+ body: args.expect::<Template>("body")?,
+ }))
+ }
+}
+
+impl<L: LineKind> Show for DecoNode<L> {
+ fn show(&self, styles: StyleChain) -> Template {
+ self.body.clone().styled(TextNode::LINES, vec![Decoration {
line: L::LINE,
- stroke: args.named("stroke")?.or_else(|| args.find()),
- thickness: args.named::<Linear>("thickness")?.or_else(|| args.find()),
- offset: args.named("offset")?,
- extent: args.named("extent")?.unwrap_or_default(),
- evade: args.named("evade")?.unwrap_or(true),
- };
- Ok(args.expect::<Template>("body")?.styled(TextNode::LINES, vec![deco]))
+ stroke: styles.get(Self::STROKE),
+ thickness: styles.get(Self::THICKNESS),
+ offset: styles.get(Self::OFFSET),
+ extent: styles.get(Self::EXTENT),
+ evade: styles.get(Self::EVADE),
+ }])
}
}
/// Defines a line that is positioned over, under or on top of text.
+///
+/// For more details, see [`DecoNode`].
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Decoration {
- /// Which line to draw.
pub line: DecoLine,
- /// Stroke color of the line, defaults to the text color if `None`.
pub stroke: Option<Paint>,
- /// Thickness of the line's strokes (dependent on scaled font size), read
- /// from the font tables if `None`.
pub thickness: Option<Linear>,
- /// Position of the line relative to the baseline (dependent on scaled font
- /// size), read from the font tables if `None`.
pub offset: Option<Linear>,
- /// Amount that the line will be longer or shorter than its associated text
- /// (dependent on scaled font size).
pub extent: Linear,
- /// Whether the line skips sections in which it would collide
- /// with the glyphs. Does not apply to strikethrough.
pub evade: bool,
}
-impl From<DecoLine> for Decoration {
- fn from(line: DecoLine) -> Self {
- Self {
- line,
- stroke: None,
- thickness: None,
- offset: None,
- extent: Linear::zero(),
- evade: true,
- }
- }
-}
-
/// The kind of decorative line.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum DecoLine {
@@ -67,7 +77,7 @@ pub enum DecoLine {
}
/// Different kinds of decorative lines for text.
-pub trait LineKind {
+pub trait LineKind: Debug + Default + Hash + Sync + Send + 'static {
const LINE: DecoLine;
}
diff --git a/src/library/flow.rs b/src/library/flow.rs
index bba296a0..ca554033 100644
--- a/src/library/flow.rs
+++ b/src/library/flow.rs
@@ -13,6 +13,8 @@ pub struct FlowNode(pub StyleVec<FlowChild>);
/// A child of a flow node.
#[derive(Hash)]
pub enum FlowChild {
+ /// Leading between other children.
+ Leading,
/// A paragraph / block break.
Parbreak,
/// A column / region break.
@@ -20,7 +22,7 @@ pub enum FlowChild {
/// Vertical spacing between other children.
Spacing(SpacingKind),
/// An arbitrary block-level node.
- Node(PackedNode),
+ Node(LayoutNode),
}
impl Layout for FlowNode {
@@ -35,10 +37,17 @@ impl Layout for FlowNode {
for (child, map) in self.0.iter() {
let styles = map.chain(&styles);
match child {
+ FlowChild::Leading => {
+ let em = styles.get(TextNode::SIZE).abs;
+ let amount = styles.get(ParNode::LEADING).resolve(em);
+ layouter.layout_spacing(amount.into());
+ }
FlowChild::Parbreak => {
let em = styles.get(TextNode::SIZE).abs;
- let amount = styles.get(ParNode::SPACING).resolve(em);
- layouter.layout_spacing(SpacingKind::Linear(amount.into()));
+ let leading = styles.get(ParNode::LEADING);
+ let spacing = styles.get(ParNode::SPACING);
+ let amount = (leading + spacing).resolve(em);
+ layouter.layout_spacing(amount.into());
}
FlowChild::Colbreak => {
layouter.finish_region();
@@ -72,6 +81,7 @@ impl Debug for FlowNode {
impl Debug for FlowChild {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
+ Self::Leading => f.pad("Leading"),
Self::Parbreak => f.pad("Parbreak"),
Self::Colbreak => f.pad("Colbreak"),
Self::Spacing(kind) => write!(f, "{:?}", kind),
@@ -93,8 +103,6 @@ pub struct FlowLayouter {
used: Size,
/// The sum of fractional ratios in the current region.
fr: Fractional,
- /// Whether to add leading before the next node.
- leading: bool,
/// Spacing and layouted nodes.
items: Vec<FlowItem>,
/// Finished frames for previous regions.
@@ -129,7 +137,6 @@ impl FlowLayouter {
full,
used: Size::zero(),
fr: Fractional::zero(),
- leading: false,
items: vec![],
finished: vec![],
}
@@ -149,7 +156,6 @@ impl FlowLayouter {
SpacingKind::Fractional(v) => {
self.items.push(FlowItem::Fractional(v));
self.fr += v;
- self.leading = false;
}
}
}
@@ -158,7 +164,7 @@ impl FlowLayouter {
pub fn layout_node(
&mut self,
ctx: &mut LayoutContext,
- node: &PackedNode,
+ node: &LayoutNode,
styles: StyleChain,
) {
// Don't even try layouting into a full region.
@@ -168,9 +174,7 @@ impl FlowLayouter {
// Placed nodes that are out of flow produce placed items which aren't
// aligned later.
- let mut is_placed = false;
if let Some(placed) = node.downcast::<PlaceNode>() {
- is_placed = true;
if placed.out_of_flow() {
let frame = node.layout(ctx, &self.regions, styles).remove(0);
self.items.push(FlowItem::Placed(frame.item));
@@ -178,13 +182,6 @@ impl FlowLayouter {
}
}
- // Add leading.
- if self.leading {
- let em = styles.get(TextNode::SIZE).abs;
- let amount = styles.get(ParNode::LEADING).resolve(em);
- self.layout_spacing(SpacingKind::Linear(amount.into()));
- }
-
// How to align the node.
let aligns = Spec::new(
// For non-expanding paragraphs it is crucial that we align the
@@ -210,8 +207,6 @@ impl FlowLayouter {
self.finish_region();
}
}
-
- self.leading = !is_placed;
}
/// Finish the frame for one region.
@@ -264,7 +259,6 @@ impl FlowLayouter {
self.full = self.regions.current;
self.used = Size::zero();
self.fr = Fractional::zero();
- self.leading = false;
self.finished.push(output.constrain(cts));
}
diff --git a/src/library/grid.rs b/src/library/grid.rs
index 59fe8486..f85e5d56 100644
--- a/src/library/grid.rs
+++ b/src/library/grid.rs
@@ -10,7 +10,7 @@ pub struct GridNode {
/// Defines sizing of gutter rows and columns between content.
pub gutter: Spec<Vec<TrackSizing>>,
/// The nodes to be arranged in a grid.
- pub children: Vec<PackedNode>,
+ pub children: Vec<LayoutNode>,
}
#[class]
@@ -92,7 +92,7 @@ castable! {
/// Performs grid layout.
pub struct GridLayouter<'a> {
/// The grid cells.
- cells: &'a [PackedNode],
+ cells: &'a [LayoutNode],
/// The column tracks including gutter tracks.
cols: Vec<TrackSizing>,
/// The row tracks including gutter tracks.
@@ -136,7 +136,7 @@ impl<'a> GridLayouter<'a> {
pub fn new(
tracks: Spec<&[TrackSizing]>,
gutter: Spec<&[TrackSizing]>,
- cells: &'a [PackedNode],
+ cells: &'a [LayoutNode],
regions: &Regions,
styles: StyleChain<'a>,
) -> Self {
@@ -606,7 +606,7 @@ impl<'a> GridLayouter<'a> {
///
/// Returns `None` if it's a gutter cell.
#[track_caller]
- fn cell(&self, x: usize, y: usize) -> Option<&'a PackedNode> {
+ fn cell(&self, x: usize, y: usize) -> Option<&'a LayoutNode> {
assert!(x < self.cols.len());
assert!(y < self.rows.len());
diff --git a/src/library/heading.rs b/src/library/heading.rs
index ab0864e7..00a48021 100644
--- a/src/library/heading.rs
+++ b/src/library/heading.rs
@@ -9,8 +9,8 @@ pub struct HeadingNode {
/// The logical nesting depth of the section, starting from one. In the
/// default style, this controls the text size of the heading.
pub level: usize,
- /// The node that produces the heading's contents.
- pub child: PackedNode,
+ /// The heading's contents.
+ pub body: Template,
}
#[class]
@@ -23,70 +23,76 @@ impl HeadingNode {
/// The fill color of text in the heading. Just the surrounding text color
/// if `auto`.
pub const FILL: Smart<Paint> = Smart::Auto;
+ /// Whether text in the heading is strengthend.
+ pub const STRONG: bool = true;
+ /// Whether text in the heading is emphasized.
+ pub const EMPH: bool = false;
+ /// Whether the heading is underlined.
+ pub const UNDERLINE: bool = false;
/// The extra padding above the heading.
pub const ABOVE: Length = Length::zero();
/// The extra padding below the heading.
pub const BELOW: Length = Length::zero();
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
- Ok(Template::block(Self {
- child: args.expect("body")?,
+ Ok(Template::show(Self {
+ body: args.expect("body")?,
level: args.named("level")?.unwrap_or(1),
}))
}
-
- fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
- styles.set_opt(Self::FAMILY, args.named("family")?);
- styles.set_opt(Self::SIZE, args.named("size")?);
- styles.set_opt(Self::FILL, args.named("fill")?);
- styles.set_opt(Self::ABOVE, args.named("above")?);
- styles.set_opt(Self::BELOW, args.named("below")?);
- Ok(())
- }
}
-impl Layout for HeadingNode {
- fn layout(
- &self,
- ctx: &mut LayoutContext,
- regions: &Regions,
- styles: StyleChain,
- ) -> Vec<Constrained<Arc<Frame>>> {
- let upscale = (1.6 - 0.1 * self.level as f64).max(0.75);
+impl Show for HeadingNode {
+ fn show(&self, styles: StyleChain) -> Template {
+ let mut map = StyleMap::new();
- let mut passed = StyleMap::new();
- passed.set(TextNode::STRONG, true);
- passed.set(
+ let upscale = (1.6 - 0.1 * self.level as f64).max(0.75);
+ map.set(
TextNode::SIZE,
styles.get(Self::SIZE).unwrap_or(Relative::new(upscale).into()),
);
if let Smart::Custom(family) = styles.get_ref(Self::FAMILY) {
- passed.set(
- TextNode::FAMILY_LIST,
+ map.set(
+ TextNode::FAMILY,
std::iter::once(family)
- .chain(styles.get_ref(TextNode::FAMILY_LIST))
+ .chain(styles.get_ref(TextNode::FAMILY))
.cloned()
.collect(),
);
}
if let Smart::Custom(fill) = styles.get(Self::FILL) {
- passed.set(TextNode::FILL, fill);
+ map.set(TextNode::FILL, fill);
+ }
+
+ if styles.get(Self::STRONG) {
+ map.set(TextNode::STRONG, true);
+ }
+
+ if styles.get(Self::EMPH) {
+ map.set(TextNode::EMPH, true);
+ }
+
+ let mut body = self.body.clone();
+ if styles.get(Self::UNDERLINE) {
+ body = body.underlined();
}
- let mut frames = self.child.layout(ctx, regions, passed.chain(&styles));
+ let mut seq = vec![];
let above = styles.get(Self::ABOVE);
- let below = styles.get(Self::BELOW);
+ if !above.is_zero() {
+ seq.push(Template::Vertical(above.into()));
+ }
+
+ seq.push(body);
- // FIXME: Constraints and region size.
- for Constrained { item: frame, .. } in &mut frames {
- let frame = Arc::make_mut(frame);
- frame.size.y += above + below;
- frame.translate(Point::with_y(above));
+ let below = styles.get(Self::BELOW);
+ if !below.is_zero() {
+ seq.push(Template::Vertical(below.into()));
}
- frames
+ Template::block(Template::sequence(seq).styled_with_map(map))
}
}
diff --git a/src/library/hide.rs b/src/library/hide.rs
index cfef8f73..72d8e52b 100644
--- a/src/library/hide.rs
+++ b/src/library/hide.rs
@@ -4,7 +4,7 @@ use super::prelude::*;
/// Hide a node without affecting layout.
#[derive(Debug, Hash)]
-pub struct HideNode(pub PackedNode);
+pub struct HideNode(pub LayoutNode);
#[class]
impl HideNode {
diff --git a/src/library/image.rs b/src/library/image.rs
index 309ffad3..59e30364 100644
--- a/src/library/image.rs
+++ b/src/library/image.rs
@@ -31,11 +31,6 @@ impl ImageNode {
ImageNode(id).pack().sized(Spec::new(width, height)),
))
}
-
- fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
- styles.set_opt(Self::FIT, args.named("fit")?);
- Ok(())
- }
}
impl Layout for ImageNode {
diff --git a/src/library/link.rs b/src/library/link.rs
index a27908c7..a43c5290 100644
--- a/src/library/link.rs
+++ b/src/library/link.rs
@@ -5,10 +5,22 @@ use super::TextNode;
use crate::util::EcoString;
/// Link text and other elements to an URL.
-pub struct LinkNode;
+#[derive(Debug, Hash)]
+pub struct LinkNode {
+ /// The url the link points to.
+ pub url: EcoString,
+ /// How the link is represented.
+ pub body: Template,
+}
#[class]
impl LinkNode {
+ /// The fill color of text in the link. Just the surrounding text color
+ /// if `auto`.
+ pub const FILL: Smart<Paint> = Smart::Auto;
+ /// Whether to underline link.
+ pub const UNDERLINE: bool = true;
+
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
let url = args.expect::<EcoString>("url")?;
let body = args.find().unwrap_or_else(|| {
@@ -20,6 +32,24 @@ impl LinkNode {
Template::Text(if shorter { text.into() } else { url.clone() })
});
- Ok(body.styled(TextNode::LINK, Some(url)))
+ Ok(Template::show(Self { url, body }))
+ }
+}
+
+impl Show for LinkNode {
+ fn show(&self, styles: StyleChain) -> Template {
+ let mut map = StyleMap::new();
+ map.set(TextNode::LINK, Some(self.url.clone()));
+
+ if let Smart::Custom(fill) = styles.get(Self::FILL) {
+ map.set(TextNode::FILL, fill);
+ }
+
+ let mut body = self.body.clone();
+ if styles.get(Self::UNDERLINE) {
+ body = body.underlined();
+ }
+
+ body.styled_with_map(map)
}
}
diff --git a/src/library/list.rs b/src/library/list.rs
index 639b5715..0c0e61ad 100644
--- a/src/library/list.rs
+++ b/src/library/list.rs
@@ -5,15 +5,15 @@ use super::{GridNode, TextNode, TrackSizing};
/// An unordered or ordered list.
#[derive(Debug, Hash)]
-pub struct ListNode<L: ListKind> {
- /// The list labelling style -- unordered or ordered.
- pub kind: L,
+pub struct ListNode<L: ListLabel> {
+ /// The list label -- unordered or ordered with index.
+ pub label: L,
/// The node that produces the item's body.
- pub child: PackedNode,
+ pub child: LayoutNode,
}
#[class]
-impl<L: ListKind> ListNode<L> {
+impl<L: ListLabel> ListNode<L> {
/// The indentation of each item's label.
pub const LABEL_INDENT: Linear = Relative::new(0.0).into();
/// The space between the label and the body of each item.
@@ -22,29 +22,19 @@ impl<L: ListKind> ListNode<L> {
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
Ok(args
.all()
- .map(|child: PackedNode| Template::block(Self { kind: L::default(), child }))
+ .enumerate()
+ .map(|(i, child)| Template::show(Self { label: L::new(1 + i), child }))
.sum())
}
-
- fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
- styles.set_opt(Self::LABEL_INDENT, args.named("label-indent")?);
- styles.set_opt(Self::BODY_INDENT, args.named("body-indent")?);
- Ok(())
- }
}
-impl<L: ListKind> Layout for ListNode<L> {
- fn layout(
- &self,
- ctx: &mut LayoutContext,
- regions: &Regions,
- styles: StyleChain,
- ) -> Vec<Constrained<Arc<Frame>>> {
+impl<L: ListLabel> Show for ListNode<L> {
+ fn show(&self, styles: StyleChain) -> Template {
let em = styles.get(TextNode::SIZE).abs;
let label_indent = styles.get(Self::LABEL_INDENT).resolve(em);
let body_indent = styles.get(Self::BODY_INDENT).resolve(em);
- let grid = GridNode {
+ Template::block(GridNode {
tracks: Spec::with_x(vec![
TrackSizing::Linear(label_indent.into()),
TrackSizing::Auto,
@@ -53,19 +43,20 @@ impl<L: ListKind> Layout for ListNode<L> {
]),
gutter: Spec::default(),
children: vec![
- PackedNode::default(),
- Template::Text(self.kind.label()).pack(),
- PackedNode::default(),
+ LayoutNode::default(),
+ Template::Text(self.label.label()).pack(),
+ LayoutNode::default(),
self.child.clone(),
],
- };
-
- grid.layout(ctx, regions, styles)
+ })
}
}
/// How to label a list.
-pub trait ListKind: Debug + Default + Hash + Sync + Send + 'static {
+pub trait ListLabel: Debug + Default + Hash + Sync + Send + 'static {
+ /// Create a new list label.
+ fn new(number: usize) -> Self;
+
/// Return the item's label.
fn label(&self) -> EcoString;
}
@@ -74,7 +65,11 @@ pub trait ListKind: Debug + Default + Hash + Sync + Send + 'static {
#[derive(Debug, Default, Hash)]
pub struct Unordered;
-impl ListKind for Unordered {
+impl ListLabel for Unordered {
+ fn new(_: usize) -> Self {
+ Self
+ }
+
fn label(&self) -> EcoString {
'•'.into()
}
@@ -84,7 +79,11 @@ impl ListKind for Unordered {
#[derive(Debug, Default, Hash)]
pub struct Ordered(pub Option<usize>);
-impl ListKind for Ordered {
+impl ListLabel for Ordered {
+ fn new(number: usize) -> Self {
+ Self(Some(number))
+ }
+
fn label(&self) -> EcoString {
format_eco!("{}.", self.0.unwrap_or(1))
}
diff --git a/src/library/math.rs b/src/library/math.rs
new file mode 100644
index 00000000..c75cdea8
--- /dev/null
+++ b/src/library/math.rs
@@ -0,0 +1,32 @@
+//! Mathematical formulas.
+
+use super::prelude::*;
+
+/// A mathematical formula.
+#[derive(Debug, Hash)]
+pub struct MathNode {
+ /// The formula.
+ pub formula: EcoString,
+ /// Whether the formula is display-level.
+ pub display: bool,
+}
+
+#[class]
+impl MathNode {
+ fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
+ Ok(Template::show(Self {
+ formula: args.expect("formula")?,
+ display: args.named("display")?.unwrap_or(false),
+ }))
+ }
+}
+
+impl Show for MathNode {
+ fn show(&self, _: StyleChain) -> Template {
+ let mut template = Template::Text(self.formula.trim().into());
+ if self.display {
+ template = Template::Block(template.pack());
+ }
+ template.monospaced()
+ }
+}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index ef0fa016..b2cb8698 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -14,18 +14,20 @@ pub mod hide;
pub mod image;
pub mod link;
pub mod list;
+pub mod math;
pub mod pad;
pub mod page;
pub mod par;
pub mod place;
+pub mod raw;
pub mod shape;
pub mod spacing;
pub mod stack;
pub mod table;
pub mod text;
pub mod transform;
-pub mod utility;
+pub mod utility;
pub use self::image::*;
pub use align::*;
pub use columns::*;
@@ -37,10 +39,12 @@ pub use heading::*;
pub use hide::*;
pub use link::*;
pub use list::*;
+pub use math::*;
pub use pad::*;
pub use page::*;
pub use par::*;
pub use place::*;
+pub use raw::*;
pub use shape::*;
pub use spacing::*;
pub use stack::*;
@@ -68,13 +72,13 @@ prelude! {
pub use crate::diag::{At, TypResult};
pub use crate::eval::{
- Args, Construct, EvalContext, Merge, Property, Scope, Set, Smart, StyleChain,
- StyleMap, StyleVec, Template, Value,
+ Args, Construct, EvalContext, Merge, Property, Scope, Set, Show, ShowNode, Smart,
+ StyleChain, StyleMap, StyleVec, Template, Value,
};
pub use crate::frame::*;
pub use crate::geom::*;
pub use crate::layout::{
- Constrain, Constrained, Constraints, Layout, LayoutContext, PackedNode, Regions,
+ Constrain, Constrained, Constraints, Layout, LayoutContext, LayoutNode, Regions,
};
pub use crate::syntax::{Span, Spanned};
pub use crate::util::{EcoString, OptionExt};
@@ -95,6 +99,8 @@ pub fn new() -> Scope {
std.def_class::<TextNode>("text");
std.def_class::<StrongNode>("strong");
std.def_class::<EmphNode>("emph");
+ std.def_class::<RawNode>("raw");
+ std.def_class::<MathNode>("math");
std.def_class::<DecoNode<Underline>>("underline");
std.def_class::<DecoNode<Strikethrough>>("strike");
std.def_class::<DecoNode<Overline>>("overline");
@@ -207,9 +213,9 @@ castable! {
castable! {
NonZeroUsize,
Expected: "positive integer",
- Value::Int(int) => int
+ Value::Int(int) => Value::Int(int)
+ .cast::<usize>()?
.try_into()
- .and_then(usize::try_into)
.map_err(|_| "must be positive")?,
}
@@ -226,7 +232,7 @@ castable! {
}
castable! {
- PackedNode,
+ LayoutNode,
Expected: "template",
Value::Template(template) => template.pack(),
}
diff --git a/src/library/pad.rs b/src/library/pad.rs
index af570659..b65057dc 100644
--- a/src/library/pad.rs
+++ b/src/library/pad.rs
@@ -8,7 +8,7 @@ pub struct PadNode {
/// The amount of padding.
pub padding: Sides<Linear>,
/// The child node whose sides to pad.
- pub child: PackedNode,
+ pub child: LayoutNode,
}
#[class]
@@ -19,7 +19,7 @@ impl PadNode {
let top = args.named("top")?;
let right = args.named("right")?;
let bottom = args.named("bottom")?;
- let body: PackedNode = args.expect("body")?;
+ let body: LayoutNode = args.expect("body")?;
let padding = Sides::new(
left.or(all).unwrap_or_default(),
top.or(all).unwrap_or_default(),
diff --git a/src/library/page.rs b/src/library/page.rs
index cdb78cc1..d54fefb0 100644
--- a/src/library/page.rs
+++ b/src/library/page.rs
@@ -8,16 +8,14 @@ use super::ColumnsNode;
/// Layouts its child onto one or multiple pages.
#[derive(Clone, PartialEq, Hash)]
-pub struct PageNode(pub PackedNode);
+pub struct PageNode(pub LayoutNode);
#[class]
impl PageNode {
/// The unflipped width of the page.
- pub const WIDTH: Smart<Length> = Smart::Custom(Paper::default().width());
+ pub const WIDTH: Smart<Length> = Smart::Custom(Paper::A4.width());
/// The unflipped height of the page.
- pub const HEIGHT: Smart<Length> = Smart::Custom(Paper::default().height());
- /// The class of paper. Defines the default margins.
- pub const CLASS: PaperClass = Paper::default().class();
+ pub const HEIGHT: Smart<Length> = Smart::Custom(Paper::A4.height());
/// Whether the page is flipped into landscape orientation.
pub const FLIPPED: bool = false;
/// The left margin.
@@ -39,20 +37,12 @@ impl PageNode {
fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
if let Some(paper) = args.named::<Paper>("paper")?.or_else(|| args.find()) {
- styles.set(Self::CLASS, paper.class());
styles.set(Self::WIDTH, Smart::Custom(paper.width()));
styles.set(Self::HEIGHT, Smart::Custom(paper.height()));
}
- if let Some(width) = args.named("width")? {
- styles.set(Self::CLASS, PaperClass::Custom);
- styles.set(Self::WIDTH, width);
- }
-
- if let Some(height) = args.named("height")? {
- styles.set(Self::CLASS, PaperClass::Custom);
- styles.set(Self::HEIGHT, height);
- }
+ styles.set_opt(Self::WIDTH, args.named("width")?);
+ styles.set_opt(Self::HEIGHT, args.named("height")?);
let margins = args.named("margins")?;
styles.set_opt(Self::LEFT, args.named("left")?.or(margins));
@@ -80,14 +70,18 @@ impl PageNode {
std::mem::swap(&mut size.x, &mut size.y);
}
+ let mut min = width.min(height);
+ if min.is_infinite() {
+ min = Paper::A4.width();
+ }
+
// Determine the margins.
- let class = styles.get(Self::CLASS);
- let default = class.default_margins();
+ let default = Linear::from(0.1190 * min);
let padding = Sides {
- left: styles.get(Self::LEFT).unwrap_or(default.left),
- right: styles.get(Self::RIGHT).unwrap_or(default.right),
- top: styles.get(Self::TOP).unwrap_or(default.top),
- bottom: styles.get(Self::BOTTOM).unwrap_or(default.bottom),
+ left: styles.get(Self::LEFT).unwrap_or(default),
+ right: styles.get(Self::RIGHT).unwrap_or(default),
+ top: styles.get(Self::TOP).unwrap_or(default),
+ bottom: styles.get(Self::BOTTOM).unwrap_or(default),
};
let mut child = self.0.clone();
@@ -138,8 +132,6 @@ impl PagebreakNode {
/// Specification of a paper.
#[derive(Debug, Copy, Clone)]
pub struct Paper {
- /// The broad class this paper belongs to.
- class: PaperClass,
/// The width of the paper in millimeters.
width: f64,
/// The height of the paper in millimeters.
@@ -147,11 +139,6 @@ pub struct Paper {
}
impl Paper {
- /// The class of the paper.
- pub fn class(self) -> PaperClass {
- self.class
- }
-
/// The width of the paper.
pub fn width(self) -> Length {
Length::mm(self.width)
@@ -163,24 +150,14 @@ impl Paper {
}
}
-impl Default for Paper {
- fn default() -> Self {
- Paper::A4
- }
-}
-
/// Defines paper constants and a paper parsing implementation.
macro_rules! papers {
- ($(($var:ident: $class:ident, $width:expr, $height: expr, $($pats:tt)*))*) => {
+ ($(($var:ident: $width:expr, $height: expr, $($pats:tt)*))*) => {
/// Predefined papers.
///
/// Each paper is parsable from its name in kebab-case.
impl Paper {
- $(pub const $var: Self = Self {
- class: PaperClass::$class,
- width: $width,
- height: $height,
- };)*
+ $(pub const $var: Self = Self { width: $width, height: $height };)*
}
impl FromStr for Paper {
@@ -206,149 +183,149 @@ macro_rules! papers {
papers! {
// ---------------------------------------------------------------------- //
// ISO 216 A Series
- (A0: Base, 841.0, 1189.0, "a0")
- (A1: Base, 594.0, 841.0, "a1")
- (A2: Base, 420.0, 594.0, "a2")
- (A3: Base, 297.0, 420.0, "a3")
- (A4: Base, 210.0, 297.0, "a4")
- (A5: Base, 148.0, 210.0, "a5")
- (A6: Book, 105.0, 148.0, "a6")
- (A7: Base, 74.0, 105.0, "a7")
- (A8: Base, 52.0, 74.0, "a8")
- (A9: Base, 37.0, 52.0, "a9")
- (A10: Base, 26.0, 37.0, "a10")
- (A11: Base, 18.0, 26.0, "a11")
+ (A0: 841.0, 1189.0, "a0")
+ (A1: 594.0, 841.0, "a1")
+ (A2: 420.0, 594.0, "a2")
+ (A3: 297.0, 420.0, "a3")
+ (A4: 210.0, 297.0, "a4")
+ (A5: 148.0, 210.0, "a5")
+ (A6: 105.0, 148.0, "a6")
+ (A7: 74.0, 105.0, "a7")
+ (A8: 52.0, 74.0, "a8")
+ (A9: 37.0, 52.0, "a9")
+ (A10: 26.0, 37.0, "a10")
+ (A11: 18.0, 26.0, "a11")
// ISO 216 B Series
- (ISO_B1: Base, 707.0, 1000.0, "iso-b1")
- (ISO_B2: Base, 500.0, 707.0, "iso-b2")
- (ISO_B3: Base, 353.0, 500.0, "iso-b3")
- (ISO_B4: Base, 250.0, 353.0, "iso-b4")
- (ISO_B5: Book, 176.0, 250.0, "iso-b5")
- (ISO_B6: Book, 125.0, 176.0, "iso-b6")
- (ISO_B7: Base, 88.0, 125.0, "iso-b7")
- (ISO_B8: Base, 62.0, 88.0, "iso-b8")
+ (ISO_B1: 707.0, 1000.0, "iso-b1")
+ (ISO_B2: 500.0, 707.0, "iso-b2")
+ (ISO_B3: 353.0, 500.0, "iso-b3")
+ (ISO_B4: 250.0, 353.0, "iso-b4")
+ (ISO_B5: 176.0, 250.0, "iso-b5")
+ (ISO_B6: 125.0, 176.0, "iso-b6")
+ (ISO_B7: 88.0, 125.0, "iso-b7")
+ (ISO_B8: 62.0, 88.0, "iso-b8")
// ISO 216 C Series
- (ISO_C3: Base, 324.0, 458.0, "iso-c3")
- (ISO_C4: Base, 229.0, 324.0, "iso-c4")
- (ISO_C5: Base, 162.0, 229.0, "iso-c5")
- (ISO_C6: Base, 114.0, 162.0, "iso-c6")
- (ISO_C7: Base, 81.0, 114.0, "iso-c7")
- (ISO_C8: Base, 57.0, 81.0, "iso-c8")
+ (ISO_C3: 324.0, 458.0, "iso-c3")
+ (ISO_C4: 229.0, 324.0, "iso-c4")
+ (ISO_C5: 162.0, 229.0, "iso-c5")
+ (ISO_C6: 114.0, 162.0, "iso-c6")
+ (ISO_C7: 81.0, 114.0, "iso-c7")
+ (ISO_C8: 57.0, 81.0, "iso-c8")
// DIN D Series (extension to ISO)
- (DIN_D3: Base, 272.0, 385.0, "din-d3")
- (DIN_D4: Base, 192.0, 272.0, "din-d4")
- (DIN_D5: Base, 136.0, 192.0, "din-d5")
- (DIN_D6: Base, 96.0, 136.0, "din-d6")
- (DIN_D7: Base, 68.0, 96.0, "din-d7")
- (DIN_D8: Base, 48.0, 68.0, "din-d8")
+ (DIN_D3: 272.0, 385.0, "din-d3")
+ (DIN_D4: 192.0, 272.0, "din-d4")
+ (DIN_D5: 136.0, 192.0, "din-d5")
+ (DIN_D6: 96.0, 136.0, "din-d6")
+ (DIN_D7: 68.0, 96.0, "din-d7")
+ (DIN_D8: 48.0, 68.0, "din-d8")
// SIS (used in academia)
- (SIS_G5: Base, 169.0, 239.0, "sis-g5")
- (SIS_E5: Base, 115.0, 220.0, "sis-e5")
+ (SIS_G5: 169.0, 239.0, "sis-g5")
+ (SIS_E5: 115.0, 220.0, "sis-e5")
// ANSI Extensions
- (ANSI_A: Base, 216.0, 279.0, "ansi-a")
- (ANSI_B: Base, 279.0, 432.0, "ansi-b")
- (ANSI_C: Base, 432.0, 559.0, "ansi-c")
- (ANSI_D: Base, 559.0, 864.0, "ansi-d")
- (ANSI_E: Base, 864.0, 1118.0, "ansi-e")
+ (ANSI_A: 216.0, 279.0, "ansi-a")
+ (ANSI_B: 279.0, 432.0, "ansi-b")
+ (ANSI_C: 432.0, 559.0, "ansi-c")
+ (ANSI_D: 559.0, 864.0, "ansi-d")
+ (ANSI_E: 864.0, 1118.0, "ansi-e")
// ANSI Architectural Paper
- (ARCH_A: Base, 229.0, 305.0, "arch-a")
- (ARCH_B: Base, 305.0, 457.0, "arch-b")
- (ARCH_C: Base, 457.0, 610.0, "arch-c")
- (ARCH_D: Base, 610.0, 914.0, "arch-d")
- (ARCH_E1: Base, 762.0, 1067.0, "arch-e1")
- (ARCH_E: Base, 914.0, 1219.0, "arch-e")
+ (ARCH_A: 229.0, 305.0, "arch-a")
+ (ARCH_B: 305.0, 457.0, "arch-b")
+ (ARCH_C: 457.0, 610.0, "arch-c")
+ (ARCH_D: 610.0, 914.0, "arch-d")
+ (ARCH_E1: 762.0, 1067.0, "arch-e1")
+ (ARCH_E: 914.0, 1219.0, "arch-e")
// JIS B Series
- (JIS_B0: Base, 1030.0, 1456.0, "jis-b0")
- (JIS_B1: Base, 728.0, 1030.0, "jis-b1")
- (JIS_B2: Base, 515.0, 728.0, "jis-b2")
- (JIS_B3: Base, 364.0, 515.0, "jis-b3")
- (JIS_B4: Base, 257.0, 364.0, "jis-b4")
- (JIS_B5: Base, 182.0, 257.0, "jis-b5")
- (JIS_B6: Base, 128.0, 182.0, "jis-b6")
- (JIS_B7: Base, 91.0, 128.0, "jis-b7")
- (JIS_B8: Base, 64.0, 91.0, "jis-b8")
- (JIS_B9: Base, 45.0, 64.0, "jis-b9")
- (JIS_B10: Base, 32.0, 45.0, "jis-b10")
- (JIS_B11: Base, 22.0, 32.0, "jis-b11")
+ (JIS_B0: 1030.0, 1456.0, "jis-b0")
+ (JIS_B1: 728.0, 1030.0, "jis-b1")
+ (JIS_B2: 515.0, 728.0, "jis-b2")
+ (JIS_B3: 364.0, 515.0, "jis-b3")
+ (JIS_B4: 257.0, 364.0, "jis-b4")
+ (JIS_B5: 182.0, 257.0, "jis-b5")
+ (JIS_B6: 128.0, 182.0, "jis-b6")
+ (JIS_B7: 91.0, 128.0, "jis-b7")
+ (JIS_B8: 64.0, 91.0, "jis-b8")
+ (JIS_B9: 45.0, 64.0, "jis-b9")
+ (JIS_B10: 32.0, 45.0, "jis-b10")
+ (JIS_B11: 22.0, 32.0, "jis-b11")
// SAC D Series
- (SAC_D0: Base, 764.0, 1064.0, "sac-d0")
- (SAC_D1: Base, 532.0, 760.0, "sac-d1")
- (SAC_D2: Base, 380.0, 528.0, "sac-d2")
- (SAC_D3: Base, 264.0, 376.0, "sac-d3")
- (SAC_D4: Base, 188.0, 260.0, "sac-d4")
- (SAC_D5: Base, 130.0, 184.0, "sac-d5")
- (SAC_D6: Base, 92.0, 126.0, "sac-d6")
+ (SAC_D0: 764.0, 1064.0, "sac-d0")
+ (SAC_D1: 532.0, 760.0, "sac-d1")
+ (SAC_D2: 380.0, 528.0, "sac-d2")
+ (SAC_D3: 264.0, 376.0, "sac-d3")
+ (SAC_D4: 188.0, 260.0, "sac-d4")
+ (SAC_D5: 130.0, 184.0, "sac-d5")
+ (SAC_D6: 92.0, 126.0, "sac-d6")
// ISO 7810 ID
- (ISO_ID_1: Base, 85.6, 53.98, "iso-id-1")
- (ISO_ID_2: Base, 74.0, 105.0, "iso-id-2")
- (ISO_ID_3: Base, 88.0, 125.0, "iso-id-3")
+ (ISO_ID_1: 85.6, 53.98, "iso-id-1")
+ (ISO_ID_2: 74.0, 105.0, "iso-id-2")
+ (ISO_ID_3: 88.0, 125.0, "iso-id-3")
// ---------------------------------------------------------------------- //
// Asia
- (ASIA_F4: Base, 210.0, 330.0, "asia-f4")
+ (ASIA_F4: 210.0, 330.0, "asia-f4")
// Japan
- (JP_SHIROKU_BAN_4: Base, 264.0, 379.0, "jp-shiroku-ban-4")
- (JP_SHIROKU_BAN_5: Base, 189.0, 262.0, "jp-shiroku-ban-5")
- (JP_SHIROKU_BAN_6: Base, 127.0, 188.0, "jp-shiroku-ban-6")
- (JP_KIKU_4: Base, 227.0, 306.0, "jp-kiku-4")
- (JP_KIKU_5: Base, 151.0, 227.0, "jp-kiku-5")
- (JP_BUSINESS_CARD: Base, 91.0, 55.0, "jp-business-card")
+ (JP_SHIROKU_BAN_4: 264.0, 379.0, "jp-shiroku-ban-4")
+ (JP_SHIROKU_BAN_5: 189.0, 262.0, "jp-shiroku-ban-5")
+ (JP_SHIROKU_BAN_6: 127.0, 188.0, "jp-shiroku-ban-6")
+ (JP_KIKU_4: 227.0, 306.0, "jp-kiku-4")
+ (JP_KIKU_5: 151.0, 227.0, "jp-kiku-5")
+ (JP_BUSINESS_CARD: 91.0, 55.0, "jp-business-card")
// China
- (CN_BUSINESS_CARD: Base, 90.0, 54.0, "cn-business-card")
+ (CN_BUSINESS_CARD: 90.0, 54.0, "cn-business-card")
// Europe
- (EU_BUSINESS_CARD: Base, 85.0, 55.0, "eu-business-card")
+ (EU_BUSINESS_CARD: 85.0, 55.0, "eu-business-card")
// French Traditional (AFNOR)
- (FR_TELLIERE: Base, 340.0, 440.0, "fr-tellière")
- (FR_COURONNE_ECRITURE: Base, 360.0, 460.0, "fr-couronne-écriture")
- (FR_COURONNE_EDITION: Base, 370.0, 470.0, "fr-couronne-édition")
- (FR_RAISIN: Base, 500.0, 650.0, "fr-raisin")
- (FR_CARRE: Base, 450.0, 560.0, "fr-carré")
- (FR_JESUS: Base, 560.0, 760.0, "fr-jésus")
+ (FR_TELLIERE: 340.0, 440.0, "fr-tellière")
+ (FR_COURONNE_ECRITURE: 360.0, 460.0, "fr-couronne-écriture")
+ (FR_COURONNE_EDITION: 370.0, 470.0, "fr-couronne-édition")
+ (FR_RAISIN: 500.0, 650.0, "fr-raisin")
+ (FR_CARRE: 450.0, 560.0, "fr-carré")
+ (FR_JESUS: 560.0, 760.0, "fr-jésus")
// United Kingdom Imperial
- (UK_BRIEF: Base, 406.4, 342.9, "uk-brief")
- (UK_DRAFT: Base, 254.0, 406.4, "uk-draft")
- (UK_FOOLSCAP: Base, 203.2, 330.2, "uk-foolscap")
- (UK_QUARTO: Base, 203.2, 254.0, "uk-quarto")
- (UK_CROWN: Base, 508.0, 381.0, "uk-crown")
- (UK_BOOK_A: Book, 111.0, 178.0, "uk-book-a")
- (UK_BOOK_B: Book, 129.0, 198.0, "uk-book-b")
+ (UK_BRIEF: 406.4, 342.9, "uk-brief")
+ (UK_DRAFT: 254.0, 406.4, "uk-draft")
+ (UK_FOOLSCAP: 203.2, 330.2, "uk-foolscap")
+ (UK_QUARTO: 203.2, 254.0, "uk-quarto")
+ (UK_CROWN: 508.0, 381.0, "uk-crown")
+ (UK_BOOK_A: 111.0, 178.0, "uk-book-a")
+ (UK_BOOK_B: 129.0, 198.0, "uk-book-b")
// Unites States
- (US_LETTER: US, 215.9, 279.4, "us-letter")
- (US_LEGAL: US, 215.9, 355.6, "us-legal")
- (US_TABLOID: US, 279.4, 431.8, "us-tabloid")
- (US_EXECUTIVE: US, 184.15, 266.7, "us-executive")
- (US_FOOLSCAP_FOLIO: US, 215.9, 342.9, "us-foolscap-folio")
- (US_STATEMENT: US, 139.7, 215.9, "us-statement")
- (US_LEDGER: US, 431.8, 279.4, "us-ledger")
- (US_OFICIO: US, 215.9, 340.36, "us-oficio")
- (US_GOV_LETTER: US, 203.2, 266.7, "us-gov-letter")
- (US_GOV_LEGAL: US, 215.9, 330.2, "us-gov-legal")
- (US_BUSINESS_CARD: Base, 88.9, 50.8, "us-business-card")
- (US_DIGEST: Book, 139.7, 215.9, "us-digest")
- (US_TRADE: Book, 152.4, 228.6, "us-trade")
+ (US_LETTER: 215.9, 279.4, "us-letter")
+ (US_LEGAL: 215.9, 355.6, "us-legal")
+ (US_TABLOID: 279.4, 431.8, "us-tabloid")
+ (US_EXECUTIVE: 84.15, 266.7, "us-executive")
+ (US_FOOLSCAP_FOLIO: 215.9, 342.9, "us-foolscap-folio")
+ (US_STATEMENT: 139.7, 215.9, "us-statement")
+ (US_LEDGER: 431.8, 279.4, "us-ledger")
+ (US_OFICIO: 215.9, 340.36, "us-oficio")
+ (US_GOV_LETTER: 203.2, 266.7, "us-gov-letter")
+ (US_GOV_LEGAL: 215.9, 330.2, "us-gov-legal")
+ (US_BUSINESS_CARD: 88.9, 50.8, "us-business-card")
+ (US_DIGEST: 139.7, 215.9, "us-digest")
+ (US_TRADE: 152.4, 228.6, "us-trade")
// ---------------------------------------------------------------------- //
// Other
- (NEWSPAPER_COMPACT: Newspaper, 280.0, 430.0, "newspaper-compact")
- (NEWSPAPER_BERLINER: Newspaper, 315.0, 470.0, "newspaper-berliner")
- (NEWSPAPER_BROADSHEET: Newspaper, 381.0, 578.0, "newspaper-broadsheet")
- (PRESENTATION_16_9: Base, 297.0, 167.0625, "presentation-16-9")
- (PRESENTATION_4_3: Base, 280.0, 210.0, "presentation-4-3")
+ (NEWSPAPER_COMPACT: 280.0, 430.0, "newspaper-compact")
+ (NEWSPAPER_BERLINER: 315.0, 470.0, "newspaper-berliner")
+ (NEWSPAPER_BROADSHEET: 381.0, 578.0, "newspaper-broadsheet")
+ (PRESENTATION_16_9: 297.0, 167.0625, "presentation-16-9")
+ (PRESENTATION_4_3: 280.0, 210.0, "presentation-4-3")
}
castable! {
@@ -357,31 +334,6 @@ castable! {
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),
- }
- }
-}
-
/// The error when parsing a [`Paper`] from a string fails.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct PaperError;
diff --git a/src/library/par.rs b/src/library/par.rs
index 962834f2..2a4b4d08 100644
--- a/src/library/par.rs
+++ b/src/library/par.rs
@@ -22,7 +22,7 @@ pub enum ParChild {
/// Horizontal spacing between other children.
Spacing(SpacingKind),
/// An arbitrary inline-level node.
- Node(PackedNode),
+ Node(LayoutNode),
}
#[class]
diff --git a/src/library/place.rs b/src/library/place.rs
index d7b98c16..ee5ee2c9 100644
--- a/src/library/place.rs
+++ b/src/library/place.rs
@@ -5,7 +5,7 @@ use super::AlignNode;
/// Place a node at an absolute position.
#[derive(Debug, Hash)]
-pub struct PlaceNode(pub PackedNode);
+pub struct PlaceNode(pub LayoutNode);
#[class]
impl PlaceNode {
@@ -13,7 +13,7 @@ impl PlaceNode {
let aligns = args.find().unwrap_or(Spec::with_x(Some(Align::Left)));
let tx = args.named("dx")?.unwrap_or_default();
let ty = args.named("dy")?.unwrap_or_default();
- let body: PackedNode = args.expect("body")?;
+ let body: LayoutNode = args.expect("body")?;
Ok(Template::block(Self(
body.moved(Point::new(tx, ty)).aligned(aligns),
)))
diff --git a/src/library/raw.rs b/src/library/raw.rs
new file mode 100644
index 00000000..e2ae259b
--- /dev/null
+++ b/src/library/raw.rs
@@ -0,0 +1,117 @@
+//! Monospaced text and code.
+
+use once_cell::sync::Lazy;
+use syntect::easy::HighlightLines;
+use syntect::highlighting::{FontStyle, Highlighter, Style, Theme, ThemeSet};
+use syntect::parsing::SyntaxSet;
+
+use super::prelude::*;
+use crate::library::TextNode;
+use crate::source::SourceId;
+use crate::syntax::{self, RedNode};
+
+/// The lazily-loaded theme used for syntax highlighting.
+static THEME: Lazy<Theme> =
+ Lazy::new(|| ThemeSet::load_defaults().themes.remove("InspiredGitHub").unwrap());
+
+/// The lazily-loaded syntect syntax definitions.
+static SYNTAXES: Lazy<SyntaxSet> = Lazy::new(|| SyntaxSet::load_defaults_newlines());
+
+/// Monospaced text with optional syntax highlighting.
+#[derive(Debug, Hash)]
+pub struct RawNode {
+ /// The raw text.
+ pub text: EcoString,
+ /// Whether the node is block-level.
+ pub block: bool,
+}
+
+#[class]
+impl RawNode {
+ /// The language to syntax-highlight in.
+ pub const LANG: Option<EcoString> = None;
+
+ fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
+ Ok(Template::show(Self {
+ text: args.expect("text")?,
+ block: args.named("block")?.unwrap_or(false),
+ }))
+ }
+}
+
+impl Show for RawNode {
+ fn show(&self, styles: StyleChain) -> Template {
+ let lang = styles.get_ref(Self::LANG).as_ref();
+ let foreground = THEME
+ .settings
+ .foreground
+ .map(Color::from)
+ .unwrap_or(Color::BLACK)
+ .into();
+
+ let mut template = if matches!(
+ lang.map(|s| s.to_lowercase()).as_deref(),
+ Some("typ" | "typst")
+ ) {
+ let mut seq = vec![];
+ let green = crate::parse::parse(&self.text);
+ let red = RedNode::from_root(green, SourceId::from_raw(0));
+ let highlighter = Highlighter::new(&THEME);
+
+ syntax::highlight_syntect(red.as_ref(), &highlighter, &mut |range, style| {
+ seq.push(styled(&self.text[range], foreground, style));
+ });
+
+ Template::sequence(seq)
+ } else if let Some(syntax) =
+ lang.and_then(|token| SYNTAXES.find_syntax_by_token(&token))
+ {
+ let mut seq = vec![];
+ let mut highlighter = HighlightLines::new(syntax, &THEME);
+ for (i, line) in self.text.lines().enumerate() {
+ if i != 0 {
+ seq.push(Template::Linebreak);
+ }
+
+ for (style, piece) in highlighter.highlight(line, &SYNTAXES) {
+ seq.push(styled(piece, foreground, style));
+ }
+ }
+
+ Template::sequence(seq)
+ } else {
+ Template::Text(self.text.clone())
+ };
+
+ if self.block {
+ template = Template::Block(template.pack());
+ }
+
+ template.monospaced()
+ }
+}
+
+/// Style a piece of text with a syntect style.
+fn styled(piece: &str, foreground: Paint, style: Style) -> Template {
+ let mut styles = StyleMap::new();
+ let mut body = Template::Text(piece.into());
+
+ let paint = style.foreground.into();
+ if paint != foreground {
+ styles.set(TextNode::FILL, paint);
+ }
+
+ if style.font_style.contains(FontStyle::BOLD) {
+ styles.set(TextNode::STRONG, true);
+ }
+
+ if style.font_style.contains(FontStyle::ITALIC) {
+ styles.set(TextNode::EMPH, true);
+ }
+
+ if style.font_style.contains(FontStyle::UNDERLINE) {
+ body = body.underlined();
+ }
+
+ body.styled_with_map(styles)
+}
diff --git a/src/library/shape.rs b/src/library/shape.rs
index 8ac9956f..95e3e288 100644
--- a/src/library/shape.rs
+++ b/src/library/shape.rs
@@ -11,7 +11,7 @@ pub struct ShapeNode<S: ShapeKind> {
/// Which shape to place the child into.
pub kind: S,
/// The child node to place into the shape, if any.
- pub child: Option<PackedNode>,
+ pub child: Option<LayoutNode>,
}
#[class]
@@ -50,14 +50,6 @@ impl<S: ShapeKind> ShapeNode<S> {
.sized(Spec::new(width, height)),
))
}
-
- fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
- styles.set_opt(Self::FILL, args.named("fill")?);
- styles.set_opt(Self::STROKE, args.named("stroke")?);
- styles.set_opt(Self::THICKNESS, args.named("thickness")?);
- styles.set_opt(Self::PADDING, args.named("padding")?);
- Ok(())
- }
}
impl<S: ShapeKind> Layout for ShapeNode<S> {
diff --git a/src/library/spacing.rs b/src/library/spacing.rs
index feadf7f3..3e26cb3c 100644
--- a/src/library/spacing.rs
+++ b/src/library/spacing.rs
@@ -38,6 +38,12 @@ impl SpacingKind {
}
}
+impl From<Length> for SpacingKind {
+ fn from(length: Length) -> Self {
+ Self::Linear(length.into())
+ }
+}
+
castable! {
SpacingKind,
Expected: "linear or fractional",
diff --git a/src/library/stack.rs b/src/library/stack.rs
index 0ff8e973..bccd552f 100644
--- a/src/library/stack.rs
+++ b/src/library/stack.rs
@@ -64,7 +64,7 @@ pub enum StackChild {
/// Spacing between other nodes.
Spacing(SpacingKind),
/// An arbitrary node.
- Node(PackedNode),
+ Node(LayoutNode),
}
impl Debug for StackChild {
@@ -166,7 +166,7 @@ impl StackLayouter {
pub fn layout_node(
&mut self,
ctx: &mut LayoutContext,
- node: &PackedNode,
+ node: &LayoutNode,
styles: StyleChain,
) {
if self.regions.is_full() {
diff --git a/src/library/table.rs b/src/library/table.rs
index 33fa68cd..c40a117b 100644
--- a/src/library/table.rs
+++ b/src/library/table.rs
@@ -11,7 +11,7 @@ pub struct TableNode {
/// Defines sizing of gutter rows and columns between content.
pub gutter: Spec<Vec<TrackSizing>>,
/// The nodes to be arranged in the table.
- pub children: Vec<PackedNode>,
+ pub children: Vec<LayoutNode>,
}
#[class]
@@ -33,7 +33,7 @@ impl TableNode {
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")?;
- Ok(Template::block(Self {
+ Ok(Template::show(Self {
tracks: Spec::new(columns, rows),
gutter: Spec::new(
column_gutter.unwrap_or_else(|| base_gutter.clone()),
@@ -54,13 +54,8 @@ impl TableNode {
}
}
-impl Layout for TableNode {
- fn layout(
- &self,
- ctx: &mut LayoutContext,
- regions: &Regions,
- styles: StyleChain,
- ) -> Vec<Constrained<Arc<Frame>>> {
+impl Show for TableNode {
+ fn show(&self, styles: StyleChain) -> Template {
let primary = styles.get(Self::PRIMARY);
let secondary = styles.get(Self::SECONDARY);
let thickness = styles.get(Self::THICKNESS);
@@ -90,12 +85,10 @@ impl Layout for TableNode {
})
.collect();
- let grid = GridNode {
+ Template::block(GridNode {
tracks: self.tracks.clone(),
gutter: self.gutter.clone(),
children,
- };
-
- grid.layout(ctx, regions, styles)
+ })
}
}
diff --git a/src/library/text.rs b/src/library/text.rs
index fb0f7e08..1ccbb1c6 100644
--- a/src/library/text.rs
+++ b/src/library/text.rs
@@ -24,13 +24,14 @@ pub struct TextNode;
#[class]
impl TextNode {
/// A prioritized sequence of font families.
- pub const FAMILY_LIST: Vec<FontFamily> = vec![FontFamily::SansSerif];
+ #[variadic]
+ pub const FAMILY: Vec<FontFamily> = vec![FontFamily::SansSerif];
/// The serif font family/families.
- pub const SERIF_LIST: Vec<NamedFamily> = vec![NamedFamily::new("IBM Plex Serif")];
+ pub const SERIF: Vec<NamedFamily> = vec![NamedFamily::new("IBM Plex Serif")];
/// The sans-serif font family/families.
- pub const SANS_SERIF_LIST: Vec<NamedFamily> = vec![NamedFamily::new("IBM Plex Sans")];
+ pub const SANS_SERIF: Vec<NamedFamily> = vec![NamedFamily::new("IBM Plex Sans")];
/// The monospace font family/families.
- pub const MONOSPACE_LIST: Vec<NamedFamily> = vec![NamedFamily::new("IBM Plex Mono")];
+ pub const MONOSPACE: Vec<NamedFamily> = vec![NamedFamily::new("IBM Plex Mono")];
/// Whether to allow font fallback when the primary font list contains no
/// match.
pub const FALLBACK: bool = true;
@@ -41,23 +42,12 @@ impl TextNode {
pub const WEIGHT: FontWeight = FontWeight::REGULAR;
/// The width of the glyphs.
pub const STRETCH: FontStretch = FontStretch::NORMAL;
- /// Whether the font weight should be increased by 300.
- #[fold(bool::bitxor)]
- pub const STRONG: bool = false;
- /// Whether the the font style should be inverted.
- #[fold(bool::bitxor)]
- pub const EMPH: bool = false;
- /// Whether a monospace font should be preferred.
- pub const MONOSPACE: bool = false;
/// The glyph fill color.
+ #[shorthand]
pub const FILL: Paint = Color::BLACK.into();
- /// Decorative lines.
- #[fold(|a, b| a.into_iter().chain(b).collect())]
- pub const LINES: Vec<Decoration> = vec![];
- /// An URL the text should link to.
- pub const LINK: Option<EcoString> = None;
/// The size of the glyphs.
+ #[shorthand]
#[fold(Linear::compose)]
pub const SIZE: Linear = Length::pt(11.0).into();
/// The amount of space that should be added between characters.
@@ -94,73 +84,64 @@ impl TextNode {
/// Raw OpenType features to apply.
pub const FEATURES: Vec<(Tag, u32)> = vec![];
+ /// Whether the font weight should be increased by 300.
+ #[skip]
+ #[fold(bool::bitxor)]
+ pub const STRONG: bool = false;
+ /// Whether the the font style should be inverted.
+ #[skip]
+ #[fold(bool::bitxor)]
+ pub const EMPH: bool = false;
+ /// Whether a monospace font should be preferred.
+ #[skip]
+ pub const MONOSPACED: bool = false;
+ /// Decorative lines.
+ #[skip]
+ #[fold(|a, b| a.into_iter().chain(b).collect())]
+ pub const LINES: Vec<Decoration> = vec![];
+ /// An URL the text should link to.
+ #[skip]
+ pub const LINK: Option<EcoString> = None;
+
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
// The text constructor is special: It doesn't create a text node.
// Instead, it leaves the passed argument structurally unchanged, but
// styles all text in it.
args.expect("body")
}
-
- fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
- let list = args.named("family")?.or_else(|| {
- let families: Vec<_> = args.all().collect();
- (!families.is_empty()).then(|| families)
- });
-
- styles.set_opt(Self::FAMILY_LIST, list);
- styles.set_opt(Self::SERIF_LIST, args.named("serif")?);
- styles.set_opt(Self::SANS_SERIF_LIST, args.named("sans-serif")?);
- styles.set_opt(Self::MONOSPACE_LIST, args.named("monospace")?);
- styles.set_opt(Self::FALLBACK, args.named("fallback")?);
- styles.set_opt(Self::STYLE, args.named("style")?);
- styles.set_opt(Self::WEIGHT, args.named("weight")?);
- styles.set_opt(Self::STRETCH, args.named("stretch")?);
- styles.set_opt(Self::FILL, args.named("fill")?.or_else(|| args.find()));
- styles.set_opt(Self::SIZE, args.named("size")?.or_else(|| args.find()));
- styles.set_opt(Self::TRACKING, args.named("tracking")?.map(Em::new));
- styles.set_opt(Self::TOP_EDGE, args.named("top-edge")?);
- styles.set_opt(Self::BOTTOM_EDGE, args.named("bottom-edge")?);
- styles.set_opt(Self::KERNING, args.named("kerning")?);
- styles.set_opt(Self::SMALLCAPS, args.named("smallcaps")?);
- styles.set_opt(Self::ALTERNATES, args.named("alternates")?);
- styles.set_opt(Self::STYLISTIC_SET, args.named("stylistic-set")?);
- styles.set_opt(Self::LIGATURES, args.named("ligatures")?);
- styles.set_opt(
- Self::DISCRETIONARY_LIGATURES,
- args.named("discretionary-ligatures")?,
- );
- styles.set_opt(
- Self::HISTORICAL_LIGATURES,
- args.named("historical-ligatures")?,
- );
- styles.set_opt(Self::NUMBER_TYPE, args.named("number-type")?);
- styles.set_opt(Self::NUMBER_WIDTH, args.named("number-width")?);
- styles.set_opt(Self::NUMBER_POSITION, args.named("number-position")?);
- styles.set_opt(Self::SLASHED_ZERO, args.named("slashed-zero")?);
- styles.set_opt(Self::FRACTIONS, args.named("fractions")?);
- styles.set_opt(Self::FEATURES, args.named("features")?);
-
- Ok(())
- }
}
/// Strong text, rendered in boldface.
-pub struct StrongNode;
+#[derive(Debug, Hash)]
+pub struct StrongNode(pub Template);
#[class]
impl StrongNode {
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
- Ok(args.expect::<Template>("body")?.styled(TextNode::STRONG, true))
+ Ok(Template::show(Self(args.expect("body")?)))
+ }
+}
+
+impl Show for StrongNode {
+ fn show(&self, _: StyleChain) -> Template {
+ self.0.clone().styled(TextNode::STRONG, true)
}
}
/// Emphasized text, rendered with an italic face.
-pub struct EmphNode;
+#[derive(Debug, Hash)]
+pub struct EmphNode(pub Template);
#[class]
impl EmphNode {
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
- Ok(args.expect::<Template>("body")?.styled(TextNode::EMPH, true))
+ Ok(Template::show(Self(args.expect("body")?)))
+ }
+}
+
+impl Show for EmphNode {
+ fn show(&self, _: StyleChain) -> Template {
+ self.0.clone().styled(TextNode::EMPH, true)
}
}
@@ -275,6 +256,12 @@ castable! {
}
castable! {
+ Em,
+ Expected: "float",
+ Value::Float(v) => Self::new(v),
+}
+
+castable! {
VerticalFontMetric,
Expected: "linear or string",
Value::Length(v) => Self::Linear(v.into()),
@@ -644,18 +631,18 @@ fn variant(styles: StyleChain) -> FontVariant {
/// Resolve a prioritized iterator over the font families.
fn families(styles: StyleChain) -> impl Iterator<Item = &str> + Clone {
- let head = if styles.get(TextNode::MONOSPACE) {
- styles.get_ref(TextNode::MONOSPACE_LIST).as_slice()
+ let head = if styles.get(TextNode::MONOSPACED) {
+ styles.get_ref(TextNode::MONOSPACE).as_slice()
} else {
&[]
};
- let core = styles.get_ref(TextNode::FAMILY_LIST).iter().flat_map(move |family| {
+ let core = styles.get_ref(TextNode::FAMILY).iter().flat_map(move |family| {
match family {
FontFamily::Named(name) => std::slice::from_ref(name),
- FontFamily::Serif => styles.get_ref(TextNode::SERIF_LIST),
- FontFamily::SansSerif => styles.get_ref(TextNode::SANS_SERIF_LIST),
- FontFamily::Monospace => styles.get_ref(TextNode::MONOSPACE_LIST),
+ FontFamily::Serif => styles.get_ref(TextNode::SERIF),
+ FontFamily::SansSerif => styles.get_ref(TextNode::SANS_SERIF),
+ FontFamily::Monospace => styles.get_ref(TextNode::MONOSPACE),
}
});
diff --git a/src/library/transform.rs b/src/library/transform.rs
index 9d04071d..de053a9d 100644
--- a/src/library/transform.rs
+++ b/src/library/transform.rs
@@ -9,7 +9,7 @@ pub struct TransformNode<T: TransformKind> {
/// Transformation to apply to the contents.
pub kind: T,
/// The node whose contents should be transformed.
- pub child: PackedNode,
+ pub child: LayoutNode,
}
#[class]
@@ -23,11 +23,6 @@ impl<T: TransformKind> TransformNode<T> {
child: args.expect("body")?,
}))
}
-
- fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
- styles.set_opt(Self::ORIGIN, args.named("origin")?);
- Ok(())
- }
}
impl<T: TransformKind> Layout for TransformNode<T> {