summaryrefslogtreecommitdiff
path: root/library/src
diff options
context:
space:
mode:
Diffstat (limited to 'library/src')
-rw-r--r--library/src/basics/table.rs4
-rw-r--r--library/src/layout/columns.rs15
-rw-r--r--library/src/layout/container.rs14
-rw-r--r--library/src/layout/grid.rs27
-rw-r--r--library/src/layout/hide.rs7
-rw-r--r--library/src/layout/pad.rs7
-rw-r--r--library/src/layout/page.rs14
-rw-r--r--library/src/layout/par.rs2
-rw-r--r--library/src/layout/place.rs7
-rw-r--r--library/src/layout/repeat.rs7
-rw-r--r--library/src/layout/spacing.rs34
-rw-r--r--library/src/layout/stack.rs20
-rw-r--r--library/src/layout/transform.rs22
-rw-r--r--library/src/math/matrix.rs18
-rw-r--r--library/src/meta/link.rs6
-rw-r--r--library/src/text/misc.rs122
-rw-r--r--library/src/text/mod.rs421
-rw-r--r--library/src/text/quotes.rs42
-rw-r--r--library/src/text/raw.rs88
-rw-r--r--library/src/text/symbol.rs42
-rw-r--r--library/src/visualize/shape.rs10
21 files changed, 876 insertions, 53 deletions
diff --git a/library/src/basics/table.rs b/library/src/basics/table.rs
index dbb3d58e..ed8df104 100644
--- a/library/src/basics/table.rs
+++ b/library/src/basics/table.rs
@@ -67,6 +67,10 @@ impl TableNode {
fn field(&self, name: &str) -> Option<Value> {
match name {
+ "columns" => Some(TrackSizing::encode_slice(&self.tracks.x)),
+ "rows" => Some(TrackSizing::encode_slice(&self.tracks.y)),
+ "column-gutter" => Some(TrackSizing::encode_slice(&self.gutter.x)),
+ "row-gutter" => Some(TrackSizing::encode_slice(&self.gutter.y)),
"cells" => Some(Value::Array(
self.cells.iter().cloned().map(Value::Content).collect(),
)),
diff --git a/library/src/layout/columns.rs b/library/src/layout/columns.rs
index c03ff433..a5f67b94 100644
--- a/library/src/layout/columns.rs
+++ b/library/src/layout/columns.rs
@@ -65,6 +65,14 @@ impl ColumnsNode {
}
.pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "count" => Some(Value::Int(self.count.get() as i64)),
+ "body" => Some(Value::Content(self.body.clone())),
+ _ => None,
+ }
+ }
}
impl Layout for ColumnsNode {
@@ -186,6 +194,13 @@ impl ColbreakNode {
let weak = args.named("weak")?.unwrap_or(false);
Ok(Self { weak }.pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "weak" => Some(Value::Bool(self.weak)),
+ _ => None,
+ }
+ }
}
impl Behave for ColbreakNode {
diff --git a/library/src/layout/container.rs b/library/src/layout/container.rs
index 62e129b4..112c6f03 100644
--- a/library/src/layout/container.rs
+++ b/library/src/layout/container.rs
@@ -50,6 +50,13 @@ impl BoxNode {
let body = args.eat::<Content>()?.unwrap_or_default();
Ok(Self { sizing: Axes::new(width, height), body }.pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "body" => Some(Value::Content(self.body.clone())),
+ _ => None,
+ }
+ }
}
impl Layout for BoxNode {
@@ -163,6 +170,13 @@ impl BlockNode {
args.named("below")?.map(VNode::block_around).or(spacing),
);
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "body" => Some(Value::Content(self.0.clone())),
+ _ => None,
+ }
+ }
}
impl Layout for BlockNode {
diff --git a/library/src/layout/grid.rs b/library/src/layout/grid.rs
index 85d464b1..eafe644f 100644
--- a/library/src/layout/grid.rs
+++ b/library/src/layout/grid.rs
@@ -120,6 +120,19 @@ impl GridNode {
}
.pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "columns" => Some(TrackSizing::encode_slice(&self.tracks.x)),
+ "rows" => Some(TrackSizing::encode_slice(&self.tracks.y)),
+ "column-gutter" => Some(TrackSizing::encode_slice(&self.gutter.x)),
+ "row-gutter" => Some(TrackSizing::encode_slice(&self.gutter.y)),
+ "cells" => Some(Value::Array(
+ self.cells.iter().cloned().map(Value::Content).collect(),
+ )),
+ _ => None,
+ }
+ }
}
impl Layout for GridNode {
@@ -157,6 +170,20 @@ pub enum TrackSizing {
Fractional(Fr),
}
+impl TrackSizing {
+ pub fn encode(self) -> Value {
+ match self {
+ Self::Auto => Value::Auto,
+ Self::Relative(rel) => Spacing::Relative(rel).encode(),
+ Self::Fractional(fr) => Spacing::Fractional(fr).encode(),
+ }
+ }
+
+ pub fn encode_slice(vec: &[TrackSizing]) -> Value {
+ Value::Array(vec.iter().copied().map(Self::encode).collect())
+ }
+}
+
impl From<Spacing> for TrackSizing {
fn from(spacing: Spacing) -> Self {
match spacing {
diff --git a/library/src/layout/hide.rs b/library/src/layout/hide.rs
index dec6dd1c..6d168d35 100644
--- a/library/src/layout/hide.rs
+++ b/library/src/layout/hide.rs
@@ -30,6 +30,13 @@ impl HideNode {
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.expect("body")?).pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "body" => Some(Value::Content(self.0.clone())),
+ _ => None,
+ }
+ }
}
impl Layout for HideNode {
diff --git a/library/src/layout/pad.rs b/library/src/layout/pad.rs
index 13b573bf..bbd55225 100644
--- a/library/src/layout/pad.rs
+++ b/library/src/layout/pad.rs
@@ -67,6 +67,13 @@ impl PadNode {
let padding = Sides::new(left, top, right, bottom);
Ok(Self { padding, body }.pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "body" => Some(Value::Content(self.body.clone())),
+ _ => None,
+ }
+ }
}
impl Layout for PadNode {
diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs
index 105638a7..8e146126 100644
--- a/library/src/layout/page.rs
+++ b/library/src/layout/page.rs
@@ -64,6 +64,13 @@ impl PageNode {
styles.set(Self::HEIGHT, Smart::Custom(paper.height().into()));
}
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "body" => Some(Value::Content(self.0.clone())),
+ _ => None,
+ }
+ }
}
impl PageNode {
@@ -190,6 +197,13 @@ impl PagebreakNode {
let weak = args.named("weak")?.unwrap_or(false);
Ok(Self { weak }.pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "weak" => Some(Value::Bool(self.weak)),
+ _ => None,
+ }
+ }
}
/// A header, footer, foreground or background definition.
diff --git a/library/src/layout/par.rs b/library/src/layout/par.rs
index f966d30b..412a279e 100644
--- a/library/src/layout/par.rs
+++ b/library/src/layout/par.rs
@@ -467,7 +467,7 @@ fn collect<'a>(
Segment::Text(c.len_utf8())
} else if let Some(node) = child.to::<SmartQuoteNode>() {
let prev = full.len();
- if styles.get(TextNode::SMART_QUOTES) {
+ if styles.get(SmartQuoteNode::ENABLED) {
let lang = styles.get(TextNode::LANG);
let region = styles.get(TextNode::REGION);
let quotes = Quotes::from_lang(lang, region);
diff --git a/library/src/layout/place.rs b/library/src/layout/place.rs
index ed3d71bf..406aa862 100644
--- a/library/src/layout/place.rs
+++ b/library/src/layout/place.rs
@@ -33,6 +33,13 @@ impl PlaceNode {
let out_of_flow = aligns.y.is_some();
Ok(Self(body.moved(Axes::new(dx, dy)).aligned(aligns), out_of_flow).pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "body" => Some(Value::Content(self.0.clone())),
+ _ => None,
+ }
+ }
}
impl Layout for PlaceNode {
diff --git a/library/src/layout/repeat.rs b/library/src/layout/repeat.rs
index 864454c4..04610fca 100644
--- a/library/src/layout/repeat.rs
+++ b/library/src/layout/repeat.rs
@@ -19,6 +19,13 @@ impl RepeatNode {
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.expect("body")?).pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "body" => Some(Value::Content(self.0.clone())),
+ _ => None,
+ }
+ }
}
impl Layout for RepeatNode {
diff --git a/library/src/layout/spacing.rs b/library/src/layout/spacing.rs
index 91b36661..35f6aa9b 100644
--- a/library/src/layout/spacing.rs
+++ b/library/src/layout/spacing.rs
@@ -54,10 +54,18 @@ pub struct HNode {
#[node]
impl HNode {
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let amount = args.expect("spacing")?;
+ let amount = args.expect("amount")?;
let weak = args.named("weak")?.unwrap_or(false);
Ok(Self { amount, weak }.pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "amount" => Some(self.amount.encode()),
+ "weak" => Some(Value::Bool(self.weak)),
+ _ => None,
+ }
+ }
}
impl HNode {
@@ -159,6 +167,14 @@ impl VNode {
};
Ok(node.pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "amount" => Some(self.amount.encode()),
+ "weak" => Some(Value::Bool(self.weakness != 0)),
+ _ => None,
+ }
+ }
}
impl VNode {
@@ -220,6 +236,22 @@ impl Spacing {
pub fn is_fractional(self) -> bool {
matches!(self, Self::Fractional(_))
}
+
+ /// Encode into a value.
+ pub fn encode(self) -> Value {
+ match self {
+ Self::Relative(rel) => {
+ if rel.rel.is_zero() {
+ Value::Length(rel.abs)
+ } else if rel.abs.is_zero() {
+ Value::Ratio(rel.rel)
+ } else {
+ Value::Relative(rel)
+ }
+ }
+ Self::Fractional(fr) => Value::Fraction(fr),
+ }
+ }
}
impl From<Abs> for Spacing {
diff --git a/library/src/layout/stack.rs b/library/src/layout/stack.rs
index f83f4e41..c423b1a3 100644
--- a/library/src/layout/stack.rs
+++ b/library/src/layout/stack.rs
@@ -40,6 +40,26 @@ impl StackNode {
}
.pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "dir" => Some(Value::dynamic(self.dir)),
+ "spacing" => Some(match self.spacing {
+ Some(spacing) => spacing.encode(),
+ None => Value::None,
+ }),
+ "items" => Some(Value::Array(
+ self.children
+ .iter()
+ .map(|child| match child {
+ StackChild::Spacing(spacing) => spacing.encode(),
+ StackChild::Block(content) => Value::Content(content.clone()),
+ })
+ .collect(),
+ )),
+ _ => None,
+ }
+ }
}
impl Layout for StackNode {
diff --git a/library/src/layout/transform.rs b/library/src/layout/transform.rs
index 92a31780..57a6c069 100644
--- a/library/src/layout/transform.rs
+++ b/library/src/layout/transform.rs
@@ -31,6 +31,7 @@ use crate::prelude::*;
/// ### Example
/// ```
/// Hello, world!#move(dy: -2pt)[!]#move(dy: 2pt)[!]
+/// ```
///
/// - dx: Rel<Length> (named)
/// The horizontal displacement of the content.
@@ -61,6 +62,13 @@ impl MoveNode {
}
.pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "body" => Some(Value::Content(self.body.clone())),
+ _ => None,
+ }
+ }
}
impl Layout for MoveNode {
@@ -130,6 +138,13 @@ impl RotateNode {
}
.pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "body" => Some(Value::Content(self.body.clone())),
+ _ => None,
+ }
+ }
}
impl Layout for RotateNode {
@@ -209,6 +224,13 @@ impl ScaleNode {
}
.pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "body" => Some(Value::Content(self.body.clone())),
+ _ => None,
+ }
+ }
}
impl Layout for ScaleNode {
diff --git a/library/src/math/matrix.rs b/library/src/math/matrix.rs
index 92199774..49527354 100644
--- a/library/src/math/matrix.rs
+++ b/library/src/math/matrix.rs
@@ -22,6 +22,15 @@ impl VecNode {
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.all()?).pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "elements" => {
+ Some(Value::Array(self.0.iter().cloned().map(Value::Content).collect()))
+ }
+ _ => None,
+ }
+ }
}
impl Texify for VecNode {
@@ -89,6 +98,15 @@ impl CasesNode {
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.all()?).pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "branches" => {
+ Some(Value::Array(self.0.iter().cloned().map(Value::Content).collect()))
+ }
+ _ => None,
+ }
+ }
}
impl Texify for CasesNode {
diff --git a/library/src/meta/link.rs b/library/src/meta/link.rs
index 501ccddb..60d57a5f 100644
--- a/library/src/meta/link.rs
+++ b/library/src/meta/link.rs
@@ -5,7 +5,9 @@ use crate::text::TextNode;
/// Link to a URL or another location in the document.
///
/// The link function makes its positional `body` argument clickable and links
-/// it to the destination specified by the `dest` argument.
+/// it to the destination specified by the `dest` argument. By default, links
+/// are not styled any different from normal text. However, you can easily apply
+/// a style of your choice with a show rule.
///
/// ## Example
/// ```
@@ -85,7 +87,7 @@ impl LinkNode {
fn field(&self, name: &str) -> Option<Value> {
match name {
- "url" => Some(match &self.dest {
+ "dest" => Some(match &self.dest {
Destination::Url(url) => Value::Str(url.clone().into()),
Destination::Internal(loc) => Value::Dict(loc.encode()),
}),
diff --git a/library/src/text/misc.rs b/library/src/text/misc.rs
index df40e52d..4cbc038e 100644
--- a/library/src/text/misc.rs
+++ b/library/src/text/misc.rs
@@ -27,12 +27,41 @@ impl Behave for SpaceNode {
}
/// # Line Break
-/// A line break.
+/// Inserts a line break.
+///
+/// Advances the paragraph to the next line. A single trailing linebreak at the
+/// end of a paragraph is ignored, but more than one creates additional empty
+/// lines.
+///
+/// ## Example
+/// ```
+/// *Date:* 26.12.2022 \
+/// *Topic:* Infrastructure Test \
+/// *Severity:* High \
+/// ```
+///
+/// ## Syntax
+/// This function also has dedicated syntax: To insert a linebreak, simply write
+/// a backslash followed by whitespace. This always creates an unjustified
+/// break.
///
/// ## Parameters
/// - justify: bool (named)
/// Whether to justify the line before the break.
///
+/// This is useful if you found a better line break opportunity in your
+/// justified text than Typst did.
+///
+/// ### Example
+/// ```
+/// #set par(justify: true)
+/// #let jb = linebreak(justify: true)
+///
+/// I have manually tuned the #jb
+/// linebreaks in this paragraph #jb
+/// for an _interesting_ result. #jb
+/// ```
+///
/// ## Category
/// text
#[func]
@@ -48,6 +77,13 @@ impl LinebreakNode {
let justify = args.named("justify")?.unwrap_or(false);
Ok(Self { justify }.pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "justify" => Some(Value::Bool(self.justify)),
+ _ => None,
+ }
+ }
}
impl Behave for LinebreakNode {
@@ -59,6 +95,23 @@ impl Behave for LinebreakNode {
/// # Strong Emphasis
/// Strongly emphasizes content by increasing the font weight.
///
+/// Increases the current font weight by a given `delta`.
+///
+/// ## Example
+/// ```
+/// This is *strong.* \
+/// This is #strong[too.] \
+///
+/// #show strong: set text(red)
+/// And this is *evermore.*
+/// ```
+///
+/// ## Syntax
+/// This function also has dedicated syntax: To strongly emphasize content,
+/// simply enclose it in stars/asterisks (`*`). Note that this only works at
+/// word boundaries. To strongly emphasize part of a word, you have to use the
+/// function.
+///
/// ## Parameters
/// - body: Content (positional, required)
/// The content to strongly emphasize.
@@ -73,6 +126,12 @@ pub struct StrongNode(pub Content);
#[node]
impl StrongNode {
/// The delta to apply on the font weight.
+ ///
+ /// # Example
+ /// ```
+ /// #set strong(delta: 0)
+ /// No *effect!*
+ /// ```
pub const DELTA: i64 = 300;
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
@@ -111,7 +170,29 @@ impl Fold for Delta {
}
/// # Emphasis
-/// Emphasizes content by flipping the italicness.
+/// Emphasizes content by setting it in italics.
+///
+/// - If the current [text](@text) style is `{"normal"}`,
+/// this turns it into `{"italic"}`.
+/// - If it is already `{"italic"}` or `{"oblique"}`,
+/// it turns it back to `{"normal"}`.
+///
+/// ## Example
+/// ```
+/// This is _emphasized._ \
+/// This is #emph[too.]
+///
+/// #show emph: it => {
+/// text(blue, it.body)
+/// }
+///
+/// This is _emphasized_ differently.
+/// ```
+///
+/// ## Syntax
+/// This function also has dedicated syntax: To emphasize content, simply
+/// enclose it in underscores (`_`). Note that this only works at word
+/// boundaries. To emphasize part of a word, you have to use the function.
///
/// ## Parameters
/// - body: Content (positional, required)
@@ -159,6 +240,13 @@ impl Fold for Toggle {
/// # Lowercase
/// Convert text or content to lowercase.
///
+/// ## Example
+/// ```
+/// #lower("ABC") \
+/// #lower[*My Text*] \
+/// #lower[already low]
+/// ```
+///
/// ## Parameters
/// - text: ToCase (positional, required)
/// The text to convert to lowercase.
@@ -173,6 +261,13 @@ pub fn lower(args: &mut Args) -> SourceResult<Value> {
/// # Uppercase
/// Convert text or content to uppercase.
///
+/// ## Example
+/// ```
+/// #upper("abc") \
+/// #upper[*my text*] \
+/// #upper[ALREADY HIGH]
+/// ```
+///
/// ## Parameters
/// - text: ToCase (positional, required)
/// The text to convert to uppercase.
@@ -225,6 +320,29 @@ impl Case {
/// # Small Capitals
/// Display text in small capitals.
///
+/// _Note:_ This enables the OpenType `smcp` feature for the font. Not all fonts
+/// support this feature (including Typst's current default font,
+/// unfortunately). Sometimes smallcaps are part of a dedicated font and
+/// sometimes they are not available at all. In the future, this function will
+/// support selecting a dedicated smallcaps font as well as synthesizing
+/// smallcaps from normal letters, but this is not yet implemented.
+///
+/// ## Example
+/// ```
+/// #set par(justify: true)
+/// #set text(family: "Noto Serif")
+/// #set heading(numbering: "I.")
+///
+/// #show heading: it => {
+/// set block(below: 10pt)
+/// set text(weight: "regular")
+/// align(center, smallcaps(it))
+/// }
+///
+/// = Introduction
+/// #lorem(40)
+/// ```
+///
/// ## Parameters
/// - text: Content (positional, required)
/// The text to display to small capitals.
diff --git a/library/src/text/mod.rs b/library/src/text/mod.rs
index e962685d..191b1fb8 100644
--- a/library/src/text/mod.rs
+++ b/library/src/text/mod.rs
@@ -26,14 +26,45 @@ use crate::layout::ParNode;
use crate::prelude::*;
/// # Text
-/// Stylable text.
+/// Customize the look and layout of text in a variety of ways.
+///
+/// This function is used often, both with set rules and directly. While the set
+/// rule is often the simpler choice, calling the text function directly can be
+/// useful when passing text as an argument to another function.
+///
+/// ## Example
+/// ```
+/// #set text(18pt)
+/// With a set rule.
+///
+/// #emph(text(blue)[
+/// With a function call.
+/// ])
+/// ```
///
/// ## Parameters
-/// - family: EcoString (positional, variadic, settable)
-/// A prioritized sequence of font families.
+/// - family: EcoString (positional, variadic, settable) A prioritized sequence
+/// of font families.
+///
+/// When processing text, Typst tries all specified font families in order
+/// until it finds a font that has the necessary glyphs. In the example below,
+/// the font `Inria Serif` is preferred, but since it does not contain Arabic
+/// glyphs, the arabic text uses `Noto Sans Arabic` instead.
///
-/// - body: Content (positional, required)
-/// Content in which all text is styled according to the other arguments.
+/// ### Example
+/// ```
+/// #set text(
+/// "Inria Serif",
+/// "Noto Sans Arabic",
+/// )
+///
+/// This is Latin. \
+/// هذا عربي.
+///
+/// ```
+///
+/// - body: Content (positional, required) Content in which all text is styled
+/// according to the other arguments.
///
/// ## Category
/// text
@@ -54,75 +85,383 @@ impl TextNode {
/// A prioritized sequence of font families.
#[property(skip, referenced)]
pub const FAMILY: FallbackList = FallbackList(vec![FontFamily::new("IBM Plex Sans")]);
- /// Whether to allow font fallback when the primary font list contains no
- /// match.
+
+ /// Whether to allow last resort font fallback when the primary font list
+ /// contains no match. This lets Typst search through all available fonts
+ /// for the most similar one that has the necessary glyphs.
+ ///
+ /// _Note:_ Currently, there are no warnings when fallback is disabled and
+ /// no glyphs are found. Instead, your text shows up in the form of "tofus":
+ /// Small boxes that indicate the lack of an appropriate glyph. In the
+ /// future, you will be able to instruct Typst to issue warnings so you know
+ /// something is up.
+ ///
+ /// # Example
+ /// ```
+ /// #set text(family: "Inria Serif")
+ /// هذا عربي
+ ///
+ /// #set text(fallback: false)
+ /// هذا عربي
+ /// ```
pub const FALLBACK: bool = true;
- /// How the font is styled.
+ /// The desired font style.
+ ///
+ /// When an italic style is requested and only an oblique one is available,
+ /// it is used. Similarly, the other way around, an italic style can stand
+ /// in for an oblique one. When neither an italic nor an oblique style is
+ /// available, Typst selects the normal style. Since most fonts are only
+ /// available either in an italic or oblique style, the difference between
+ /// italic and oblique style is rarely observable.
+ ///
+ /// If you want to emphasize your text, you should do so using the
+ /// [emph](@emph) function instead. This makes it easy to adapt the style
+ /// later if you change your mind about how to signify the emphasis.
+ ///
+ /// # Example
+ /// ```
+ /// #text("IBM Plex Sans", style: "italic")[Italic]
+ /// #text("DejaVu Sans", style: "oblique")[Oblique]
+ /// ```
pub const STYLE: FontStyle = FontStyle::Normal;
- /// The boldness / thickness of the font's glyphs.
+
+ /// The desired thickness of the font's glyphs. Accepts an integer between
+ /// `{100}` and `{900}` or one of the predefined weight names. When the
+ /// desired weight is not available, Typst selects the font from the family
+ /// that is closest in weight.
+ ///
+ /// If you want to strongly emphasize your text, you should do so using the
+ /// [strong](@strong) function instead. This makes it easy to adapt the
+ /// style later if you change your mind about how to signify the strong
+ /// emphasis.
+ ///
+ /// # Example
+ /// ```
+ /// #text(weight: "light")[Light] \
+ /// #text(weight: "regular")[Regular] \
+ /// #text(weight: "medium")[Medium] \
+ /// #text(weight: "bold")[Bold]
+ /// ```
pub const WEIGHT: FontWeight = FontWeight::REGULAR;
- /// The width of the glyphs.
+
+ /// The desired width of the glyphs. Accepts a ratio between `{50%}` and
+ /// `{200%}`. When the desired weight is not available, Typst selects the
+ /// font from the family that is closest in stretch.
+ ///
+ /// # Example
+ /// ```
+ /// #text(stretch: 75%)[Condensed] \
+ /// #text(stretch: 100%)[Normal]
+ /// ```
pub const STRETCH: FontStretch = FontStretch::NORMAL;
- /// The size of the glyphs.
+ /// The size of the glyphs. This value forms the basis of the `em` unit:
+ /// `{1em}` is equivalent to the font size.
+ ///
+ /// You can also give the font size itself in `em` units. Then, it is
+ /// relative to the previous font size.
+ ///
+ /// # Example
+ /// ```
+ /// #set text(size: 20pt)
+ /// very #text(1.5em)[big] text
+ /// ```
#[property(shorthand, fold)]
pub const SIZE: TextSize = Abs::pt(11.0);
+
/// The glyph fill color.
+ ///
+ /// # Example
+ /// ```
+ /// #set text(fill: red)
+ /// This text is red.
+ /// ```
#[property(shorthand)]
pub const FILL: Paint = Color::BLACK.into();
+
/// The amount of space that should be added between characters.
+ ///
+ /// # Example
+ /// ```
+ /// #set text(tracking: 1.5pt)
+ /// Distant text.
+ /// ```
#[property(resolve)]
pub const TRACKING: Length = Length::zero();
- /// The width of spaces relative to the font's space width.
+
+ /// The amount of space between words.
+ ///
+ /// Can be given as an absolute length, but also relative to the width of
+ /// the space character in the font.
+ ///
+ /// # Example
+ /// ```
+ /// #set text(spacing: 200%)
+ /// Text with distant words.
+ /// ```
#[property(resolve)]
pub const SPACING: Rel<Length> = Rel::one();
- /// The offset of the baseline.
+
+ /// An amount to shift the text baseline by.
+ ///
+ /// # Example
+ /// ```
+ /// A #text(baseline: 3pt)[lowered]
+ /// word.
+ /// ```
#[property(resolve)]
pub const BASELINE: Length = Length::zero();
- /// Whether certain glyphs can hang over into the margin.
+
+ /// Whether certain glyphs can hang over into the margin in justified text.
+ /// This can make justification visually more pleasing.
+ ///
+ /// # Example
+ /// ```
+ /// #set par(justify: true)
+ /// In this particular text, the
+ /// justification produces a hyphen
+ /// in the first line. Letting this
+ /// hyphen hang slightly into the
+ /// margin makes for a clear
+ /// paragraph edge.
+ ///
+ /// #set text(overhang: false)
+ /// In this particular text, the
+ /// justification produces a hyphen
+ /// in the first line. This time the
+ /// hyphen does not hang into the
+ /// margin, making the paragraph's
+ /// edge less clear.
+ /// ```
pub const OVERHANG: bool = true;
- /// The top end of the text bounding box.
+
+ /// The top end of the conceptual frame around the text used for layout and
+ /// positioning. This affects the size of containers that hold text.
+ ///
+ /// # Example
+ /// ```
+ /// #set text(size: 20pt)
+ /// #set text(top-edge: "ascender")
+ /// #rect(fill: aqua)[Typst]
+ ///
+ /// #set text(top-edge: "cap-height")
+ /// #rect(fill: aqua)[Typst]
+ /// ```
pub const TOP_EDGE: TextEdge = TextEdge::Metric(VerticalFontMetric::CapHeight);
- /// The bottom end of the text bounding box.
+
+ /// The bottom end of the conceptual frame around the text used for layout
+ /// and positioning. This affects the size of containers that hold text.
+ ///
+ /// # Example
+ /// ```
+ /// #set text(size: 20pt)
+ /// #set text(bottom-edge: "baseline")
+ /// #rect(fill: aqua)[Typst]
+ ///
+ /// #set text(bottom-edge: "descender")
+ /// #rect(fill: aqua)[Typst]
+ /// ```
pub const BOTTOM_EDGE: TextEdge = TextEdge::Metric(VerticalFontMetric::Baseline);
- /// An ISO 639-1/2/3 language code.
+ /// An [ISO 639-1/2/3 language code.](https://en.wikipedia.org/wiki/ISO_639)
+ ///
+ /// Setting the correct language affects various parts of Typst:
+ ///
+ /// - The text processing pipeline can make more informed choices.
+ /// - Hyphenation will use the correct patterns for the language.
+ /// - [Smart quotes](@smartquote) turns into the correct quotes for the
+ /// language.
+ /// - And all other things which are language-aware.
+ ///
+ /// # Example
+ /// ```
+ /// #set text(lang: "de")
+ /// #outline()
+ ///
+ /// = Einleitung
+ /// In diesem Dokument, ...
+ /// ```
pub const LANG: Lang = Lang::ENGLISH;
- /// An ISO 3166-1 alpha-2 region code.
+
+ /// An [ISO 3166-1 alpha-2 region code.](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
+ ///
+ /// This lets the text processing pipeline make more informed choices.
pub const REGION: Option<Region> = None;
- /// The direction for text and inline objects. When `auto`, the direction is
- /// automatically inferred from the language.
+
+ /// The dominant direction for text and inline objects. Possible values are:
+ ///
+ /// - `{auto}`: Automatically infer the direction from the `lang` property.
+ /// - `{ltr}`: Layout text from left to right.
+ /// - `{rtl}`: Layout text from right to left.
+ ///
+ /// When writing in right-to-left scripts like Arabic or Hebrew, you should
+ /// set the language or direction. While individual runs of text are
+ /// automatically layouted in the correct direction, setting the dominant
+ /// direction gives the bidirectional reordering algorithm the necessary
+ /// information to correctly place punctuation and inline objects.
+ /// Furthermore, setting the direction affects the alignment values `start`
+ /// and `end`, which are equivalent to `left` and `right` in `ltr` text and
+ /// the other way around in `rtl` text.
+ ///
+ /// If you set this to `rtl` and experience bugs or in some way bad looking
+ /// output, please do get in touch with us through the [contact
+ /// form](/contact) or our [Discord server](/docs/community/#discord)!
+ ///
+ /// # Example
+ /// ```
+ /// #set text(dir: rtl)
+ /// هذا عربي.
+ /// ```
#[property(resolve)]
pub const DIR: HorizontalDir = HorizontalDir(Smart::Auto);
- /// Whether to hyphenate text to improve line breaking. When `auto`, words
- /// will will be hyphenated if and only if justification is enabled.
+
+ /// Whether to hyphenate text to improve line breaking. When `{auto}`, text
+ /// will be hyphenated if and only if justification is enabled.
+ ///
+ /// # Example
+ /// ```
+ /// #set par(justify: true)
+ /// This text illustrates how
+ /// enabling hyphenation can
+ /// improve justification.
+ ///
+ /// #set text(hyphenate: false)
+ /// This text illustrates how
+ /// enabling hyphenation can
+ /// improve justification.
+ /// ```
#[property(resolve)]
pub const HYPHENATE: Hyphenate = Hyphenate(Smart::Auto);
- /// Whether to apply smart quotes.
- pub const SMART_QUOTES: bool = true;
- /// Whether to apply kerning ("kern").
+ /// Whether to apply kerning.
+ ///
+ /// When enabled, specific letter pairings move closer together or further
+ /// apart for a more visually pleasing result. The example below
+ /// demonstrates how decreasing the gap between the "T" and "o" results in a
+ /// more natural look. Setting this to `{false}` disables kerning by turning
+ /// off the OpenType `kern` font feature.
+ ///
+ /// # Example
+ /// ```
+ /// #set text(size: 25pt)
+ /// Totally
+ ///
+ /// #set text(kerning: false)
+ /// Totally
+ /// ```
pub const KERNING: bool = true;
- /// Whether to apply stylistic alternates. ("salt")
+
+ /// Whether to apply stylistic alternates.
+ ///
+ /// Sometimes fonts contain alternative glyphs for the same codepoint.
+ /// Setting this to `{true}` switches to these by enabling the OpenType
+ /// `salt` font feature.
+ ///
+ /// # Example
+ /// ```
+ /// #set text(size: 20pt)
+ /// 0, a, g, ß
+ ///
+ /// #set text(alternates: true)
+ /// 0, a, g, ß
+ /// ```
pub const ALTERNATES: bool = false;
- /// Which stylistic set to apply. ("ss01" - "ss20")
+
+ /// Which stylistic set to apply. Font designers can categorize alternative
+ /// glyphs forms into stylistic sets. As this value is highly font-specific,
+ /// you need to consult your font to know which sets are available. When set
+ /// to an integer between `{1}` and `{20}`, enables the corresponding
+ /// OpenType font feature from `ss01`, ..., `ss20`.
pub const STYLISTIC_SET: Option<StylisticSet> = None;
- /// Whether standard ligatures are active. ("liga", "clig")
+
+ /// Whether standard ligatures are active.
+ ///
+ /// Certain letter combinations like "fi" are often displayed as a single
+ /// merged glyph called a _ligature._ Setting this to `{false}` disables
+ /// these ligatures by turning off the OpenType `liga` and `clig` font
+ /// features.
+ ///
+ /// # Example
+ /// ```
+ /// #set text(size: 20pt)
+ /// A fine ligature.
+ ///
+ /// #set text(ligatures: false)
+ /// A fine ligature.
+ /// ```
pub const LIGATURES: bool = true;
- /// Whether ligatures that should be used sparingly are active. ("dlig")
+
+ /// Whether ligatures that should be used sparingly are active. Setting this
+ /// to `{true}` enables the OpenType `dlig` font feature.
pub const DISCRETIONARY_LIGATURES: bool = false;
- /// Whether historical ligatures are active. ("hlig")
+
+ /// Whether historical ligatures are active. Setting this to `{true}`
+ /// enables the OpenType `hlig` font feature.
pub const HISTORICAL_LIGATURES: bool = false;
- /// Which kind of numbers / figures to select.
+
+ /// Which kind of numbers / figures to select. When set to `{auto}`, the
+ /// default numbers for the font are used.
+ ///
+ /// # Example
+ /// ```
+ /// #set text(20pt, "Noto Sans")
+ /// #set text(number-type: "lining")
+ /// Number 9.
+ ///
+ /// #set text(number-type: "old-style")
+ /// Number 9.
+ /// ```
pub const NUMBER_TYPE: Smart<NumberType> = Smart::Auto;
- /// The width of numbers / figures.
+
+ /// The width of numbers / figures. When set to `{auto}`, the default
+ /// numbers for the font are used.
+ ///
+ /// # Example
+ /// ```
+ /// #set text(20pt, "Noto Sans")
+ /// #set text(number-width: "proportional")
+ /// A 12 B 34. \
+ /// A 56 B 78.
+ ///
+ /// #set text(number-width: "tabular")
+ /// A 12 B 34. \
+ /// A 56 B 78.
+ /// ```
pub const NUMBER_WIDTH: Smart<NumberWidth> = Smart::Auto;
- /// Whether to have a slash through the zero glyph. ("zero")
+
+ /// Whether to have a slash through the zero glyph. Setting this to `{true}`
+ /// enables the OpenType `zero` font feature.
+ ///
+ /// # Example
+ /// ```
+ /// 0, #text(slashed-zero: true)[0]
+ /// ```
pub const SLASHED_ZERO: bool = false;
- /// Whether to convert fractions. ("frac")
+
+ /// Whether to turns numbers into fractions. Setting this to `{true}`
+ /// enables the OpenType `frac` font feature.
+ ///
+ /// # Example
+ /// ```
+ /// 1/2 \
+ /// #text(fractions: true)[1/2]
+ /// ```
pub const FRACTIONS: bool = false;
+
/// Raw OpenType features to apply.
+ ///
+ /// - If given an array of strings, sets the features identified by the
+ /// strings to `{1}`.
+ /// - If given a dictionary mapping to numbers, sets the features
+ /// identified by the keys to the values.
+ ///
+ /// # Example
+ /// ```
+ /// // Enable the `frac` feature manually.
+ /// #set text(features: ("frac",))
+ /// 1/2
+ /// ```
#[property(fold)]
pub const FEATURES: FontFeatures = FontFeatures(vec![]);
@@ -272,7 +611,7 @@ impl TextEdge {
castable! {
TextEdge,
v: Length => Self::Length(v),
- /// The distance from the baseline to the ascender.
+ /// The font's ascender, which typically exceeds the height of all glyphs.
"ascender" => Self::Metric(VerticalFontMetric::Ascender),
/// The approximate height of uppercase letters.
"cap-height" => Self::Metric(VerticalFontMetric::CapHeight),
@@ -280,7 +619,7 @@ castable! {
"x-height" => Self::Metric(VerticalFontMetric::XHeight),
/// The baseline on which the letters rest.
"baseline" => Self::Metric(VerticalFontMetric::Baseline),
- /// The distance from the baseline to the descender.
+ /// The font's ascender, which typically exceeds the depth of all glyphs.
"descender" => Self::Metric(VerticalFontMetric::Descender),
}
@@ -364,9 +703,11 @@ pub enum NumberType {
castable! {
NumberType,
- /// Numbers that fit well with capital text.
+ /// Numbers that fit well with capital text (the OpenType `lnum`
+ /// font feature).
"lining" => Self::Lining,
- /// Numbers that fit well into a flow of upper- and lowercase text.
+ /// Numbers that fit well into a flow of upper- and lowercase text (the
+ /// OpenType `onum` font feature).
"old-style" => Self::OldStyle,
}
@@ -381,9 +722,9 @@ pub enum NumberWidth {
castable! {
NumberWidth,
- /// Number widths are glyph specific.
+ /// Numbers with glyph-specific widths (the OpenType `pnum` font feature).
"proportional" => Self::Proportional,
- /// All numbers are of equal width / monospaced.
+ /// Numbers of equal width (the OpenType `tnum` font feature).
"tabular" => Self::Tabular,
}
diff --git a/library/src/text/quotes.rs b/library/src/text/quotes.rs
index 5965df56..40a9bb82 100644
--- a/library/src/text/quotes.rs
+++ b/library/src/text/quotes.rs
@@ -3,11 +3,29 @@ use typst::syntax::is_newline;
use crate::prelude::*;
/// # Smart Quote
-/// A smart quote.
+/// A language-aware quote that reacts to its context.
+///
+/// Automatically turns into an appropriate opening or closing quote based on
+/// the active [text](@text) language.
+///
+/// ## Syntax
+/// This function also has dedicated syntax: The normal quote characters
+/// (`'` and `"`). Typst automatically makes your quotes smart.
+///
+/// ## Example
+/// ```
+/// "This is in quotes."
+///
+/// #set text(lang: "de")
+/// "Das ist in Anführungszeichen."
+///
+/// #set text(lang: "fr")
+/// "C'est entre guillemets."
+/// ```
///
/// ## Parameters
/// - double: bool (named)
-/// Whether to produce a smart double quote.
+/// Whether this should be a double quote.
///
/// ## Category
/// text
@@ -20,10 +38,30 @@ pub struct SmartQuoteNode {
#[node]
impl SmartQuoteNode {
+ /// Whether smart quotes are enabled.
+ ///
+ /// To disable smartness for a single quote, you can also escape it with a
+ /// backslash.
+ ///
+ /// # Example
+ /// ```
+ /// #set smartquote(enabled: false)
+ ///
+ /// These are "dumb" quotes.
+ /// ```
+ pub const ENABLED: bool = true;
+
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let double = args.named("double")?.unwrap_or(true);
Ok(Self { double }.pack())
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "double" => Some(Value::Bool(self.double)),
+ _ => None,
+ }
+ }
}
/// State machine for smart quote subtitution.
diff --git a/library/src/text/raw.rs b/library/src/text/raw.rs
index 240f82e8..938224d0 100644
--- a/library/src/text/raw.rs
+++ b/library/src/text/raw.rs
@@ -2,24 +2,93 @@ use once_cell::sync::Lazy;
use syntect::highlighting as synt;
use typst::syntax::{self, LinkedNode};
-use super::{FontFamily, Hyphenate, LinebreakNode, TextNode};
+use super::{FontFamily, Hyphenate, LinebreakNode, SmartQuoteNode, TextNode};
use crate::layout::BlockNode;
use crate::prelude::*;
-/// # Raw Text
+/// # Raw Text / Code
/// Raw text with optional syntax highlighting.
///
+/// Displays the text verbatim and in a monospace font. This is typically used
+/// to embed computer code into your document.
+///
+/// ## Syntax
+/// This function also has dedicated syntax. You can enclose text in 1 or 3+
+/// backticks (`` ` ``) to make it raw. Two backticks produce empty raw text.
+/// When you use three or more backticks, you can additionally specify a
+/// language tag for syntax highlighting directly after the opening backticks.
+/// Within raw blocks, everything is rendered as is, in particular, there are no
+/// escape sequences.
+///
+/// ## Example
+/// ````
+/// Adding `rbx` to `rcx` gives
+/// the desired result.
+///
+/// ```rust
+/// fn main() {
+/// println!("Hello World!");
+/// }
+/// ```
+/// ````
+///
/// ## Parameters
/// - text: EcoString (positional, required)
/// The raw text.
///
+/// You can also use raw blocks creatively to create custom syntaxes for
+/// your automations.
+///
+/// ### Example
+/// ````
+/// // Parse numbers in raw blocks with the `mydsl` tag and
+/// // sum them up.
+/// #show raw.where(lang: "mydsl"): it => {
+/// let sum = 0
+/// for part in it.text.split("+") {
+/// sum += int(part.trim())
+/// }
+/// sum
+/// }
+///
+/// ```mydsl
+/// 1 + 2 + 3 + 4 + 5
+/// ```
+/// ````
+///
/// - block: bool (named)
/// Whether the raw text is displayed as a separate block.
///
+/// ### Example
+/// ````
+/// // Display inline code in a small box
+/// // that retains the correct baseline.
+/// #show raw.where(block: false): rect.with(
+/// fill: luma(240),
+/// inset: (x: 3pt),
+/// outset: (y: 3pt),
+/// radius: 2pt,
+/// )
+///
+/// // Display block code in a larger box
+/// // with more padding.
+/// #show raw.where(block: true): rect.with(
+/// fill: luma(240),
+/// inset: 10pt,
+/// radius: 4pt,
+/// )
+///
+/// With `rg`, you can search through your files quickly.
+///
+/// ```bash
+/// rg "Hello World"
+/// ```
+/// ````
+///
/// ## Category
/// text
#[func]
-#[capable(Show)]
+#[capable(Show, Prepare)]
#[derive(Debug, Hash)]
pub struct RawNode {
/// The raw text.
@@ -31,6 +100,17 @@ pub struct RawNode {
#[node]
impl RawNode {
/// The language to syntax-highlight in.
+ ///
+ /// Apart from typical language tags known from Markdown, this supports the
+ /// `{"typ"}` and `{"typc"}` tags for Typst markup and Typst code,
+ /// respectively.
+ ///
+ /// # Example
+ /// ````
+ /// ```typ
+ /// This is *Typst!*
+ /// ```
+ /// ````
#[property(referenced)]
pub const LANG: Option<EcoString> = None;
@@ -121,7 +201,7 @@ impl Show for RawNode {
let mut map = StyleMap::new();
map.set(TextNode::OVERHANG, false);
map.set(TextNode::HYPHENATE, Hyphenate(Smart::Custom(false)));
- map.set(TextNode::SMART_QUOTES, false);
+ map.set(SmartQuoteNode::ENABLED, false);
map.set_family(FontFamily::new("IBM Plex Mono"), styles);
Ok(realized.styled_with_map(map))
diff --git a/library/src/text/symbol.rs b/library/src/text/symbol.rs
index ec2653df..a59461a7 100644
--- a/library/src/text/symbol.rs
+++ b/library/src/text/symbol.rs
@@ -4,9 +4,49 @@ use crate::text::TextNode;
/// # Symbol
/// A symbol identified by symmie notation.
///
+/// Symmie is Typst's notation for Unicode symbols. It is based on the idea of
+/// _modifiers._ Many symbols in Unicode are very similar. In symmie, such
+/// groups of symbols share a common name. To distinguish between the symbols
+/// within a group, we use one or multiple modifiers that are separated from the
+/// name by colons.
+///
+/// There is currently no easily viewable list of all names, but in the
+/// meantime you can rely on the autocompletion in Typst's web editor.
+///
+/// ## Syntax
+/// This function also has dedicated syntax: In markup, you can enclose symmie
+/// notation within colons to produce a symbol. And in math, you can just write
+/// the notation directly. There, all letter sequence of length at least two are
+/// automatically parsed as symbols (unless a variable of that name is defined).
+///
+/// ## Example
+/// ```
+/// // In text, with colons.
+/// :arrow:l: \
+/// :arrow:r: \
+/// :arrow:t: \
+/// :turtle: \
+/// :face:halo: \
+/// :woman:old:
+///
+/// // In math, directly.
+/// $f : NN -> RR$ \
+/// $A sub:eq B without C$ \
+/// $a times:div b eq:not c$
+/// ```
+///
/// ## Parameters
/// - notation: EcoString (positional, required)
-/// The symbols symmie notation.
+/// The symbol's symmie notation.
+///
+/// Consists of a name, followed by a number colon-separated modifiers
+/// in no particular order.
+///
+/// ### Example
+/// ```
+/// #symbol("NN") \
+/// #symbol("face:grin")
+/// ```
///
/// ## Category
/// text
diff --git a/library/src/visualize/shape.rs b/library/src/visualize/shape.rs
index 5edab70b..d1ced9cd 100644
--- a/library/src/visualize/shape.rs
+++ b/library/src/visualize/shape.rs
@@ -94,6 +94,16 @@ impl<const S: ShapeKind> ShapeNode<S> {
styles.set_opt(Self::RADIUS, args.named("radius")?);
}
}
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "body" => match &self.0 {
+ Some(body) => Some(Value::Content(body.clone())),
+ None => Some(Value::None),
+ },
+ _ => None,
+ }
+ }
}
impl<const S: ShapeKind> Layout for ShapeNode<S> {