From c0e972b91a7bf8d22cd24a38fc92a9c6214c8a0c Mon Sep 17 00:00:00 2001 From: Laurenz Date: Tue, 18 Oct 2022 00:02:38 +0200 Subject: Reduce dependencies from compiler on library --- src/library/graphics/mod.rs | 2 - src/library/graphics/transform.rs | 118 ------------------------------------ src/library/layout/mod.rs | 2 + src/library/layout/page.rs | 2 +- src/library/layout/transform.rs | 118 ++++++++++++++++++++++++++++++++++++ src/library/math/mod.rs | 2 +- src/library/mod.rs | 122 ++++++++++++++++++++++++++++++++++++-- src/library/prelude.rs | 3 +- src/library/structure/list.rs | 2 +- src/library/text/lang.rs | 74 ----------------------- src/library/text/mod.rs | 16 ++++- 11 files changed, 257 insertions(+), 204 deletions(-) delete mode 100644 src/library/graphics/transform.rs create mode 100644 src/library/layout/transform.rs delete mode 100644 src/library/text/lang.rs (limited to 'src/library') diff --git a/src/library/graphics/mod.rs b/src/library/graphics/mod.rs index e9a6188f..34182121 100644 --- a/src/library/graphics/mod.rs +++ b/src/library/graphics/mod.rs @@ -4,10 +4,8 @@ mod hide; mod image; mod line; mod shape; -mod transform; pub use self::image::*; pub use hide::*; pub use line::*; pub use shape::*; -pub use transform::*; diff --git a/src/library/graphics/transform.rs b/src/library/graphics/transform.rs deleted file mode 100644 index b110f343..00000000 --- a/src/library/graphics/transform.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::geom::Transform; -use crate::library::prelude::*; - -/// Move a node without affecting layout. -#[derive(Debug, Hash)] -pub struct MoveNode { - /// The offset by which to move the node. - pub delta: Spec>, - /// The node whose contents should be moved. - pub child: LayoutNode, -} - -#[node] -impl MoveNode { - fn construct(_: &mut Vm, args: &mut Args) -> SourceResult { - let dx = args.named("dx")?.unwrap_or_default(); - let dy = args.named("dy")?.unwrap_or_default(); - Ok(Content::inline(Self { - delta: Spec::new(dx, dy), - child: args.expect("body")?, - })) - } -} - -impl Layout for MoveNode { - fn layout( - &self, - world: Tracked, - regions: &Regions, - styles: StyleChain, - ) -> SourceResult> { - let mut frames = self.child.layout(world, regions, styles)?; - - let delta = self.delta.resolve(styles); - for frame in &mut frames { - let delta = delta.zip(frame.size()).map(|(d, s)| d.relative_to(s)); - frame.translate(delta.to_point()); - } - - Ok(frames) - } -} - -/// Transform a node without affecting layout. -#[derive(Debug, Hash)] -pub struct TransformNode { - /// Transformation to apply to the contents. - pub transform: Transform, - /// The node whose contents should be transformed. - pub child: LayoutNode, -} - -/// Rotate a node without affecting layout. -pub type RotateNode = TransformNode; - -/// Scale a node without affecting layout. -pub type ScaleNode = TransformNode; - -#[node] -impl TransformNode { - /// The origin of the transformation. - #[property(resolve)] - pub const ORIGIN: Spec> = Spec::default(); - - fn construct(_: &mut Vm, args: &mut Args) -> SourceResult { - let transform = match T { - ROTATE => { - let angle = args.named_or_find("angle")?.unwrap_or_default(); - Transform::rotate(angle) - } - SCALE | _ => { - let all = args.find()?; - let sx = args.named("x")?.or(all).unwrap_or(Ratio::one()); - let sy = args.named("y")?.or(all).unwrap_or(Ratio::one()); - Transform::scale(sx, sy) - } - }; - - Ok(Content::inline(Self { - transform, - child: args.expect("body")?, - })) - } -} - -impl Layout for TransformNode { - fn layout( - &self, - world: Tracked, - regions: &Regions, - styles: StyleChain, - ) -> SourceResult> { - let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON); - let mut frames = self.child.layout(world, regions, styles)?; - - for frame in &mut frames { - let Spec { x, y } = origin.zip(frame.size()).map(|(o, s)| o.position(s)); - let transform = Transform::translate(x, y) - .pre_concat(self.transform) - .pre_concat(Transform::translate(-x, -y)); - - frame.transform(transform); - } - - Ok(frames) - } -} - -/// Kinds of transformations. -/// -/// The move transformation is handled separately. -pub type TransformKind = usize; - -/// A rotational transformation. -const ROTATE: TransformKind = 1; - -/// A scale transformation. -const SCALE: TransformKind = 2; diff --git a/src/library/layout/mod.rs b/src/library/layout/mod.rs index 588b15aa..02276f22 100644 --- a/src/library/layout/mod.rs +++ b/src/library/layout/mod.rs @@ -10,6 +10,7 @@ mod page; mod place; mod spacing; mod stack; +mod transform; pub use align::*; pub use columns::*; @@ -21,3 +22,4 @@ pub use page::*; pub use place::*; pub use spacing::*; pub use stack::*; +pub use transform::*; diff --git a/src/library/layout/page.rs b/src/library/layout/page.rs index 9cbbcca5..7d13163d 100644 --- a/src/library/layout/page.rs +++ b/src/library/layout/page.rs @@ -188,7 +188,7 @@ impl Marginal { Self::Content(content) => Some(content.clone()), Self::Func(func, span) => { let args = Args::new(*span, [Value::Int(page as i64)]); - Some(func.call_detached(world, args)?.display()) + Some(func.call_detached(world, args)?.display(world)) } }) } diff --git a/src/library/layout/transform.rs b/src/library/layout/transform.rs new file mode 100644 index 00000000..b110f343 --- /dev/null +++ b/src/library/layout/transform.rs @@ -0,0 +1,118 @@ +use crate::geom::Transform; +use crate::library::prelude::*; + +/// Move a node without affecting layout. +#[derive(Debug, Hash)] +pub struct MoveNode { + /// The offset by which to move the node. + pub delta: Spec>, + /// The node whose contents should be moved. + pub child: LayoutNode, +} + +#[node] +impl MoveNode { + fn construct(_: &mut Vm, args: &mut Args) -> SourceResult { + let dx = args.named("dx")?.unwrap_or_default(); + let dy = args.named("dy")?.unwrap_or_default(); + Ok(Content::inline(Self { + delta: Spec::new(dx, dy), + child: args.expect("body")?, + })) + } +} + +impl Layout for MoveNode { + fn layout( + &self, + world: Tracked, + regions: &Regions, + styles: StyleChain, + ) -> SourceResult> { + let mut frames = self.child.layout(world, regions, styles)?; + + let delta = self.delta.resolve(styles); + for frame in &mut frames { + let delta = delta.zip(frame.size()).map(|(d, s)| d.relative_to(s)); + frame.translate(delta.to_point()); + } + + Ok(frames) + } +} + +/// Transform a node without affecting layout. +#[derive(Debug, Hash)] +pub struct TransformNode { + /// Transformation to apply to the contents. + pub transform: Transform, + /// The node whose contents should be transformed. + pub child: LayoutNode, +} + +/// Rotate a node without affecting layout. +pub type RotateNode = TransformNode; + +/// Scale a node without affecting layout. +pub type ScaleNode = TransformNode; + +#[node] +impl TransformNode { + /// The origin of the transformation. + #[property(resolve)] + pub const ORIGIN: Spec> = Spec::default(); + + fn construct(_: &mut Vm, args: &mut Args) -> SourceResult { + let transform = match T { + ROTATE => { + let angle = args.named_or_find("angle")?.unwrap_or_default(); + Transform::rotate(angle) + } + SCALE | _ => { + let all = args.find()?; + let sx = args.named("x")?.or(all).unwrap_or(Ratio::one()); + let sy = args.named("y")?.or(all).unwrap_or(Ratio::one()); + Transform::scale(sx, sy) + } + }; + + Ok(Content::inline(Self { + transform, + child: args.expect("body")?, + })) + } +} + +impl Layout for TransformNode { + fn layout( + &self, + world: Tracked, + regions: &Regions, + styles: StyleChain, + ) -> SourceResult> { + let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON); + let mut frames = self.child.layout(world, regions, styles)?; + + for frame in &mut frames { + let Spec { x, y } = origin.zip(frame.size()).map(|(o, s)| o.position(s)); + let transform = Transform::translate(x, y) + .pre_concat(self.transform) + .pre_concat(Transform::translate(-x, -y)); + + frame.transform(transform); + } + + Ok(frames) + } +} + +/// Kinds of transformations. +/// +/// The move transformation is handled separately. +pub type TransformKind = usize; + +/// A rotational transformation. +const ROTATE: TransformKind = 1; + +/// A scale transformation. +const SCALE: TransformKind = 2; diff --git a/src/library/math/mod.rs b/src/library/math/mod.rs index 7b5fdf52..7d0fecb4 100644 --- a/src/library/math/mod.rs +++ b/src/library/math/mod.rs @@ -15,7 +15,7 @@ use rex::render::{Backend, Cursor, Renderer}; use crate::font::Font; use crate::library::layout::BlockSpacing; use crate::library::prelude::*; -use crate::library::text::{variant, FontFamily, Lang, TextNode}; +use crate::library::text::{variant, FontFamily, TextNode}; /// A piece of a mathematical formula. #[derive(Debug, Clone, Hash)] diff --git a/src/library/mod.rs b/src/library/mod.rs index b42ec071..cf9acdac 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -14,7 +14,7 @@ pub mod utility; use prelude::*; /// Construct a scope containing all standard library definitions. -pub fn new() -> Scope { +pub fn scope() -> Scope { let mut std = Scope::new(); // Text. @@ -58,6 +58,9 @@ pub fn new() -> Scope { std.def_node::("columns"); std.def_node::("colbreak"); std.def_node::("place"); + std.def_node::("move"); + std.def_node::("scale"); + std.def_node::("rotate"); // Graphics. std.def_node::("image"); @@ -66,9 +69,6 @@ pub fn new() -> Scope { std.def_node::("square"); std.def_node::("ellipse"); std.def_node::("circle"); - std.def_node::("move"); - std.def_node::("scale"); - std.def_node::("rotate"); std.def_node::("hide"); // Math. @@ -142,3 +142,117 @@ pub fn new() -> Scope { std } + +/// Construct the standard role map. +pub fn roles() -> RoleMap { + RoleMap { + strong: |body| Content::show(text::StrongNode(body)), + emph: |body| Content::show(text::EmphNode(body)), + raw: |text, lang, block| { + let node = Content::show(text::RawNode { text, block }); + match lang { + Some(_) => node.styled(text::RawNode::LANG, lang), + None => node, + } + }, + link: |url| Content::show(text::LinkNode::from_url(url)), + ref_: |target| Content::show(structure::RefNode(target)), + heading: |level, body| Content::show(structure::HeadingNode { level, body }), + list_item: |body| Content::Item(structure::ListItem::List(Box::new(body))), + enum_item: |number, body| { + Content::Item(structure::ListItem::Enum(number, Box::new(body))) + }, + desc_item: |term, body| { + Content::Item(structure::ListItem::Desc(Box::new(structure::DescItem { + term, + body, + }))) + }, + } +} + +/// Additional methods on content. +pub trait ContentExt { + /// Make this content strong. + fn strong(self) -> Self; + + /// Make this content emphasized. + fn emph(self) -> Self; + + /// Underline this content. + fn underlined(self) -> Self; +} + +impl ContentExt for Content { + fn strong(self) -> Self { + Self::show(text::StrongNode(self)) + } + + fn emph(self) -> Self { + Self::show(text::EmphNode(self)) + } + + fn underlined(self) -> Self { + Self::show(text::DecoNode::<{ text::UNDERLINE }>(self)) + } +} + +/// Additional methods for the style chain. +pub trait StyleMapExt { + /// Set a font family composed of a preferred family and existing families + /// from a style chain. + fn set_family(&mut self, preferred: text::FontFamily, existing: StyleChain); +} + +impl StyleMapExt for StyleMap { + fn set_family(&mut self, preferred: text::FontFamily, existing: StyleChain) { + self.set( + text::TextNode::FAMILY, + std::iter::once(preferred) + .chain(existing.get(text::TextNode::FAMILY).iter().cloned()) + .collect(), + ); + } +} + +/// Additional methods for layout nodes. +pub trait LayoutNodeExt { + /// Set alignments for this node. + fn aligned(self, aligns: Spec>) -> Self; + + /// Pad this node at the sides. + fn padded(self, padding: Sides>) -> Self; + + /// Transform this node's contents without affecting layout. + fn moved(self, delta: Spec>) -> Self; +} + +impl LayoutNodeExt for LayoutNode { + fn aligned(self, aligns: Spec>) -> Self { + if aligns.any(Option::is_some) { + layout::AlignNode { aligns, child: self }.pack() + } else { + self + } + } + + fn padded(self, padding: Sides>) -> Self { + if !padding.left.is_zero() + || !padding.top.is_zero() + || !padding.right.is_zero() + || !padding.bottom.is_zero() + { + layout::PadNode { padding, child: self }.pack() + } else { + self + } + } + + fn moved(self, delta: Spec>) -> Self { + if delta.any(|r| !r.is_zero()) { + layout::MoveNode { delta, child: self }.pack() + } else { + self + } + } +} diff --git a/src/library/prelude.rs b/src/library/prelude.rs index 7a4284f1..1d98e2a9 100644 --- a/src/library/prelude.rs +++ b/src/library/prelude.rs @@ -9,6 +9,7 @@ pub use std::sync::Arc; pub use comemo::Tracked; pub use typst_macros::node; +pub use super::{ContentExt, LayoutNodeExt, StyleMapExt}; pub use crate::diag::{ with_alternative, At, FileError, FileResult, SourceError, SourceResult, StrResult, }; @@ -24,4 +25,4 @@ pub use crate::model::{ }; pub use crate::syntax::{Span, Spanned}; pub use crate::util::EcoString; -pub use crate::World; +pub use crate::{RoleMap, World}; diff --git a/src/library/structure/list.rs b/src/library/structure/list.rs index 7a43e5db..9d78238d 100644 --- a/src/library/structure/list.rs +++ b/src/library/structure/list.rs @@ -328,7 +328,7 @@ impl Label { Self::Content(content) => content.clone(), Self::Func(func, span) => { let args = Args::new(*span, [Value::Int(number as i64)]); - func.call_detached(world, args)?.display() + func.call_detached(world, args)?.display(world) } }) } diff --git a/src/library/text/lang.rs b/src/library/text/lang.rs deleted file mode 100644 index f2193f05..00000000 --- a/src/library/text/lang.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::geom::Dir; -use crate::model::Value; - -/// A code for a natural language. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct Lang([u8; 3], u8); - -impl Lang { - /// The code for the english language. - pub const ENGLISH: Self = Self(*b"en ", 2); - - /// Construct a language from a two- or three-byte ISO 639-1/2/3 code. - pub fn from_str(iso: &str) -> Option { - let len = iso.len(); - if matches!(len, 2 ..= 3) && iso.is_ascii() { - let mut bytes = [b' '; 3]; - bytes[.. len].copy_from_slice(iso.as_bytes()); - bytes.make_ascii_lowercase(); - Some(Self(bytes, len as u8)) - } else { - None - } - } - - /// Return the language code as an all lowercase string slice. - pub fn as_str(&self) -> &str { - std::str::from_utf8(&self.0[.. usize::from(self.1)]).unwrap_or_default() - } - - /// The default direction for the language. - pub fn dir(self) -> Dir { - match self.as_str() { - "ar" | "dv" | "fa" | "he" | "ks" | "pa" | "ps" | "sd" | "ug" | "ur" - | "yi" => Dir::RTL, - _ => Dir::LTR, - } - } -} - -castable! { - Lang, - Expected: "string", - Value::Str(string) => Self::from_str(&string) - .ok_or("expected two or three letter language code (ISO 639-1/2/3)")?, -} - -/// A code for a region somewhere in the world. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct Region([u8; 2]); - -impl Region { - /// Construct a region from its two-byte ISO 3166-1 alpha-2 code. - pub fn from_str(iso: &str) -> Option { - if iso.is_ascii() { - let mut bytes: [u8; 2] = iso.as_bytes().try_into().ok()?; - bytes.make_ascii_uppercase(); - Some(Self(bytes)) - } else { - None - } - } - - /// Return the region code as an all uppercase string slice. - pub fn as_str(&self) -> &str { - std::str::from_utf8(&self.0).unwrap_or_default() - } -} - -castable! { - Region, - Expected: "string", - Value::Str(string) => Self::from_str(&string) - .ok_or("expected two letter region code (ISO 3166-1 alpha-2)")?, -} diff --git a/src/library/text/mod.rs b/src/library/text/mod.rs index c9bf2e57..299357de 100644 --- a/src/library/text/mod.rs +++ b/src/library/text/mod.rs @@ -1,7 +1,6 @@ //! Text handling and paragraph layout. mod deco; -mod lang; mod link; mod par; mod quotes; @@ -11,7 +10,6 @@ mod shaping; mod shift; pub use deco::*; -pub use lang::*; pub use link::*; pub use par::*; pub use quotes::*; @@ -290,6 +288,20 @@ castable! { }), } +castable! { + Lang, + Expected: "string", + Value::Str(string) => Self::from_str(&string) + .ok_or("expected two or three letter language code (ISO 639-1/2/3)")?, +} + +castable! { + Region, + Expected: "string", + Value::Str(string) => Self::from_str(&string) + .ok_or("expected two letter region code (ISO 3166-1 alpha-2)")?, +} + /// The direction of text and inline objects in their line. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub struct HorizontalDir(pub Dir); -- cgit v1.2.3