typst_library/foundations/content/
mod.rs

1mod element;
2mod field;
3mod packed;
4mod raw;
5mod vtable;
6
7pub use self::element::*;
8pub use self::field::*;
9pub use self::packed::Packed;
10pub use self::vtable::{ContentVtable, FieldVtable};
11#[doc(inline)]
12pub use typst_macros::elem;
13
14use std::fmt::{self, Debug, Formatter};
15use std::hash::Hash;
16use std::iter::{self, Sum};
17use std::ops::{Add, AddAssign, ControlFlow};
18
19use comemo::Tracked;
20use ecow::{EcoString, eco_format};
21use serde::{Serialize, Serializer};
22
23use typst_syntax::Span;
24use typst_utils::singleton;
25
26use crate::diag::{SourceResult, StrResult};
27use crate::engine::Engine;
28use crate::foundations::{
29    Context, Dict, IntoValue, Label, Property, Recipe, RecipeIndex, Repr, Selector, Str,
30    Style, StyleChain, Styles, Value, func, repr, scope, ty,
31};
32use crate::introspection::Location;
33use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides};
34use crate::model::{Destination, EmphElem, LinkElem, LinkMarker, StrongElem};
35use crate::pdf::{ArtifactElem, ArtifactKind};
36use crate::text::UnderlineElem;
37
38/// A piece of document content.
39///
40/// This type is at the heart of Typst. All markup you write and most
41/// [functions]($function) you call produce content values. You can create a
42/// content value by enclosing markup in square brackets. This is also how you
43/// pass content to functions.
44///
45/// # Example
46/// ```example
47/// Type of *Hello!* is
48/// #type([*Hello!*])
49/// ```
50///
51/// Content can be added with the `+` operator,
52/// [joined together]($scripting/#blocks) and multiplied with integers. Wherever
53/// content is expected, you can also pass a [string]($str) or `{none}`.
54///
55/// # Representation
56/// Content consists of elements with fields. When constructing an element with
57/// its _element function,_ you provide these fields as arguments and when you
58/// have a content value, you can access its fields with [field access
59/// syntax]($scripting/#field-access).
60///
61/// Some fields are required: These must be provided when constructing an
62/// element and as a consequence, they are always available through field access
63/// on content of that type. Required fields are marked as such in the
64/// documentation.
65///
66/// Most fields are optional: Like required fields, they can be passed to the
67/// element function to configure them for a single element. However, these can
68/// also be configured with [set rules]($styling/#set-rules) to apply them to
69/// all elements within a scope. Optional fields are only available with field
70/// access syntax when they were explicitly passed to the element function, not
71/// when they result from a set rule.
72///
73/// Each element has a default appearance. However, you can also completely
74/// customize its appearance with a [show rule]($styling/#show-rules). The show
75/// rule is passed the element. It can access the element's field and produce
76/// arbitrary content from it.
77///
78/// In the web app, you can hover over a content variable to see exactly which
79/// elements the content is composed of and what fields they have.
80/// Alternatively, you can inspect the output of the [`repr`] function.
81#[ty(scope, cast)]
82#[derive(Clone, PartialEq, Hash)]
83#[repr(transparent)]
84pub struct Content(raw::RawContent);
85
86impl Content {
87    /// Creates a new content from an element.
88    pub fn new<T: NativeElement>(elem: T) -> Self {
89        Self(raw::RawContent::new(elem))
90    }
91
92    /// Creates a empty sequence content.
93    pub fn empty() -> Self {
94        singleton!(Content, SequenceElem::default().pack()).clone()
95    }
96
97    /// Get the element of this content.
98    pub fn elem(&self) -> Element {
99        self.0.elem()
100    }
101
102    /// Get the span of the content.
103    pub fn span(&self) -> Span {
104        self.0.span()
105    }
106
107    /// Set the span of the content.
108    pub fn spanned(mut self, span: Span) -> Self {
109        if self.0.span().is_detached() {
110            *self.0.span_mut() = span;
111        }
112        self
113    }
114
115    /// Get the label of the content.
116    pub fn label(&self) -> Option<Label> {
117        self.0.meta().label
118    }
119
120    /// Attach a label to the content.
121    pub fn labelled(mut self, label: Label) -> Self {
122        self.set_label(label);
123        self
124    }
125
126    /// Set the label of the content.
127    pub fn set_label(&mut self, label: Label) {
128        self.0.meta_mut().label = Some(label);
129    }
130
131    /// Assigns a location to the content.
132    ///
133    /// This identifies the content and e.g. makes it linkable by
134    /// `.linked(Destination::Location(loc))`.
135    ///
136    /// Useful in combination with [`Location::variant`].
137    pub fn located(mut self, loc: Location) -> Self {
138        self.set_location(loc);
139        self
140    }
141
142    /// Set the location of the content.
143    pub fn set_location(&mut self, location: Location) {
144        self.0.meta_mut().location = Some(location);
145    }
146
147    /// Check whether a show rule recipe is disabled.
148    pub fn is_guarded(&self, index: RecipeIndex) -> bool {
149        self.0.meta().lifecycle.contains(index.0)
150    }
151
152    /// Disable a show rule recipe.
153    pub fn guarded(mut self, index: RecipeIndex) -> Self {
154        self.0.meta_mut().lifecycle.insert(index.0);
155        self
156    }
157
158    /// Whether this content has already been prepared.
159    pub fn is_prepared(&self) -> bool {
160        self.0.meta().lifecycle.contains(0)
161    }
162
163    /// Mark this content as prepared.
164    pub fn mark_prepared(&mut self) {
165        self.0.meta_mut().lifecycle.insert(0);
166    }
167
168    /// Get a field by ID.
169    ///
170    /// This is the preferred way to access fields. However, you can only use it
171    /// if you have set the field IDs yourself or are using the field IDs
172    /// generated by the `#[elem]` macro.
173    pub fn get(
174        &self,
175        id: u8,
176        styles: Option<StyleChain>,
177    ) -> Result<Value, FieldAccessError> {
178        if id == 255
179            && let Some(label) = self.label()
180        {
181            return Ok(label.into_value());
182        }
183
184        match self.0.handle().field(id) {
185            Some(handle) => match styles {
186                Some(styles) => handle.get_with_styles(styles),
187                None => handle.get(),
188            }
189            .ok_or(FieldAccessError::Unset),
190            None => Err(FieldAccessError::Unknown),
191        }
192    }
193
194    /// Get a field by name.
195    ///
196    /// If you have access to the field IDs of the element, use [`Self::get`]
197    /// instead.
198    pub fn get_by_name(&self, name: &str) -> Result<Value, FieldAccessError> {
199        if name == "label" {
200            return self
201                .label()
202                .map(|label| label.into_value())
203                .ok_or(FieldAccessError::Unknown);
204        }
205
206        match self.elem().field_id(name).and_then(|id| self.0.handle().field(id)) {
207            Some(handle) => handle.get().ok_or(FieldAccessError::Unset),
208            None => Err(FieldAccessError::Unknown),
209        }
210    }
211
212    /// Get a field by ID, returning a missing field error if it does not exist.
213    ///
214    /// This is the preferred way to access fields. However, you can only use it
215    /// if you have set the field IDs yourself or are using the field IDs
216    /// generated by the `#[elem]` macro.
217    pub fn field(&self, id: u8) -> StrResult<Value> {
218        self.get(id, None)
219            .map_err(|e| e.message(self, self.elem().field_name(id).unwrap()))
220    }
221
222    /// Get a field by name, returning a missing field error if it does not
223    /// exist.
224    ///
225    /// If you have access to the field IDs of the element, use [`Self::field`]
226    /// instead.
227    pub fn field_by_name(&self, name: &str) -> StrResult<Value> {
228        self.get_by_name(name).map_err(|e| e.message(self, name))
229    }
230
231    /// Resolve all fields with the styles and save them in-place.
232    pub fn materialize(&mut self, styles: StyleChain) {
233        for id in 0..self.elem().vtable().fields.len() as u8 {
234            self.0.handle_mut().field(id).unwrap().materialize(styles);
235        }
236    }
237
238    /// Create a new sequence element from multiples elements.
239    pub fn sequence(iter: impl IntoIterator<Item = Self>) -> Self {
240        let vec: Vec<_> = iter.into_iter().collect();
241        if vec.is_empty() {
242            Self::empty()
243        } else if vec.len() == 1 {
244            vec.into_iter().next().unwrap()
245        } else {
246            SequenceElem::new(vec).into()
247        }
248    }
249
250    /// Whether the contained element is of type `T`.
251    pub fn is<T: NativeElement>(&self) -> bool {
252        self.0.is::<T>()
253    }
254
255    /// Downcasts the element to a packed value.
256    pub fn to_packed<T: NativeElement>(&self) -> Option<&Packed<T>> {
257        Packed::from_ref(self)
258    }
259
260    /// Downcasts the element to a mutable packed value.
261    pub fn to_packed_mut<T: NativeElement>(&mut self) -> Option<&mut Packed<T>> {
262        Packed::from_mut(self)
263    }
264
265    /// Downcasts the element into an owned packed value.
266    pub fn into_packed<T: NativeElement>(self) -> Result<Packed<T>, Self> {
267        Packed::from_owned(self)
268    }
269
270    /// Extract the raw underlying element.
271    pub fn unpack<T: NativeElement>(self) -> Result<T, Self> {
272        self.into_packed::<T>().map(Packed::unpack)
273    }
274
275    /// Whether the contained element has the given capability.
276    pub fn can<C>(&self) -> bool
277    where
278        C: ?Sized + 'static,
279    {
280        self.elem().can::<C>()
281    }
282
283    /// Cast to a trait object if the contained element has the given
284    /// capability.
285    pub fn with<C>(&self) -> Option<&C>
286    where
287        C: ?Sized + 'static,
288    {
289        self.0.with::<C>()
290    }
291
292    /// Cast to a mutable trait object if the contained element has the given
293    /// capability.
294    pub fn with_mut<C>(&mut self) -> Option<&mut C>
295    where
296        C: ?Sized + 'static,
297    {
298        self.0.with_mut::<C>()
299    }
300
301    /// Whether the content is an empty sequence.
302    pub fn is_empty(&self) -> bool {
303        let Some(sequence) = self.to_packed::<SequenceElem>() else {
304            return false;
305        };
306
307        sequence.children.is_empty()
308    }
309
310    /// Also auto expands sequence of sequences into flat sequence
311    pub fn sequence_recursive_for_each<'a>(&'a self, f: &mut impl FnMut(&'a Self)) {
312        if let Some(sequence) = self.to_packed::<SequenceElem>() {
313            for child in &sequence.children {
314                child.sequence_recursive_for_each(f);
315            }
316        } else {
317            f(self);
318        }
319    }
320
321    /// Style this content with a recipe, eagerly applying it if possible.
322    pub fn styled_with_recipe(
323        self,
324        engine: &mut Engine,
325        context: Tracked<Context>,
326        recipe: Recipe,
327    ) -> SourceResult<Self> {
328        if recipe.selector().is_none() {
329            recipe.apply(engine, context, self)
330        } else {
331            Ok(self.styled(recipe))
332        }
333    }
334
335    /// Repeat this content `count` times.
336    pub fn repeat(&self, count: usize) -> Self {
337        Self::sequence(std::iter::repeat_with(|| self.clone()).take(count))
338    }
339
340    /// Sets a style property on the content.
341    pub fn set<E, const I: u8>(self, field: Field<E, I>, value: E::Type) -> Self
342    where
343        E: SettableProperty<I>,
344        E::Type: Debug + Clone + Hash + Send + Sync + 'static,
345    {
346        self.styled(Property::new(field, value))
347    }
348
349    /// Style this content with a style entry.
350    pub fn styled(mut self, style: impl Into<Style>) -> Self {
351        if let Some(style_elem) = self.to_packed_mut::<StyledElem>() {
352            style_elem.styles.apply_one(style.into());
353            self
354        } else {
355            self.styled_with_map(style.into().into())
356        }
357    }
358
359    /// Style this content with a full style map.
360    pub fn styled_with_map(mut self, styles: Styles) -> Self {
361        if styles.is_empty() {
362            return self;
363        }
364
365        if let Some(style_elem) = self.to_packed_mut::<StyledElem>() {
366            style_elem.styles.apply(styles);
367            self
368        } else {
369            StyledElem::new(self, styles).into()
370        }
371    }
372
373    /// Style this content with a full style map in-place.
374    pub fn style_in_place(&mut self, styles: Styles) {
375        if styles.is_empty() {
376            return;
377        }
378
379        if let Some(style_elem) = self.to_packed_mut::<StyledElem>() {
380            style_elem.styles.apply(styles);
381        } else {
382            *self = StyledElem::new(std::mem::take(self), styles).into();
383        }
384    }
385
386    /// Queries the content tree for the first element that match the given
387    /// selector.
388    ///
389    /// This is a *naive hack* because contextual content and elements produced
390    /// in `show` rules will not be included in the results. It's used should
391    /// be avoided.
392    pub fn query_first_naive(&self, selector: &Selector) -> Option<Content> {
393        self.traverse(&mut |element| -> ControlFlow<Content> {
394            if selector.matches(&element, None) {
395                ControlFlow::Break(element)
396            } else {
397                ControlFlow::Continue(())
398            }
399        })
400        .break_value()
401    }
402
403    /// Extracts the plain text of this content.
404    pub fn plain_text(&self) -> EcoString {
405        let mut text = EcoString::new();
406        let _ = self.traverse(&mut |element| -> ControlFlow<()> {
407            if let Some(textable) = element.with::<dyn PlainText>() {
408                textable.plain_text(&mut text);
409            }
410            ControlFlow::Continue(())
411        });
412        text
413    }
414
415    /// Traverse this content.
416    pub fn traverse<F, B>(&self, f: &mut F) -> ControlFlow<B>
417    where
418        F: FnMut(Content) -> ControlFlow<B>,
419    {
420        /// Walks a given value to find any content that matches the selector.
421        ///
422        /// Returns early if the function gives `ControlFlow::Break`.
423        fn walk_value<F, B>(value: Value, f: &mut F) -> ControlFlow<B>
424        where
425            F: FnMut(Content) -> ControlFlow<B>,
426        {
427            match value {
428                Value::Content(content) => content.traverse(f),
429                Value::Array(array) => {
430                    for value in array {
431                        walk_value(value, f)?;
432                    }
433                    ControlFlow::Continue(())
434                }
435                _ => ControlFlow::Continue(()),
436            }
437        }
438
439        // Call f on the element itself before recursively iterating its fields.
440        f(self.clone())?;
441        for (_, value) in self.fields() {
442            walk_value(value, f)?;
443        }
444        ControlFlow::Continue(())
445    }
446}
447
448impl Content {
449    /// Strongly emphasize this content.
450    pub fn strong(self) -> Self {
451        let span = self.span();
452        StrongElem::new(self).pack().spanned(span)
453    }
454
455    /// Emphasize this content.
456    pub fn emph(self) -> Self {
457        let span = self.span();
458        EmphElem::new(self).pack().spanned(span)
459    }
460
461    /// Underline this content.
462    pub fn underlined(self) -> Self {
463        let span = self.span();
464        UnderlineElem::new(self).pack().spanned(span)
465    }
466
467    /// Link the content somewhere.
468    pub fn linked(self, dest: Destination, alt: Option<EcoString>) -> Self {
469        let span = self.span();
470        LinkMarker::new(self, alt)
471            .pack()
472            .spanned(span)
473            .set(LinkElem::current, Some(dest))
474    }
475
476    /// Set alignments for this content.
477    pub fn aligned(self, align: Alignment) -> Self {
478        self.set(AlignElem::alignment, align)
479    }
480
481    /// Pad this content at the sides.
482    pub fn padded(self, padding: Sides<Rel<Length>>) -> Self {
483        let span = self.span();
484        PadElem::new(self)
485            .with_left(padding.left)
486            .with_top(padding.top)
487            .with_right(padding.right)
488            .with_bottom(padding.bottom)
489            .pack()
490            .spanned(span)
491    }
492
493    /// Transform this content's contents without affecting layout.
494    pub fn moved(self, delta: Axes<Rel<Length>>) -> Self {
495        let span = self.span();
496        MoveElem::new(self)
497            .with_dx(delta.x)
498            .with_dy(delta.y)
499            .pack()
500            .spanned(span)
501    }
502
503    /// Mark content as a PDF artifact.
504    pub fn artifact(self, kind: ArtifactKind) -> Self {
505        let span = self.span();
506        ArtifactElem::new(self).with_kind(kind).pack().spanned(span)
507    }
508}
509
510#[scope]
511impl Content {
512    /// The content's element function. This function can be used to create the element
513    /// contained in this content. It can be used in set and show rules for the
514    /// element. Can be compared with global functions to check whether you have
515    /// a specific
516    /// kind of element.
517    #[func]
518    pub fn func(&self) -> Element {
519        self.elem()
520    }
521
522    /// Whether the content has the specified field.
523    #[func]
524    pub fn has(
525        &self,
526        /// The field to look for.
527        field: Str,
528    ) -> bool {
529        if field.as_str() == "label" {
530            return self.label().is_some();
531        }
532
533        let Some(id) = self.elem().field_id(&field) else {
534            return false;
535        };
536
537        match self.0.handle().field(id) {
538            Some(field) => field.has(),
539            None => false,
540        }
541    }
542
543    /// Access the specified field on the content. Returns the default value if
544    /// the field does not exist or fails with an error if no default value was
545    /// specified.
546    #[func]
547    pub fn at(
548        &self,
549        /// The field to access.
550        field: Str,
551        /// A default value to return if the field does not exist.
552        #[named]
553        default: Option<Value>,
554    ) -> StrResult<Value> {
555        self.get_by_name(&field)
556            .or_else(|e| default.ok_or(e))
557            .map_err(|e| e.message_no_default(self, &field))
558    }
559
560    /// Returns the fields of this content.
561    ///
562    /// ```example
563    /// #rect(
564    ///   width: 10cm,
565    ///   height: 10cm,
566    /// ).fields()
567    /// ```
568    #[func]
569    pub fn fields(&self) -> Dict {
570        let mut dict = Dict::new();
571        for field in self.0.handle().fields() {
572            if let Some(value) = field.get() {
573                dict.insert(field.name.into(), value);
574            }
575        }
576        if let Some(label) = self.label() {
577            dict.insert("label".into(), label.into_value());
578        }
579        dict
580    }
581
582    /// The location of the content. This is only available on content returned
583    /// by [query] or provided by a [show rule]($reference/styling/#show-rules),
584    /// for other content it will be `{none}`. The resulting location can be
585    /// used with [counters]($counter), [state] and [queries]($query).
586    #[func]
587    pub fn location(&self) -> Option<Location> {
588        self.0.meta().location
589    }
590}
591
592impl Default for Content {
593    fn default() -> Self {
594        Self::empty()
595    }
596}
597
598impl Debug for Content {
599    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
600        self.0.fmt(f)
601    }
602}
603
604impl<T: NativeElement> From<T> for Content {
605    fn from(value: T) -> Self {
606        Self::new(value)
607    }
608}
609
610impl Repr for Content {
611    fn repr(&self) -> EcoString {
612        self.0.handle().repr().unwrap_or_else(|| {
613            let fields = self
614                .0
615                .handle()
616                .fields()
617                .filter_map(|field| field.get().map(|v| (field.name, v.repr())))
618                .map(|(name, value)| eco_format!("{name}: {value}"))
619                .collect::<Vec<_>>();
620            eco_format!(
621                "{}{}",
622                self.elem().name(),
623                repr::pretty_array_like(&fields, false),
624            )
625        })
626    }
627}
628
629impl Add for Content {
630    type Output = Self;
631
632    fn add(self, mut rhs: Self) -> Self::Output {
633        let mut lhs = self;
634        match (lhs.to_packed_mut::<SequenceElem>(), rhs.to_packed_mut::<SequenceElem>()) {
635            (Some(seq_lhs), Some(rhs)) => {
636                seq_lhs.children.extend(rhs.children.iter().cloned());
637                lhs
638            }
639            (Some(seq_lhs), None) => {
640                seq_lhs.children.push(rhs);
641                lhs
642            }
643            (None, Some(rhs_seq)) => {
644                rhs_seq.children.insert(0, lhs);
645                rhs
646            }
647            (None, None) => Self::sequence([lhs, rhs]),
648        }
649    }
650}
651
652impl<'a> Add<&'a Self> for Content {
653    type Output = Self;
654
655    fn add(self, rhs: &'a Self) -> Self::Output {
656        let mut lhs = self;
657        match (lhs.to_packed_mut::<SequenceElem>(), rhs.to_packed::<SequenceElem>()) {
658            (Some(seq_lhs), Some(rhs)) => {
659                seq_lhs.children.extend(rhs.children.iter().cloned());
660                lhs
661            }
662            (Some(seq_lhs), None) => {
663                seq_lhs.children.push(rhs.clone());
664                lhs
665            }
666            (None, Some(_)) => {
667                let mut rhs = rhs.clone();
668                rhs.to_packed_mut::<SequenceElem>().unwrap().children.insert(0, lhs);
669                rhs
670            }
671            (None, None) => Self::sequence([lhs, rhs.clone()]),
672        }
673    }
674}
675
676impl AddAssign for Content {
677    fn add_assign(&mut self, rhs: Self) {
678        *self = std::mem::take(self) + rhs;
679    }
680}
681
682impl AddAssign<&Self> for Content {
683    fn add_assign(&mut self, rhs: &Self) {
684        *self = std::mem::take(self) + rhs;
685    }
686}
687
688impl Sum for Content {
689    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
690        Self::sequence(iter)
691    }
692}
693
694impl Serialize for Content {
695    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
696    where
697        S: Serializer,
698    {
699        serializer.collect_map(
700            iter::once(("func".into(), self.func().name().into_value()))
701                .chain(self.fields()),
702        )
703    }
704}
705
706/// A sequence of content.
707#[elem(Debug, Repr)]
708pub struct SequenceElem {
709    /// The elements.
710    #[required]
711    pub children: Vec<Content>,
712}
713
714impl Debug for SequenceElem {
715    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
716        write!(f, "Sequence ")?;
717        f.debug_list().entries(&self.children).finish()
718    }
719}
720
721// Derive is currently incompatible with `elem` macro.
722#[allow(clippy::derivable_impls)]
723impl Default for SequenceElem {
724    fn default() -> Self {
725        Self { children: Default::default() }
726    }
727}
728
729impl Repr for SequenceElem {
730    fn repr(&self) -> EcoString {
731        if self.children.is_empty() {
732            "[]".into()
733        } else {
734            let elements = crate::foundations::repr::pretty_array_like(
735                &self.children.iter().map(|c| c.repr()).collect::<Vec<_>>(),
736                false,
737            );
738            eco_format!("sequence{}", elements)
739        }
740    }
741}
742
743/// Content alongside styles.
744#[elem(Debug, Repr, PartialEq)]
745pub struct StyledElem {
746    /// The content.
747    #[required]
748    pub child: Content,
749    /// The styles.
750    #[required]
751    pub styles: Styles,
752}
753
754impl Debug for StyledElem {
755    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
756        for style in self.styles.iter() {
757            writeln!(f, "#{style:?}")?;
758        }
759        self.child.fmt(f)
760    }
761}
762
763impl PartialEq for StyledElem {
764    fn eq(&self, other: &Self) -> bool {
765        self.child == other.child
766    }
767}
768
769impl Repr for StyledElem {
770    fn repr(&self) -> EcoString {
771        eco_format!("styled(child: {}, ..)", self.child.repr())
772    }
773}
774
775impl<T: NativeElement> IntoValue for T {
776    fn into_value(self) -> Value {
777        Value::Content(self.pack())
778    }
779}