summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/typst-ide/src/jump.rs3
-rw-r--r--crates/typst-pdf/src/page.rs8
-rw-r--r--crates/typst-render/src/lib.rs8
-rw-r--r--crates/typst-svg/src/lib.rs8
-rw-r--r--crates/typst/src/foundations/content.rs10
-rw-r--r--crates/typst/src/introspection/counter.rs4
-rw-r--r--crates/typst/src/introspection/introspector.rs12
-rw-r--r--crates/typst/src/introspection/locator.rs4
-rw-r--r--crates/typst/src/introspection/mod.rs83
-rw-r--r--crates/typst/src/layout/flow.rs99
-rw-r--r--crates/typst/src/layout/frame.rs85
-rw-r--r--crates/typst/src/layout/hide.rs10
-rw-r--r--crates/typst/src/layout/inline/mod.rs50
-rw-r--r--crates/typst/src/math/fragment.rs14
-rw-r--r--crates/typst/src/math/mod.rs15
-rw-r--r--crates/typst/src/math/stretch.rs2
-rw-r--r--crates/typst/src/model/link.rs6
-rw-r--r--crates/typst/src/realize/mod.rs8
-rw-r--r--crates/typst/src/realize/process.rs35
19 files changed, 245 insertions, 219 deletions
diff --git a/crates/typst-ide/src/jump.rs b/crates/typst-ide/src/jump.rs
index 7c3c7569..45186d5b 100644
--- a/crates/typst-ide/src/jump.rs
+++ b/crates/typst-ide/src/jump.rs
@@ -1,7 +1,6 @@
use std::num::NonZeroUsize;
use ecow::EcoString;
-use typst::introspection::Meta;
use typst::layout::{Frame, FrameItem, Point, Position, Size};
use typst::model::{Destination, Document};
use typst::syntax::{FileId, LinkedNode, Side, Source, Span, SyntaxKind};
@@ -37,7 +36,7 @@ pub fn jump_from_click(
) -> Option<Jump> {
// Try to find a link first.
for (pos, item) in frame.items() {
- if let FrameItem::Meta(Meta::Link(dest), size) = item {
+ if let FrameItem::Link(dest, size) = item {
if is_in_rect(*pos, *size, click) {
return Some(match dest {
Destination::Url(url) => Jump::Url(url.clone()),
diff --git a/crates/typst-pdf/src/page.rs b/crates/typst-pdf/src/page.rs
index 557daa80..42c87f8e 100644
--- a/crates/typst-pdf/src/page.rs
+++ b/crates/typst-pdf/src/page.rs
@@ -12,7 +12,6 @@ use pdf_writer::types::{
};
use pdf_writer::writers::{PageLabel, Resources};
use pdf_writer::{Content, Filter, Finish, Name, Rect, Ref, Str, TextStr};
-use typst::introspection::Meta;
use typst::layout::{
Abs, Em, Frame, FrameItem, GroupItem, Page, Point, Ratio, Size, Transform,
};
@@ -749,11 +748,8 @@ pub(crate) fn write_frame(ctx: &mut PageContext, frame: &Frame) {
FrameItem::Text(text) => write_text(ctx, pos, text),
FrameItem::Shape(shape, _) => write_shape(ctx, pos, shape),
FrameItem::Image(image, size, _) => write_image(ctx, x, y, image, *size),
- FrameItem::Meta(meta, size) => match meta {
- Meta::Link(dest) => write_link(ctx, pos, dest, *size),
- Meta::Elem(_) => {}
- Meta::Hide => {}
- },
+ FrameItem::Link(dest, size) => write_link(ctx, pos, dest, *size),
+ FrameItem::Tag(_) => {}
}
}
}
diff --git a/crates/typst-render/src/lib.rs b/crates/typst-render/src/lib.rs
index 08c80050..305dcd1f 100644
--- a/crates/typst-render/src/lib.rs
+++ b/crates/typst-render/src/lib.rs
@@ -6,7 +6,6 @@ mod shape;
mod text;
use tiny_skia as sk;
-use typst::introspection::Meta;
use typst::layout::{
Abs, Axes, Frame, FrameItem, FrameKind, GroupItem, Point, Size, Transform,
};
@@ -162,11 +161,8 @@ fn render_frame(canvas: &mut sk::Pixmap, state: State, frame: &Frame) {
FrameItem::Image(image, size, _) => {
image::render_image(canvas, state.pre_translate(*pos), image, *size);
}
- FrameItem::Meta(meta, _) => match meta {
- Meta::Link(_) => {}
- Meta::Elem(_) => {}
- Meta::Hide => {}
- },
+ FrameItem::Link(_, _) => {}
+ FrameItem::Tag(_) => {}
}
}
}
diff --git a/crates/typst-svg/src/lib.rs b/crates/typst-svg/src/lib.rs
index 798a354c..01ed3fae 100644
--- a/crates/typst-svg/src/lib.rs
+++ b/crates/typst-svg/src/lib.rs
@@ -184,8 +184,9 @@ impl SVGRenderer {
}
for (pos, item) in frame.items() {
- // File size optimization
- if matches!(item, FrameItem::Meta(_, _)) {
+ // File size optimization.
+ // TODO: SVGs could contain links, couldn't they?
+ if matches!(item, FrameItem::Link(_, _) | FrameItem::Tag(_)) {
continue;
}
@@ -206,7 +207,8 @@ impl SVGRenderer {
self.render_shape(state.pre_translate(*pos), shape)
}
FrameItem::Image(image, size, _) => self.render_image(image, size),
- FrameItem::Meta(_, _) => unreachable!(),
+ FrameItem::Link(_, _) => unreachable!(),
+ FrameItem::Tag(_) => unreachable!(),
};
self.xml.end_element();
diff --git a/crates/typst/src/foundations/content.rs b/crates/typst/src/foundations/content.rs
index f11c74c0..6f8e46b4 100644
--- a/crates/typst/src/foundations/content.rs
+++ b/crates/typst/src/foundations/content.rs
@@ -18,9 +18,9 @@ use crate::foundations::{
NativeElement, Recipe, RecipeIndex, Repr, Selector, Str, Style, StyleChain, Styles,
Value,
};
-use crate::introspection::{Location, Meta, MetaElem};
+use crate::introspection::{Location, TagElem};
use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides};
-use crate::model::{Destination, EmphElem, StrongElem};
+use crate::model::{Destination, EmphElem, LinkElem, StrongElem};
use crate::realize::{Behave, Behaviour};
use crate::syntax::Span;
use crate::text::UnderlineElem;
@@ -472,16 +472,16 @@ impl Content {
/// Link the content somewhere.
pub fn linked(self, dest: Destination) -> Self {
- self.styled(MetaElem::set_data(smallvec![Meta::Link(dest)]))
+ self.styled(LinkElem::set_dests(smallvec![dest]))
}
/// Make the content linkable by `.linked(Destination::Location(loc))`.
///
/// Should be used in combination with [`Location::variant`].
pub fn backlinked(self, loc: Location) -> Self {
- let mut backlink = Content::empty();
+ let mut backlink = Content::empty().spanned(self.span());
backlink.set_location(loc);
- self.styled(MetaElem::set_data(smallvec![Meta::Elem(backlink)]))
+ TagElem::packed(backlink) + self
}
/// Set alignments for this content.
diff --git a/crates/typst/src/introspection/counter.rs b/crates/typst/src/introspection/counter.rs
index 93f954a2..82f8bbfc 100644
--- a/crates/typst/src/introspection/counter.rs
+++ b/crates/typst/src/introspection/counter.rs
@@ -13,7 +13,7 @@ use crate::foundations::{
Element, Func, IntoValue, Label, LocatableSelector, NativeElement, Packed, Repr,
Selector, Show, Smart, Str, StyleChain, Value,
};
-use crate::introspection::{Introspector, Locatable, Location, Locator, Meta};
+use crate::introspection::{Introspector, Locatable, Location, Locator};
use crate::layout::{Frame, FrameItem, PageElem};
use crate::math::EquationElem;
use crate::model::{FigureElem, HeadingElem, Numbering, NumberingPattern};
@@ -820,7 +820,7 @@ impl ManualPageCounter {
for (_, item) in page.items() {
match item {
FrameItem::Group(group) => self.visit(engine, &group.frame)?,
- FrameItem::Meta(Meta::Elem(elem), _) => {
+ FrameItem::Tag(elem) => {
let Some(elem) = elem.to_packed::<CounterUpdateElem>() else {
continue;
};
diff --git a/crates/typst/src/introspection/introspector.rs b/crates/typst/src/introspection/introspector.rs
index 93dec21c..b6c32a47 100644
--- a/crates/typst/src/introspection/introspector.rs
+++ b/crates/typst/src/introspection/introspector.rs
@@ -10,7 +10,7 @@ use smallvec::SmallVec;
use crate::diag::{bail, StrResult};
use crate::foundations::{Content, Label, Repr, Selector};
-use crate::introspection::{Location, Meta};
+use crate::introspection::Location;
use crate::layout::{Frame, FrameItem, Page, Point, Position, Transform};
use crate::model::Numbering;
use crate::utils::NonZeroExt;
@@ -61,18 +61,18 @@ impl Introspector {
.pre_concat(group.transform);
self.extract(&group.frame, page, ts);
}
- FrameItem::Meta(Meta::Elem(content), _)
- if !self.elems.contains_key(&content.location().unwrap()) =>
+ FrameItem::Tag(elem)
+ if !self.elems.contains_key(&elem.location().unwrap()) =>
{
let pos = pos.transform(ts);
let ret = self.elems.insert(
- content.location().unwrap(),
- (content.clone(), Position { page, point: pos }),
+ elem.location().unwrap(),
+ (elem.clone(), Position { page, point: pos }),
);
assert!(ret.is_none(), "duplicate locations");
// Build the label cache.
- if let Some(label) = content.label() {
+ if let Some(label) = elem.label() {
self.labels.entry(label).or_default().push(self.elems.len() - 1);
}
}
diff --git a/crates/typst/src/introspection/locator.rs b/crates/typst/src/introspection/locator.rs
index 7346b6fb..9c02f6db 100644
--- a/crates/typst/src/introspection/locator.rs
+++ b/crates/typst/src/introspection/locator.rs
@@ -3,7 +3,7 @@ use std::collections::HashMap;
use comemo::{Track, Tracked, Validate};
-use crate::introspection::{Location, Meta};
+use crate::introspection::Location;
use crate::layout::{Frame, FrameItem};
/// Provides locations for elements in the document.
@@ -77,7 +77,7 @@ impl<'a> Locator<'a> {
for (_, item) in frame.items() {
match item {
FrameItem::Group(group) => self.visit_frame(&group.frame),
- FrameItem::Meta(Meta::Elem(elem), _) => {
+ FrameItem::Tag(elem) => {
let hashes = self.hashes.get_mut();
let loc = elem.location().unwrap();
let entry = hashes.entry(loc.hash).or_default();
diff --git a/crates/typst/src/introspection/mod.rs b/crates/typst/src/introspection/mod.rs
index 4ccff0c1..9bf61d35 100644
--- a/crates/typst/src/introspection/mod.rs
+++ b/crates/typst/src/introspection/mod.rs
@@ -23,15 +23,12 @@ pub use self::metadata::*;
pub use self::query_::*;
pub use self::state::*;
-use std::fmt::{self, Debug, Formatter};
-
-use ecow::{eco_format, EcoString};
-use smallvec::SmallVec;
-
+use crate::diag::{bail, SourceResult};
+use crate::engine::Engine;
+use crate::foundations::NativeElement;
use crate::foundations::{
- category, elem, ty, Category, Content, Packed, Repr, Scope, Unlabellable,
+ category, elem, Args, Category, Construct, Content, Packed, Scope, Unlabellable,
};
-use crate::model::Destination;
use crate::realize::{Behave, Behaviour};
/// Interactions between document parts.
@@ -59,59 +56,39 @@ pub fn define(global: &mut Scope) {
global.define_func::<locate>();
}
-/// Hosts metadata and ensures metadata is produced even for empty elements.
-#[elem(Behave, Unlabellable)]
-pub struct MetaElem {
- /// Metadata that should be attached to all elements affected by this style
- /// property.
- ///
- /// This must be accessed and applied to all frames produced by elements
- /// that manually handle styles (because their children can have varying
- /// styles). This currently includes flow, par, and equation.
- ///
- /// Other elements don't manually need to handle it because their parents
- /// that result from realization will take care of it and the metadata can
- /// only apply to them as a whole, not part of it (because they don't manage
- /// styles).
- #[fold]
- pub data: SmallVec<[Meta; 1]>,
+/// Holds a locatable element that was realized.
+///
+/// The `TagElem` is handled by all layouters. The held element becomes
+/// available for introspection in the next compiler iteration.
+#[elem(Behave, Unlabellable, Construct)]
+pub struct TagElem {
+ /// The introspectible element.
+ #[required]
+ #[internal]
+ pub elem: Content,
}
-impl Unlabellable for Packed<MetaElem> {}
-
-impl Behave for Packed<MetaElem> {
- fn behaviour(&self) -> Behaviour {
- Behaviour::Invisible
+impl TagElem {
+ /// Create a packed tag element.
+ pub fn packed(elem: Content) -> Content {
+ let span = elem.span();
+ let mut content = Self::new(elem).pack().spanned(span);
+ // We can skip preparation for the `TagElem`.
+ content.mark_prepared();
+ content
}
}
-/// Meta information that isn't visible or renderable.
-#[ty]
-#[derive(Clone, PartialEq, Hash)]
-pub enum Meta {
- /// An internal or external link to a destination.
- Link(Destination),
- /// An identifiable element that produces something within the area this
- /// metadata is attached to.
- Elem(Content),
- /// Indicates that content should be hidden. This variant doesn't appear
- /// in the final frames as it is removed alongside the content that should
- /// be hidden.
- Hide,
-}
-
-impl Debug for Meta {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- match self {
- Self::Link(dest) => write!(f, "Link({dest:?})"),
- Self::Elem(content) => write!(f, "Elem({:?})", content.func()),
- Self::Hide => f.pad("Hide"),
- }
+impl Construct for TagElem {
+ fn construct(_: &mut Engine, args: &mut Args) -> SourceResult<Content> {
+ bail!(args.span, "cannot be constructed manually")
}
}
-impl Repr for Meta {
- fn repr(&self) -> EcoString {
- eco_format!("{self:?}")
+impl Unlabellable for Packed<TagElem> {}
+
+impl Behave for Packed<TagElem> {
+ fn behaviour(&self) -> Behaviour {
+ Behaviour::Invisible
}
}
diff --git a/crates/typst/src/layout/flow.rs b/crates/typst/src/layout/flow.rs
index 6f3b8f16..c550ac28 100644
--- a/crates/typst/src/layout/flow.rs
+++ b/crates/typst/src/layout/flow.rs
@@ -11,7 +11,7 @@ use crate::engine::Engine;
use crate::foundations::{
elem, Content, NativeElement, Packed, Resolve, Smart, StyleChain, StyledElem,
};
-use crate::introspection::{Meta, MetaElem};
+use crate::introspection::TagElem;
use crate::layout::{
Abs, AlignElem, Axes, BlockElem, ColbreakElem, ColumnsElem, FixedAlignment, Fr,
Fragment, Frame, FrameItem, LayoutMultiple, LayoutSingle, PlaceElem, Point, Regions,
@@ -55,8 +55,8 @@ impl LayoutMultiple for Packed<FlowElem> {
styles = outer.chain(&styled.styles);
}
- if child.is::<MetaElem>() {
- layouter.layout_meta(styles);
+ if let Some(elem) = child.to_packed::<TagElem>() {
+ layouter.layout_tag(elem);
} else if let Some(elem) = child.to_packed::<VElem>() {
layouter.layout_spacing(engine, elem, styles)?;
} else if let Some(placed) = child.to_packed::<PlaceElem>() {
@@ -107,6 +107,8 @@ struct FlowLayouter<'a> {
last_was_par: bool,
/// Spacing and layouted blocks for the current region.
items: Vec<FlowItem>,
+ /// A queue of tags that will be attached to the next frame.
+ pending_tags: Vec<Content>,
/// A queue of floating elements.
pending_floats: Vec<FlowItem>,
/// Whether we have any footnotes in the current region.
@@ -166,7 +168,9 @@ impl FlowItem {
Self::Placed { float: false, .. } => true,
Self::Frame { frame, .. } => {
frame.size().is_zero()
- && frame.items().all(|(_, item)| matches!(item, FrameItem::Meta(..)))
+ && frame.items().all(|(_, item)| {
+ matches!(item, FrameItem::Link(_, _) | FrameItem::Tag(_))
+ })
}
_ => false,
}
@@ -190,6 +194,7 @@ impl<'a> FlowLayouter<'a> {
initial: regions.size,
last_was_par: false,
items: vec![],
+ pending_tags: vec![],
pending_floats: vec![],
has_footnotes: false,
footnote_config: FootnoteConfig {
@@ -202,15 +207,8 @@ impl<'a> FlowLayouter<'a> {
}
/// Place explicit metadata into the flow.
- fn layout_meta(&mut self, styles: StyleChain) {
- let mut frame = Frame::soft(Size::zero());
- frame.meta(styles, true);
- self.items.push(FlowItem::Frame {
- frame,
- align: Axes::splat(FixedAlignment::Start),
- sticky: true,
- movable: false,
- });
+ fn layout_tag(&mut self, tag: &Packed<TagElem>) {
+ self.pending_tags.push(tag.elem.clone());
}
/// Layout vertical spacing.
@@ -232,6 +230,27 @@ impl<'a> FlowLayouter<'a> {
)
}
+ /// Layout a placed element.
+ fn layout_placed(
+ &mut self,
+ engine: &mut Engine,
+ placed: &Packed<PlaceElem>,
+ styles: StyleChain,
+ ) -> SourceResult<()> {
+ let float = placed.float(styles);
+ let clearance = placed.clearance(styles);
+ let alignment = placed.alignment(styles);
+ let delta = Axes::new(placed.dx(styles), placed.dy(styles)).resolve(styles);
+ let x_align = alignment.map_or(FixedAlignment::Center, |align| {
+ align.x().unwrap_or_default().resolve(styles)
+ });
+ let y_align = alignment.map(|align| align.y().map(|y| y.resolve(styles)));
+ let mut frame = placed.layout(engine, styles, self.regions.base())?.into_frame();
+ frame.post_process(styles);
+ let item = FlowItem::Placed { frame, x_align, y_align, delta, float, clearance };
+ self.layout_item(engine, item)
+ }
+
/// Layout a paragraph.
fn layout_par(
&mut self,
@@ -279,11 +298,12 @@ impl<'a> FlowLayouter<'a> {
}
}
- for (i, frame) in lines.into_iter().enumerate() {
+ for (i, mut frame) in lines.into_iter().enumerate() {
if i > 0 {
self.layout_item(engine, FlowItem::Absolute(leading, true))?;
}
+ self.drain_tag(&mut frame);
self.layout_item(
engine,
FlowItem::Frame { frame, align, sticky: false, movable: true },
@@ -305,7 +325,8 @@ impl<'a> FlowLayouter<'a> {
let sticky = BlockElem::sticky_in(styles);
let pod = Regions::one(self.regions.base(), Axes::splat(false));
let mut frame = layoutable.layout(engine, styles, pod)?;
- frame.meta(styles, false);
+ self.drain_tag(&mut frame);
+ frame.post_process(styles);
self.layout_item(
engine,
FlowItem::Frame { frame, align, sticky, movable: true },
@@ -314,27 +335,6 @@ impl<'a> FlowLayouter<'a> {
Ok(())
}
- /// Layout a placed element.
- fn layout_placed(
- &mut self,
- engine: &mut Engine,
- placed: &Packed<PlaceElem>,
- styles: StyleChain,
- ) -> SourceResult<()> {
- let float = placed.float(styles);
- let clearance = placed.clearance(styles);
- let alignment = placed.alignment(styles);
- let delta = Axes::new(placed.dx(styles), placed.dy(styles)).resolve(styles);
- let x_align = alignment.map_or(FixedAlignment::Center, |align| {
- align.x().unwrap_or_default().resolve(styles)
- });
- let y_align = alignment.map(|align| align.y().map(|y| y.resolve(styles)));
- let mut frame = placed.layout(engine, styles, self.regions.base())?.into_frame();
- frame.meta(styles, false);
- let item = FlowItem::Placed { frame, x_align, y_align, delta, float, clearance };
- self.layout_item(engine, item)
- }
-
/// Layout into multiple regions.
fn layout_multiple(
&mut self,
@@ -380,7 +380,8 @@ impl<'a> FlowLayouter<'a> {
self.finish_region(engine, false)?;
}
- frame.meta(styles, false);
+ self.drain_tag(&mut frame);
+ frame.post_process(styles);
self.layout_item(
engine,
FlowItem::Frame { frame, align, sticky, movable: false },
@@ -396,6 +397,17 @@ impl<'a> FlowLayouter<'a> {
Ok(())
}
+ /// Attach currently pending metadata to the frame.
+ fn drain_tag(&mut self, frame: &mut Frame) {
+ if !self.pending_tags.is_empty() && !frame.is_empty() {
+ frame.prepend_multiple(
+ self.pending_tags
+ .drain(..)
+ .map(|elem| (Point::zero(), FrameItem::Tag(elem))),
+ );
+ }
+ }
+
/// Layout a finished frame.
fn layout_item(
&mut self,
@@ -628,6 +640,13 @@ impl<'a> FlowLayouter<'a> {
}
}
+ if force && !self.pending_tags.is_empty() {
+ let pos = Point::with_y(offset);
+ output.push_multiple(
+ self.pending_tags.drain(..).map(|elem| (pos, FrameItem::Tag(elem))),
+ );
+ }
+
// Advance to the next region.
self.finished.push(output);
self.regions.next();
@@ -782,10 +801,10 @@ fn find_footnotes(notes: &mut Vec<Packed<FootnoteElem>>, frame: &Frame) {
for (_, item) in frame.items() {
match item {
FrameItem::Group(group) => find_footnotes(notes, &group.frame),
- FrameItem::Meta(Meta::Elem(content), _)
- if !notes.iter().any(|note| note.location() == content.location()) =>
+ FrameItem::Tag(elem)
+ if !notes.iter().any(|note| note.location() == elem.location()) =>
{
- let Some(footnote) = content.to_packed::<FootnoteElem>() else {
+ let Some(footnote) = elem.to_packed::<FootnoteElem>() else {
continue;
};
notes.push(footnote.clone());
diff --git a/crates/typst/src/layout/frame.rs b/crates/typst/src/layout/frame.rs
index be1a7cb3..be207dc3 100644
--- a/crates/typst/src/layout/frame.rs
+++ b/crates/typst/src/layout/frame.rs
@@ -4,11 +4,14 @@ use std::fmt::{self, Debug, Formatter};
use std::num::NonZeroUsize;
use std::sync::Arc;
-use crate::foundations::{cast, dict, Dict, StyleChain, Value};
-use crate::introspection::{Meta, MetaElem};
+use smallvec::SmallVec;
+
+use crate::foundations::{cast, dict, Content, Dict, StyleChain, Value};
use crate::layout::{
- Abs, Axes, Corners, FixedAlignment, Length, Point, Rel, Sides, Size, Transform,
+ Abs, Axes, Corners, FixedAlignment, HideElem, Length, Point, Rel, Sides, Size,
+ Transform,
};
+use crate::model::{Destination, LinkElem};
use crate::syntax::Span;
use crate::text::TextItem;
use crate::utils::{LazyHash, Numeric};
@@ -150,6 +153,17 @@ impl Frame {
Arc::make_mut(&mut self.items).push((pos, item));
}
+ /// Add multiple items at a position in the foreground.
+ ///
+ /// The first item in the iterator will be the one that is most in the
+ /// background.
+ pub fn push_multiple<I>(&mut self, items: I)
+ where
+ I: IntoIterator<Item = (Point, FrameItem)>,
+ {
+ Arc::make_mut(&mut self.items).extend(items);
+ }
+
/// Add a frame at a position in the foreground.
///
/// Automatically decides whether to inline the frame or to include it as a
@@ -162,11 +176,6 @@ impl Frame {
}
}
- /// Add zero-sized metadata at the origin.
- pub fn push_positionless_meta(&mut self, meta: Meta) {
- self.push(Point::zero(), FrameItem::Meta(meta, Size::zero()));
- }
-
/// Insert an item at the given layer in the frame.
///
/// This panics if the layer is greater than the number of layers present.
@@ -284,31 +293,40 @@ impl Frame {
}
}
- /// Attach the metadata from this style chain to the frame.
+ /// Apply late-stage properties from the style chain to this frame. This
+ /// includes:
+ /// - `HideElem::hidden`
+ /// - `LinkElem::dests`
///
- /// If `force` is true, then the metadata is attached even when
- /// the frame is empty.
- // TODO: when would you want to pass true to `force` as opposed to false?
- pub fn meta(&mut self, styles: StyleChain, force: bool) {
- if force || !self.is_empty() {
- self.meta_iter(MetaElem::data_in(styles));
+ /// This must be called on all frames produced by elements
+ /// that manually handle styles (because their children can have varying
+ /// styles). This currently includes flow, par, and equation.
+ ///
+ /// Other elements don't manually need to handle it because their parents
+ /// that result from realization will take care of it and the styles can
+ /// only apply to them as a whole, not part of it (because they don't manage
+ /// styles).
+ pub fn post_process(&mut self, styles: StyleChain) {
+ if !self.is_empty() {
+ self.post_process_raw(
+ LinkElem::dests_in(styles),
+ HideElem::hidden_in(styles),
+ );
}
}
- /// Attach metadata from an iterator.
- pub fn meta_iter(&mut self, iter: impl IntoIterator<Item = Meta>) {
- let mut hide = false;
- let size = self.size;
- self.prepend_multiple(iter.into_iter().filter_map(|meta| {
- if matches!(meta, Meta::Hide) {
- hide = true;
- None
- } else {
- Some((Point::zero(), FrameItem::Meta(meta, size)))
+ /// Apply raw late-stage properties from the raw data.
+ pub fn post_process_raw(&mut self, dests: SmallVec<[Destination; 1]>, hide: bool) {
+ if !self.is_empty() {
+ let size = self.size;
+ self.push_multiple(
+ dests
+ .into_iter()
+ .map(|dest| (Point::zero(), FrameItem::Link(dest, size))),
+ );
+ if hide {
+ self.hide();
}
- }));
- if hide {
- self.hide();
}
}
@@ -319,7 +337,7 @@ impl Frame {
group.frame.hide();
!group.frame.is_empty()
}
- FrameItem::Meta(Meta::Elem(_), _) => true,
+ FrameItem::Tag(_) => true,
_ => false,
});
}
@@ -488,8 +506,10 @@ pub enum FrameItem {
Shape(Shape, Span),
/// An image and its size.
Image(Image, Size, Span),
- /// Meta information and the region it applies to.
- Meta(Meta, Size),
+ /// An internal or external link to a destination.
+ Link(Destination, Size),
+ /// An introspectable element that produced something within this frame.
+ Tag(Content),
}
impl Debug for FrameItem {
@@ -499,7 +519,8 @@ impl Debug for FrameItem {
Self::Text(text) => write!(f, "{text:?}"),
Self::Shape(shape, _) => write!(f, "{shape:?}"),
Self::Image(image, _, _) => write!(f, "{image:?}"),
- Self::Meta(meta, _) => write!(f, "{meta:?}"),
+ Self::Link(dest, _) => write!(f, "Link({dest:?})"),
+ Self::Tag(elem) => write!(f, "Tag({elem:?})"),
}
}
}
diff --git a/crates/typst/src/layout/hide.rs b/crates/typst/src/layout/hide.rs
index 7a8618c1..1b8b9bd5 100644
--- a/crates/typst/src/layout/hide.rs
+++ b/crates/typst/src/layout/hide.rs
@@ -1,9 +1,6 @@
-use smallvec::smallvec;
-
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{elem, Content, Packed, Show, StyleChain};
-use crate::introspection::{Meta, MetaElem};
/// Hides content without affecting layout.
///
@@ -22,11 +19,16 @@ pub struct HideElem {
/// The content to hide.
#[required]
pub body: Content,
+
+ /// This style is set on the content contained in the `hide` element.
+ #[internal]
+ #[ghost]
+ pub hidden: bool,
}
impl Show for Packed<HideElem> {
#[typst_macros::time(name = "hide", span = self.span())]
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
- Ok(self.body().clone().styled(MetaElem::set_data(smallvec![Meta::Hide])))
+ Ok(self.body().clone().styled(HideElem::set_hidden(true)))
}
}
diff --git a/crates/typst/src/layout/inline/mod.rs b/crates/typst/src/layout/inline/mod.rs
index 5a74d3d2..6a39537c 100644
--- a/crates/typst/src/layout/inline/mod.rs
+++ b/crates/typst/src/layout/inline/mod.rs
@@ -14,10 +14,10 @@ use crate::diag::{bail, SourceResult};
use crate::engine::{Engine, Route};
use crate::eval::Tracer;
use crate::foundations::{Content, Packed, Resolve, Smart, StyleChain, StyledElem};
-use crate::introspection::{Introspector, Locator, MetaElem};
+use crate::introspection::{Introspector, Locator, TagElem};
use crate::layout::{
- Abs, AlignElem, Axes, BoxElem, Dir, Em, FixedAlignment, Fr, Fragment, Frame, HElem,
- Point, Regions, Size, Sizing, Spacing,
+ Abs, AlignElem, Axes, BoxElem, Dir, Em, FixedAlignment, Fr, Fragment, Frame,
+ FrameItem, HElem, Point, Regions, Size, Sizing, Spacing,
};
use crate::math::{EquationElem, MathParItem};
use crate::model::{Linebreaks, ParElem};
@@ -201,8 +201,8 @@ enum Segment<'a> {
Equation(Vec<MathParItem>),
/// A box with arbitrary content.
Box(&'a Packed<BoxElem>, bool),
- /// Metadata.
- Meta,
+ /// A tag.
+ Tag(&'a Packed<TagElem>),
}
impl Segment<'_> {
@@ -220,7 +220,7 @@ impl Segment<'_> {
.chain([LTR_ISOLATE, POP_ISOLATE])
.map(char::len_utf8)
.sum(),
- Self::Meta => 0,
+ Self::Tag(_) => 0,
}
}
}
@@ -236,8 +236,8 @@ enum Item<'a> {
Fractional(Fr, Option<(&'a Packed<BoxElem>, StyleChain<'a>)>),
/// Layouted inline-level content.
Frame(Frame),
- /// Metadata.
- Meta(Frame),
+ /// A tag.
+ Tag(&'a Packed<TagElem>),
/// An item that is invisible and needs to be skipped, e.g. a Unicode
/// isolate.
Skip(char),
@@ -266,7 +266,7 @@ impl<'a> Item<'a> {
Self::Text(shaped) => shaped.text.len(),
Self::Absolute(_) | Self::Fractional(_, _) => SPACING_REPLACE.len_utf8(),
Self::Frame(_) => OBJ_REPLACE.len_utf8(),
- Self::Meta(_) => 0,
+ Self::Tag(_) => 0,
Self::Skip(c) => c.len_utf8(),
}
}
@@ -277,7 +277,7 @@ impl<'a> Item<'a> {
Self::Text(shaped) => shaped.width,
Self::Absolute(v) => *v,
Self::Frame(frame) => frame.width(),
- Self::Fractional(_, _) | Self::Meta(_) => Abs::zero(),
+ Self::Fractional(_, _) | Self::Tag(_) => Abs::zero(),
Self::Skip(_) => Abs::zero(),
}
}
@@ -531,6 +531,9 @@ fn collect<'a>(
} else if child.is::<SpaceElem>()
|| child.is::<HElem>()
|| child.is::<LinebreakElem>()
+ // This is a temporary hack. We should rather skip these
+ // and peek at the next child.
+ || child.is::<TagElem>()
{
Some(SPACING_REPLACE)
} else {
@@ -548,7 +551,7 @@ fn collect<'a>(
let mut items = elem.layout_inline(engine, styles, pod)?;
for item in &mut items {
let MathParItem::Frame(frame) = item else { continue };
- frame.meta(styles, false);
+ frame.post_process(styles);
}
full.push(LTR_ISOLATE);
full.extend(items.iter().map(MathParItem::text));
@@ -558,8 +561,8 @@ fn collect<'a>(
let frac = elem.width(styles).is_fractional();
full.push(if frac { SPACING_REPLACE } else { OBJ_REPLACE });
Segment::Box(elem, frac)
- } else if child.is::<MetaElem>() {
- Segment::Meta
+ } else if let Some(elem) = child.to_packed::<TagElem>() {
+ Segment::Tag(elem)
} else {
bail!(child.span(), "unexpected paragraph child");
};
@@ -643,15 +646,13 @@ fn prepare<'a>(
} else {
let pod = Regions::one(region, Axes::splat(false));
let mut frame = elem.layout(engine, styles, pod)?;
- frame.meta(styles, false);
+ frame.post_process(styles);
frame.translate(Point::with_y(TextElem::baseline_in(styles)));
items.push(Item::Frame(frame));
}
}
- Segment::Meta => {
- let mut frame = Frame::soft(Size::zero());
- frame.meta(styles, true);
- items.push(Item::Meta(frame));
+ Segment::Tag(tag) => {
+ items.push(Item::Tag(tag));
}
}
@@ -687,7 +688,7 @@ fn prepare<'a>(
/// See Requirements for Chinese Text Layout, Section 3.2.2 Mixed Text Composition in Horizontal
/// Written Mode
fn add_cjk_latin_spacing(items: &mut [Item]) {
- let mut items = items.iter_mut().filter(|x| !matches!(x, Item::Meta(_))).peekable();
+ let mut items = items.iter_mut().filter(|x| !matches!(x, Item::Tag(_))).peekable();
let mut prev: Option<&ShapedGlyph> = None;
while let Some(item) = items.next() {
let Some(text) = item.text_mut() else {
@@ -1410,7 +1411,7 @@ fn commit(
let region = Size::new(amount, full);
let pod = Regions::one(region, Axes::new(true, false));
let mut frame = elem.layout(engine, *styles, pod)?;
- frame.meta(*styles, false);
+ frame.post_process(*styles);
frame.translate(Point::with_y(TextElem::baseline_in(*styles)));
push(&mut offset, frame);
} else {
@@ -1420,12 +1421,17 @@ fn commit(
Item::Text(shaped) => {
let mut frame =
shaped.build(engine, justification_ratio, extra_justification);
- frame.meta(shaped.styles, false);
+ frame.post_process(shaped.styles);
push(&mut offset, frame);
}
- Item::Frame(frame) | Item::Meta(frame) => {
+ Item::Frame(frame) => {
push(&mut offset, frame.clone());
}
+ Item::Tag(tag) => {
+ let mut frame = Frame::soft(Size::zero());
+ frame.push(Point::zero(), FrameItem::Tag(tag.elem.clone()));
+ frames.push((offset, frame));
+ }
Item::Skip(_) => {}
}
}
diff --git a/crates/typst/src/math/fragment.rs b/crates/typst/src/math/fragment.rs
index 084a1241..ef865b38 100644
--- a/crates/typst/src/math/fragment.rs
+++ b/crates/typst/src/math/fragment.rs
@@ -6,11 +6,11 @@ use ttf_parser::{GlyphId, Rect};
use unicode_math_class::MathClass;
use crate::foundations::StyleChain;
-use crate::introspection::{Meta, MetaElem};
-use crate::layout::{Abs, Corner, Em, Frame, FrameItem, Point, Size};
+use crate::layout::{Abs, Corner, Em, Frame, FrameItem, HideElem, Point, Size};
use crate::math::{
scaled_font_size, EquationElem, Limits, MathContext, MathSize, Scaled,
};
+use crate::model::{Destination, LinkElem};
use crate::syntax::Span;
use crate::text::{Font, Glyph, Lang, Region, TextElem, TextItem};
use crate::visualize::Paint;
@@ -218,7 +218,8 @@ pub struct GlyphFragment {
pub class: MathClass,
pub math_size: MathSize,
pub span: Span,
- pub meta: SmallVec<[Meta; 1]>,
+ pub dests: SmallVec<[Destination; 1]>,
+ pub hidden: bool,
pub limits: Limits,
}
@@ -273,7 +274,8 @@ impl GlyphFragment {
accent_attach: Abs::zero(),
class,
span,
- meta: MetaElem::data_in(styles),
+ dests: LinkElem::dests_in(styles),
+ hidden: HideElem::hidden_in(styles),
};
fragment.set_id(ctx, id);
fragment
@@ -357,7 +359,7 @@ impl GlyphFragment {
let mut frame = Frame::soft(size);
frame.set_baseline(self.ascent);
frame.push(Point::with_y(self.ascent + self.shift), FrameItem::Text(item));
- frame.meta_iter(self.meta);
+ frame.post_process_raw(self.dests, self.hidden);
frame
}
@@ -436,7 +438,7 @@ impl FrameFragment {
pub fn new(ctx: &MathContext, styles: StyleChain, mut frame: Frame) -> Self {
let base_ascent = frame.ascent();
let accent_attach = frame.width() / 2.0;
- frame.meta(styles, false);
+ frame.post_process(styles);
Self {
frame,
font_size: scaled_font_size(ctx, styles),
diff --git a/crates/typst/src/math/mod.rs b/crates/typst/src/math/mod.rs
index c98cac7b..305f9188 100644
--- a/crates/typst/src/math/mod.rs
+++ b/crates/typst/src/math/mod.rs
@@ -41,12 +41,12 @@ use self::row::*;
use self::spacing::*;
use crate::diag::SourceResult;
-use crate::foundations::SequenceElem;
-use crate::foundations::StyledElem;
use crate::foundations::{
- category, Category, Content, Module, Resolve, Scope, StyleChain,
+ category, Category, Content, Module, Resolve, Scope, SequenceElem, StyleChain,
+ StyledElem,
};
-use crate::layout::{BoxElem, HElem, Spacing};
+use crate::introspection::TagElem;
+use crate::layout::{BoxElem, Frame, FrameItem, HElem, Point, Size, Spacing};
use crate::realize::{process, BehavedBuilder};
use crate::text::{LinebreakElem, SpaceElem, TextElem};
@@ -296,6 +296,13 @@ impl LayoutMath for Content {
return Ok(());
}
+ if let Some(tag) = self.to_packed::<TagElem>() {
+ let mut frame = Frame::soft(Size::zero());
+ frame.push(Point::zero(), FrameItem::Tag(tag.elem.clone()));
+ ctx.push(FrameFragment::new(ctx, styles, frame));
+ return Ok(());
+ }
+
if let Some(elem) = self.with::<dyn LayoutMath>() {
return elem.layout_math(ctx, styles);
}
diff --git a/crates/typst/src/math/stretch.rs b/crates/typst/src/math/stretch.rs
index f05e3871..cc71ced6 100644
--- a/crates/typst/src/math/stretch.rs
+++ b/crates/typst/src/math/stretch.rs
@@ -167,7 +167,7 @@ fn assemble(
let mut frame = Frame::soft(size);
let mut offset = Abs::zero();
frame.set_baseline(baseline);
- frame.meta_iter(base.meta);
+ frame.post_process_raw(base.dests, base.hidden);
for (fragment, advance) in selected {
let pos = if horizontal {
diff --git a/crates/typst/src/model/link.rs b/crates/typst/src/model/link.rs
index fb93f060..107b0d9a 100644
--- a/crates/typst/src/model/link.rs
+++ b/crates/typst/src/model/link.rs
@@ -1,4 +1,5 @@
use ecow::{eco_format, EcoString};
+use smallvec::SmallVec;
use crate::diag::{At, SourceResult};
use crate::engine::Engine;
@@ -79,6 +80,11 @@ pub struct LinkElem {
_ => args.expect("body")?,
})]
pub body: Content,
+
+ /// This style is set on the content contained in the `link` element.
+ #[internal]
+ #[ghost]
+ pub dests: SmallVec<[Destination; 1]>,
}
impl LinkElem {
diff --git a/crates/typst/src/realize/mod.rs b/crates/typst/src/realize/mod.rs
index e06059ff..98b5c127 100644
--- a/crates/typst/src/realize/mod.rs
+++ b/crates/typst/src/realize/mod.rs
@@ -23,7 +23,7 @@ use crate::engine::{Engine, Route};
use crate::foundations::{
Content, NativeElement, Packed, SequenceElem, StyleChain, StyledElem, Styles,
};
-use crate::introspection::MetaElem;
+use crate::introspection::TagElem;
use crate::layout::{
AlignElem, BlockElem, BoxElem, ColbreakElem, FlowElem, HElem, LayoutMultiple,
LayoutSingle, PageElem, PagebreakElem, Parity, PlaceElem, VElem,
@@ -389,7 +389,7 @@ impl<'a> FlowBuilder<'a> {
if content.is::<VElem>()
|| content.is::<ColbreakElem>()
- || content.is::<MetaElem>()
+ || content.is::<TagElem>()
|| content.is::<PlaceElem>()
{
self.0.push(content, styles);
@@ -451,7 +451,7 @@ impl<'a> ParBuilder<'a> {
/// content could not be merged, and paragraph building should be
/// interrupted so that the content can be added elsewhere.
fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool {
- if content.is::<MetaElem>() {
+ if content.is::<TagElem>() {
if !self.0.is_empty() {
self.0.push(content, styles);
return true;
@@ -612,7 +612,7 @@ impl<'a> CiteGroupBuilder<'a> {
/// interrupted so that the content can be added elsewhere.
fn accept(&mut self, content: &'a Content, styles: StyleChain<'a>) -> bool {
if !self.items.is_empty()
- && (content.is::<SpaceElem>() || content.is::<MetaElem>())
+ && (content.is::<SpaceElem>() || content.is::<TagElem>())
{
self.staged.push((content, styles));
return true;
diff --git a/crates/typst/src/realize/process.rs b/crates/typst/src/realize/process.rs
index 7aa8b927..6ddb493d 100644
--- a/crates/typst/src/realize/process.rs
+++ b/crates/typst/src/realize/process.rs
@@ -1,7 +1,6 @@
use std::cell::OnceCell;
use comemo::{Track, Tracked};
-use smallvec::smallvec;
use crate::diag::SourceResult;
use crate::engine::Engine;
@@ -9,7 +8,7 @@ use crate::foundations::{
Content, Context, Packed, Recipe, RecipeIndex, Regex, Selector, Show, ShowSet, Style,
StyleChain, Styles, Synthesize, Transformation,
};
-use crate::introspection::{Locatable, Meta, MetaElem};
+use crate::introspection::{Locatable, TagElem};
use crate::text::TextElem;
use crate::utils::{hash128, BitSet};
@@ -57,9 +56,9 @@ pub fn process(
// If the element isn't yet prepared (we're seeing it for the first time),
// prepare it.
- let mut meta = None;
+ let mut tag = None;
if !prepared {
- meta = prepare(engine, &mut target, &mut map, styles)?;
+ tag = prepare(engine, &mut target, &mut map, styles)?;
}
// Apply a step, if there is one.
@@ -76,9 +75,9 @@ pub fn process(
None => target,
};
- // If necessary, apply metadata generated in the preparation.
- if let Some(meta) = meta {
- output += meta.pack();
+ // If necessary, add the tag generated in the preparation.
+ if let Some(tag) = tag {
+ output = tag + output;
}
Ok(Some(output.styled_with_map(map)))
@@ -194,7 +193,7 @@ fn prepare(
target: &mut Content,
map: &mut Styles,
styles: StyleChain,
-) -> SourceResult<Option<Packed<MetaElem>>> {
+) -> SourceResult<Option<Content>> {
// Generate a location for the element, which uniquely identifies it in
// the document. This has some overhead, so we only do it for elements
// that are explicitly marked as locatable and labelled elements.
@@ -225,24 +224,18 @@ fn prepare(
// available in rules.
target.materialize(styles.chain(map));
- if located {
- // Apply metadata to be able to find the element in the frames. Do this
- // after synthesis and materialization, so that it includes the
- // synthesized fields. Do it before marking as prepared so that show-set
- // rules will apply to this element when queried. This adds a style to
- // the whole element's subtree identifying it as belonging to the
- // element.
- map.set(MetaElem::set_data(smallvec![Meta::Elem(target.clone())]));
- }
+ // If the element is locatable, create a tag element to be able to find the
+ // element in the frames after layout. Do this after synthesis and
+ // materialization, so that it includes the synthesized fields. Do it before
+ // marking as prepared so that show-set rules will apply to this element
+ // when queried.
+ let tag = located.then(|| TagElem::packed(target.clone()));
// Ensure that this preparation only runs once by marking the element as
// prepared.
target.mark_prepared();
- // If the element is located, return an extra meta elem that will be
- // attached so that the metadata styles are not lost in case the element's
- // show rule results in nothing.
- Ok(located.then(|| Packed::new(MetaElem::new()).spanned(target.span())))
+ Ok(tag)
}
/// Apply a step.