diff options
| author | Martin Haug <mhaug@live.de> | 2022-05-27 16:39:06 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-05-27 16:39:06 +0200 |
| commit | 73086b5a7c1b0f9f638165803c237901499adb64 (patch) | |
| tree | c120a2449aaf325cb675ea3363ee69758a734d86 /src/library | |
| parent | 99cb655832161d4ebec73273a15453a8f6acc1b7 (diff) | |
| parent | 8ba11b0722599892499337b3272cec38945d11de (diff) | |
Merge pull request #71 from typst/pins
Diffstat (limited to 'src/library')
| -rw-r--r-- | src/library/layout/grid.rs | 4 | ||||
| -rw-r--r-- | src/library/layout/locate.rs | 14 | ||||
| -rw-r--r-- | src/library/layout/mod.rs | 2 | ||||
| -rw-r--r-- | src/library/mod.rs | 62 | ||||
| -rw-r--r-- | src/library/prelude.rs | 4 | ||||
| -rw-r--r-- | src/library/text/link.rs | 21 | ||||
| -rw-r--r-- | src/library/text/par.rs | 47 |
7 files changed, 96 insertions, 58 deletions
diff --git a/src/library/layout/grid.rs b/src/library/layout/grid.rs index 5b621732..4cad9de6 100644 --- a/src/library/layout/grid.rs +++ b/src/library/layout/grid.rs @@ -204,7 +204,9 @@ impl<'a> GridLayouter<'a> { /// Determines the columns sizes and then layouts the grid row-by-row. pub fn layout(mut self) -> TypResult<Vec<Arc<Frame>>> { + self.ctx.pins.freeze(); self.measure_columns()?; + self.ctx.pins.unfreeze(); for y in 0 .. self.rows.len() { // Skip to next region if current one is full, but only for content @@ -370,10 +372,12 @@ impl<'a> GridLayouter<'a> { pod.base.x = self.regions.base.x; } + self.ctx.pins.freeze(); let mut sizes = node .layout(self.ctx, &pod, self.styles)? .into_iter() .map(|frame| frame.size.y); + self.ctx.pins.unfreeze(); // For each region, we want to know the maximum height any // column requires. diff --git a/src/library/layout/locate.rs b/src/library/layout/locate.rs new file mode 100644 index 00000000..74480b91 --- /dev/null +++ b/src/library/layout/locate.rs @@ -0,0 +1,14 @@ +use crate::library::prelude::*; +use crate::model::{Group, LocateNode}; + +/// Format content with access to its location on the page. +pub fn locate(_: &mut Machine, args: &mut Args) -> TypResult<Value> { + let node = LocateNode::single(args.expect("recipe")?); + Ok(Value::Content(Content::Locate(node))) +} + +/// Create a new group of locatable elements. +pub fn group(_: &mut Machine, args: &mut Args) -> TypResult<Value> { + let key = args.expect("key")?; + Ok(Value::dynamic(Group::new(key))) +} diff --git a/src/library/layout/mod.rs b/src/library/layout/mod.rs index 588b15aa..6cf5b550 100644 --- a/src/library/layout/mod.rs +++ b/src/library/layout/mod.rs @@ -5,6 +5,7 @@ mod columns; mod container; mod flow; mod grid; +mod locate; mod pad; mod page; mod place; @@ -16,6 +17,7 @@ pub use columns::*; pub use container::*; pub use flow::*; pub use grid::*; +pub use locate::*; pub use pad::*; pub use page::*; pub use place::*; diff --git a/src/library/mod.rs b/src/library/mod.rs index ac0cbb92..27658189 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -54,6 +54,8 @@ pub fn new() -> Scope { std.def_node::<layout::ColumnsNode>("columns"); std.def_node::<layout::ColbreakNode>("colbreak"); std.def_node::<layout::PlaceNode>("place"); + std.def_fn("locate", layout::locate); + std.def_fn("group", layout::group); // Graphics. std.def_node::<graphics::ImageNode>("image"); @@ -94,38 +96,38 @@ pub fn new() -> Scope { std.def_fn("lorem", utility::lorem); // Predefined colors. - std.def_const("black", Color::BLACK); - std.def_const("gray", Color::GRAY); - std.def_const("silver", Color::SILVER); - std.def_const("white", Color::WHITE); - std.def_const("navy", Color::NAVY); - std.def_const("blue", Color::BLUE); - std.def_const("aqua", Color::AQUA); - std.def_const("teal", Color::TEAL); - std.def_const("eastern", Color::EASTERN); - std.def_const("purple", Color::PURPLE); - std.def_const("fuchsia", Color::FUCHSIA); - std.def_const("maroon", Color::MAROON); - std.def_const("red", Color::RED); - std.def_const("orange", Color::ORANGE); - std.def_const("yellow", Color::YELLOW); - std.def_const("olive", Color::OLIVE); - std.def_const("green", Color::GREEN); - std.def_const("lime", Color::LIME); + std.define("black", Color::BLACK); + std.define("gray", Color::GRAY); + std.define("silver", Color::SILVER); + std.define("white", Color::WHITE); + std.define("navy", Color::NAVY); + std.define("blue", Color::BLUE); + std.define("aqua", Color::AQUA); + std.define("teal", Color::TEAL); + std.define("eastern", Color::EASTERN); + std.define("purple", Color::PURPLE); + std.define("fuchsia", Color::FUCHSIA); + std.define("maroon", Color::MAROON); + std.define("red", Color::RED); + std.define("orange", Color::ORANGE); + std.define("yellow", Color::YELLOW); + std.define("olive", Color::OLIVE); + std.define("green", Color::GREEN); + std.define("lime", Color::LIME); // Other constants. - std.def_const("ltr", Dir::LTR); - std.def_const("rtl", Dir::RTL); - std.def_const("ttb", Dir::TTB); - std.def_const("btt", Dir::BTT); - std.def_const("start", RawAlign::Start); - std.def_const("end", RawAlign::End); - std.def_const("left", RawAlign::Specific(Align::Left)); - std.def_const("center", RawAlign::Specific(Align::Center)); - std.def_const("right", RawAlign::Specific(Align::Right)); - std.def_const("top", RawAlign::Specific(Align::Top)); - std.def_const("horizon", RawAlign::Specific(Align::Horizon)); - std.def_const("bottom", RawAlign::Specific(Align::Bottom)); + std.define("ltr", Dir::LTR); + std.define("rtl", Dir::RTL); + std.define("ttb", Dir::TTB); + std.define("btt", Dir::BTT); + std.define("start", RawAlign::Start); + std.define("end", RawAlign::End); + std.define("left", RawAlign::Specific(Align::Left)); + std.define("center", RawAlign::Specific(Align::Center)); + std.define("right", RawAlign::Specific(Align::Right)); + std.define("top", RawAlign::Specific(Align::Top)); + std.define("horizon", RawAlign::Specific(Align::Horizon)); + std.define("bottom", RawAlign::Specific(Align::Bottom)); std } diff --git a/src/library/prelude.rs b/src/library/prelude.rs index 371d6776..a61157a7 100644 --- a/src/library/prelude.rs +++ b/src/library/prelude.rs @@ -9,8 +9,8 @@ pub use typst_macros::node; pub use crate::diag::{with_alternative, At, Error, StrResult, TypError, TypResult}; pub use crate::eval::{ - Arg, Args, Array, Cast, Dict, Func, Machine, Node, RawAlign, RawLength, RawStroke, - Scope, Smart, Value, + Arg, Args, Array, Cast, Dict, Dynamic, Func, Machine, Node, RawAlign, RawLength, + RawStroke, Scope, Smart, Value, }; pub use crate::frame::*; pub use crate::geom::*; diff --git a/src/library/text/link.rs b/src/library/text/link.rs index 2ce7a469..12cbaf59 100644 --- a/src/library/text/link.rs +++ b/src/library/text/link.rs @@ -1,6 +1,5 @@ use super::TextNode; use crate::library::prelude::*; -use crate::util::EcoString; /// Link text and other elements to an URL. #[derive(Debug, Hash)] @@ -24,7 +23,7 @@ impl LinkNode { let dest = args.expect::<Destination>("destination")?; let body = match dest { Destination::Url(_) => args.eat()?, - Destination::Internal(_, _) => Some(args.expect("body")?), + Destination::Internal(_) => Some(args.expect("body")?), }; Self { dest, body } })) @@ -36,10 +35,10 @@ castable! { Expected: "string or dictionary with `page`, `x`, and `y` keys", Value::Str(string) => Self::Url(string), Value::Dict(dict) => { - let page: i64 = dict.get(&EcoString::from_str("page"))?.clone().cast()?; - let x: RawLength = dict.get(&EcoString::from_str("x"))?.clone().cast()?; - let y: RawLength = dict.get(&EcoString::from_str("y"))?.clone().cast()?; - Self::Internal(page as usize, Point::new(x.length, y.length)) + let page: i64 = dict.get(&"page".into())?.clone().cast()?; + let x: RawLength = dict.get(&"x".into())?.clone().cast()?; + let y: RawLength = dict.get(&"y".into())?.clone().cast()?; + Self::Internal(Location { page: page as usize, pos: Point::new(x.length, y.length) }) }, } @@ -56,11 +55,7 @@ impl Show for LinkNode { dict! { "url" => match &self.dest { Destination::Url(url) => Value::Str(url.clone()), - Destination::Internal(page, point) => Value::Dict(dict!{ - "page" => Value::Int(*page as i64), - "x" => Value::Length(point.x.into()), - "y" => Value::Length(point.y.into()), - }), + Destination::Internal(loc) => Value::Dict(loc.encode()), }, "body" => match &self.body { Some(body) => Value::Content(body.clone()), @@ -79,7 +74,7 @@ impl Show for LinkNode { let shorter = text.len() < url.len(); Content::Text(if shorter { text.into() } else { url.clone() }) } - Destination::Internal(_, _) => panic!("missing body"), + Destination::Internal(_) => Content::Empty, })) } @@ -99,7 +94,7 @@ impl Show for LinkNode { if match styles.get(Self::UNDERLINE) { Smart::Auto => match &self.dest { Destination::Url(_) => true, - Destination::Internal(_, _) => false, + Destination::Internal(_) => false, }, Smart::Custom(underline) => underline, } { diff --git a/src/library/text/par.rs b/src/library/text/par.rs index 1269ffed..709dc756 100644 --- a/src/library/text/par.rs +++ b/src/library/text/par.rs @@ -26,6 +26,8 @@ pub enum ParChild { Spacing(Spacing), /// An arbitrary inline-level node. Node(LayoutNode), + /// A pin identified by index. + Pin(usize), } #[node] @@ -100,6 +102,7 @@ impl Debug for ParChild { Self::Quote { double } => write!(f, "Quote({double})"), Self::Spacing(kind) => write!(f, "{:?}", kind), Self::Node(node) => node.fmt(f), + Self::Pin(idx) => write!(f, "Pin({idx})"), } } } @@ -191,10 +194,11 @@ impl LinebreakNode { /// Range of a substring of text. type Range = std::ops::Range<usize>; -// The characters by which spacing and nodes are replaced in the paragraph's -// full text. -const SPACING_REPLACE: char = ' '; -const NODE_REPLACE: char = '\u{FFFC}'; +// The characters by which spacing, nodes and pins are replaced in the +// paragraph's full text. +const SPACING_REPLACE: char = ' '; // Space +const NODE_REPLACE: char = '\u{FFFC}'; // Object Replacement Character +const PIN_REPLACE: char = '\u{200D}'; // Zero Width Joiner /// A paragraph representation in which children are already layouted and text /// is already preshaped. @@ -275,6 +279,8 @@ enum Segment<'a> { Spacing(Spacing), /// An arbitrary inline-level layout node. Node(&'a LayoutNode), + /// A pin identified by index. + Pin(usize), } impl Segment<'_> { @@ -284,6 +290,7 @@ impl Segment<'_> { Self::Text(len) => len, Self::Spacing(_) => SPACING_REPLACE.len_utf8(), Self::Node(_) => NODE_REPLACE.len_utf8(), + Self::Pin(_) => PIN_REPLACE.len_utf8(), } } } @@ -301,6 +308,8 @@ enum Item<'a> { Frame(Arc<Frame>), /// A repeating node. Repeat(&'a RepeatNode, StyleChain<'a>), + /// A pin identified by index. + Pin(usize), } impl<'a> Item<'a> { @@ -318,16 +327,17 @@ impl<'a> Item<'a> { Self::Text(shaped) => shaped.text.len(), Self::Absolute(_) | Self::Fractional(_) => SPACING_REPLACE.len_utf8(), Self::Frame(_) | Self::Repeat(_, _) => NODE_REPLACE.len_utf8(), + Self::Pin(_) => PIN_REPLACE.len_utf8(), } } /// The natural width of the item. fn width(&self) -> Length { match self { - Item::Text(shaped) => shaped.width, - Item::Absolute(v) => *v, - Item::Fractional(_) | Self::Repeat(_, _) => Length::zero(), - Item::Frame(frame) => frame.size.x, + Self::Text(shaped) => shaped.width, + Self::Absolute(v) => *v, + Self::Frame(frame) => frame.size.x, + Self::Fractional(_) | Self::Repeat(_, _) | Self::Pin(_) => Length::zero(), } } } @@ -447,7 +457,7 @@ fn collect<'a>( } Segment::Text(full.len() - prev) } - ParChild::Quote { double } => { + &ParChild::Quote { double } => { let prev = full.len(); if styles.get(TextNode::SMART_QUOTES) { let lang = styles.get(TextNode::LANG); @@ -458,22 +468,27 @@ fn collect<'a>( ParChild::Quote { .. } => Some('"'), ParChild::Spacing(_) => Some(SPACING_REPLACE), ParChild::Node(_) => Some(NODE_REPLACE), + ParChild::Pin(_) => Some(PIN_REPLACE), }); - full.push_str(quoter.quote("es, *double, peeked)); + full.push_str(quoter.quote("es, double, peeked)); } else { - full.push(if *double { '"' } else { '\'' }); + full.push(if double { '"' } else { '\'' }); } Segment::Text(full.len() - prev) } - ParChild::Spacing(spacing) => { + &ParChild::Spacing(spacing) => { full.push(SPACING_REPLACE); - Segment::Spacing(*spacing) + Segment::Spacing(spacing) } ParChild::Node(node) => { full.push(NODE_REPLACE); Segment::Node(node) } + &ParChild::Pin(idx) => { + full.push(PIN_REPLACE); + Segment::Pin(idx) + } }; if let Some(last) = full.chars().last() { @@ -540,6 +555,7 @@ fn prepare<'a>( items.push(Item::Frame(frame)); } } + Segment::Pin(idx) => items.push(Item::Pin(idx)), } cursor = end; @@ -1171,6 +1187,11 @@ fn commit( } offset = before + width; } + Item::Pin(idx) => { + let mut frame = Frame::new(Size::zero()); + frame.push(Point::zero(), Element::Pin(*idx)); + push(&mut offset, MaybeShared::Owned(frame)); + } } } |
