summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorMartin Haug <mhaug@live.de>2022-05-27 16:39:06 +0200
committerGitHub <noreply@github.com>2022-05-27 16:39:06 +0200
commit73086b5a7c1b0f9f638165803c237901499adb64 (patch)
treec120a2449aaf325cb675ea3363ee69758a734d86 /src/library
parent99cb655832161d4ebec73273a15453a8f6acc1b7 (diff)
parent8ba11b0722599892499337b3272cec38945d11de (diff)
Merge pull request #71 from typst/pins
Diffstat (limited to 'src/library')
-rw-r--r--src/library/layout/grid.rs4
-rw-r--r--src/library/layout/locate.rs14
-rw-r--r--src/library/layout/mod.rs2
-rw-r--r--src/library/mod.rs62
-rw-r--r--src/library/prelude.rs4
-rw-r--r--src/library/text/link.rs21
-rw-r--r--src/library/text/par.rs47
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(&quotes, *double, peeked));
+ full.push_str(quoter.quote(&quotes, 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));
+ }
}
}