summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/text
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2025-07-09 10:16:36 +0200
committerGitHub <noreply@github.com>2025-07-09 08:16:36 +0000
commite5e1dcd9c01341d2cd3473ac94a70223d5966086 (patch)
treed648efad9463cb10270d55ba35210eeb1e91ee22 /crates/typst-library/src/text
parent0a3c6939dd274f40672484695d909c2cc0d0d755 (diff)
Target-specific native show rules (#6569)
Diffstat (limited to 'crates/typst-library/src/text')
-rw-r--r--crates/typst-library/src/text/deco.rs123
-rw-r--r--crates/typst-library/src/text/raw.rs59
-rw-r--r--crates/typst-library/src/text/shift.rs83
-rw-r--r--crates/typst-library/src/text/smallcaps.rs16
-rw-r--r--crates/typst-library/src/text/smartquote.rs13
5 files changed, 31 insertions, 263 deletions
diff --git a/crates/typst-library/src/text/deco.rs b/crates/typst-library/src/text/deco.rs
index 8c1d5634..f7d5c33b 100644
--- a/crates/typst-library/src/text/deco.rs
+++ b/crates/typst-library/src/text/deco.rs
@@ -1,13 +1,6 @@
-use smallvec::smallvec;
-
-use crate::diag::SourceResult;
-use crate::engine::Engine;
-use crate::foundations::{
- elem, Content, NativeElement, Packed, Show, Smart, StyleChain, TargetElem,
-};
-use crate::html::{attr, tag, HtmlElem};
+use crate::foundations::{elem, Content, Smart};
use crate::layout::{Abs, Corners, Length, Rel, Sides};
-use crate::text::{BottomEdge, BottomEdgeMetric, TextElem, TopEdge, TopEdgeMetric};
+use crate::text::{BottomEdge, BottomEdgeMetric, TopEdge, TopEdgeMetric};
use crate::visualize::{Color, FixedStroke, Paint, Stroke};
/// Underlines text.
@@ -16,7 +9,7 @@ use crate::visualize::{Color, FixedStroke, Paint, Stroke};
/// ```example
/// This is #underline[important].
/// ```
-#[elem(Show)]
+#[elem]
pub struct UnderlineElem {
/// How to [stroke] the line.
///
@@ -78,41 +71,13 @@ pub struct UnderlineElem {
pub body: Content,
}
-impl Show for Packed<UnderlineElem> {
- #[typst_macros::time(name = "underline", span = self.span())]
- fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
- if styles.get(TargetElem::target).is_html() {
- // Note: In modern HTML, `<u>` is not the underline element, but
- // rather an "Unarticulated Annotation" element (see HTML spec
- // 4.5.22). Using `text-decoration` instead is recommended by MDN.
- return Ok(HtmlElem::new(tag::span)
- .with_attr(attr::style, "text-decoration: underline")
- .with_body(Some(self.body.clone()))
- .pack());
- }
-
- Ok(self.body.clone().set(
- TextElem::deco,
- smallvec![Decoration {
- line: DecoLine::Underline {
- stroke: self.stroke.resolve(styles).unwrap_or_default(),
- offset: self.offset.resolve(styles),
- evade: self.evade.get(styles),
- background: self.background.get(styles),
- },
- extent: self.extent.resolve(styles),
- }],
- ))
- }
-}
-
/// Adds a line over text.
///
/// # Example
/// ```example
/// #overline[A line over text.]
/// ```
-#[elem(Show)]
+#[elem]
pub struct OverlineElem {
/// How to [stroke] the line.
///
@@ -180,38 +145,13 @@ pub struct OverlineElem {
pub body: Content,
}
-impl Show for Packed<OverlineElem> {
- #[typst_macros::time(name = "overline", span = self.span())]
- fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
- if styles.get(TargetElem::target).is_html() {
- return Ok(HtmlElem::new(tag::span)
- .with_attr(attr::style, "text-decoration: overline")
- .with_body(Some(self.body.clone()))
- .pack());
- }
-
- Ok(self.body.clone().set(
- TextElem::deco,
- smallvec![Decoration {
- line: DecoLine::Overline {
- stroke: self.stroke.resolve(styles).unwrap_or_default(),
- offset: self.offset.resolve(styles),
- evade: self.evade.get(styles),
- background: self.background.get(styles),
- },
- extent: self.extent.resolve(styles),
- }],
- ))
- }
-}
-
/// Strikes through text.
///
/// # Example
/// ```example
/// This is #strike[not] relevant.
/// ```
-#[elem(title = "Strikethrough", Show)]
+#[elem(title = "Strikethrough")]
pub struct StrikeElem {
/// How to [stroke] the line.
///
@@ -264,35 +204,13 @@ pub struct StrikeElem {
pub body: Content,
}
-impl Show for Packed<StrikeElem> {
- #[typst_macros::time(name = "strike", span = self.span())]
- fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
- if styles.get(TargetElem::target).is_html() {
- return Ok(HtmlElem::new(tag::s).with_body(Some(self.body.clone())).pack());
- }
-
- Ok(self.body.clone().set(
- TextElem::deco,
- smallvec![Decoration {
- // Note that we do not support evade option for strikethrough.
- line: DecoLine::Strikethrough {
- stroke: self.stroke.resolve(styles).unwrap_or_default(),
- offset: self.offset.resolve(styles),
- background: self.background.get(styles),
- },
- extent: self.extent.resolve(styles),
- }],
- ))
- }
-}
-
/// Highlights text with a background color.
///
/// # Example
/// ```example
/// This is #highlight[important].
/// ```
-#[elem(Show)]
+#[elem]
pub struct HighlightElem {
/// The color to highlight the text with.
///
@@ -363,35 +281,6 @@ pub struct HighlightElem {
pub body: Content,
}
-impl Show for Packed<HighlightElem> {
- #[typst_macros::time(name = "highlight", span = self.span())]
- fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
- if styles.get(TargetElem::target).is_html() {
- return Ok(HtmlElem::new(tag::mark)
- .with_body(Some(self.body.clone()))
- .pack());
- }
-
- Ok(self.body.clone().set(
- TextElem::deco,
- smallvec![Decoration {
- line: DecoLine::Highlight {
- fill: self.fill.get_cloned(styles),
- stroke: self
- .stroke
- .resolve(styles)
- .unwrap_or_default()
- .map(|stroke| stroke.map(Stroke::unwrap_or_default)),
- top_edge: self.top_edge.get(styles),
- bottom_edge: self.bottom_edge.get(styles),
- radius: self.radius.resolve(styles).unwrap_or_default(),
- },
- extent: self.extent.resolve(styles),
- }],
- ))
- }
-}
-
/// A text decoration.
///
/// Can be positioned over, under, or on top of text, or highlight the text with
diff --git a/crates/typst-library/src/text/raw.rs b/crates/typst-library/src/text/raw.rs
index 67038163..8cddfbfb 100644
--- a/crates/typst-library/src/text/raw.rs
+++ b/crates/typst-library/src/text/raw.rs
@@ -16,14 +16,13 @@ use crate::diag::{
};
use crate::engine::Engine;
use crate::foundations::{
- cast, elem, scope, Bytes, Content, Derived, NativeElement, OneOrMultiple, Packed,
- PlainText, Show, ShowSet, Smart, StyleChain, Styles, Synthesize, TargetElem,
+ cast, elem, scope, Bytes, Content, Derived, OneOrMultiple, Packed, PlainText,
+ ShowSet, Smart, StyleChain, Styles, Synthesize,
};
-use crate::html::{tag, HtmlElem};
-use crate::layout::{BlockBody, BlockElem, Em, HAlignment};
+use crate::layout::{Em, HAlignment};
use crate::loading::{DataSource, Load};
use crate::model::{Figurable, ParElem};
-use crate::text::{FontFamily, FontList, LinebreakElem, LocalName, TextElem, TextSize};
+use crate::text::{FontFamily, FontList, LocalName, TextElem, TextSize};
use crate::visualize::Color;
use crate::World;
@@ -78,7 +77,6 @@ use crate::World;
scope,
title = "Raw Text / Code",
Synthesize,
- Show,
ShowSet,
LocalName,
Figurable,
@@ -429,46 +427,6 @@ impl Packed<RawElem> {
}
}
-impl Show for Packed<RawElem> {
- #[typst_macros::time(name = "raw", span = self.span())]
- fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
- let lines = self.lines.as_deref().unwrap_or_default();
-
- let mut seq = EcoVec::with_capacity((2 * lines.len()).saturating_sub(1));
- for (i, line) in lines.iter().enumerate() {
- if i != 0 {
- seq.push(LinebreakElem::shared().clone());
- }
-
- seq.push(line.clone().pack());
- }
-
- let mut realized = Content::sequence(seq);
-
- if styles.get(TargetElem::target).is_html() {
- return Ok(HtmlElem::new(if self.block.get(styles) {
- tag::pre
- } else {
- tag::code
- })
- .with_body(Some(realized))
- .pack()
- .spanned(self.span()));
- }
-
- if self.block.get(styles) {
- // Align the text before inserting it into the block.
- realized = realized.aligned(self.align.get(styles).into());
- realized = BlockElem::new()
- .with_body(Some(BlockBody::Content(realized)))
- .pack()
- .spanned(self.span());
- }
-
- Ok(realized)
- }
-}
-
impl ShowSet for Packed<RawElem> {
fn show_set(&self, styles: StyleChain) -> Styles {
let mut out = Styles::new();
@@ -634,7 +592,7 @@ fn format_theme_error(error: syntect::LoadingError) -> LoadError {
/// It allows you to access various properties of the line, such as the line
/// number, the raw non-highlighted text, the highlighted text, and whether it
/// is the first or last line of the raw block.
-#[elem(name = "line", title = "Raw Text / Code Line", Show, PlainText)]
+#[elem(name = "line", title = "Raw Text / Code Line", PlainText)]
pub struct RawLine {
/// The line number of the raw line inside of the raw block, starts at 1.
#[required]
@@ -653,13 +611,6 @@ pub struct RawLine {
pub body: Content,
}
-impl Show for Packed<RawLine> {
- #[typst_macros::time(name = "raw.line", span = self.span())]
- fn show(&self, _: &mut Engine, _styles: StyleChain) -> SourceResult<Content> {
- Ok(self.body.clone())
- }
-}
-
impl PlainText for Packed<RawLine> {
fn plain_text(&self, text: &mut EcoString) {
text.push_str(&self.text);
diff --git a/crates/typst-library/src/text/shift.rs b/crates/typst-library/src/text/shift.rs
index 1a05d8f9..87ccae63 100644
--- a/crates/typst-library/src/text/shift.rs
+++ b/crates/typst-library/src/text/shift.rs
@@ -1,13 +1,8 @@
-use crate::diag::SourceResult;
-use crate::engine::Engine;
-use crate::foundations::{
- elem, Content, NativeElement, Packed, Show, Smart, StyleChain, TargetElem,
-};
-use crate::html::{tag, HtmlElem};
-use crate::layout::{Em, Length};
-use crate::text::{FontMetrics, TextElem, TextSize};
use ttf_parser::Tag;
-use typst_library::text::ScriptMetrics;
+
+use crate::foundations::{elem, Content, Smart};
+use crate::layout::{Em, Length};
+use crate::text::{FontMetrics, ScriptMetrics, TextSize};
/// Renders text in subscript.
///
@@ -17,7 +12,7 @@ use typst_library::text::ScriptMetrics;
/// ```example
/// Revenue#sub[yearly]
/// ```
-#[elem(title = "Subscript", Show)]
+#[elem(title = "Subscript")]
pub struct SubElem {
/// Whether to create artificial subscripts by lowering and scaling down
/// regular glyphs.
@@ -64,29 +59,6 @@ pub struct SubElem {
pub body: Content,
}
-impl Show for Packed<SubElem> {
- #[typst_macros::time(name = "sub", span = self.span())]
- fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
- let body = self.body.clone();
-
- if styles.get(TargetElem::target).is_html() {
- return Ok(HtmlElem::new(tag::sub)
- .with_body(Some(body))
- .pack()
- .spanned(self.span()));
- }
-
- show_script(
- styles,
- body,
- self.typographic.get(styles),
- self.baseline.get(styles),
- self.size.get(styles),
- ScriptKind::Sub,
- )
- }
-}
-
/// Renders text in superscript.
///
/// The text is rendered smaller and its baseline is raised.
@@ -95,7 +67,7 @@ impl Show for Packed<SubElem> {
/// ```example
/// 1#super[st] try!
/// ```
-#[elem(title = "Superscript", Show)]
+#[elem(title = "Superscript")]
pub struct SuperElem {
/// Whether to create artificial superscripts by raising and scaling down
/// regular glyphs.
@@ -146,49 +118,6 @@ pub struct SuperElem {
pub body: Content,
}
-impl Show for Packed<SuperElem> {
- #[typst_macros::time(name = "super", span = self.span())]
- fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
- let body = self.body.clone();
-
- if styles.get(TargetElem::target).is_html() {
- return Ok(HtmlElem::new(tag::sup)
- .with_body(Some(body))
- .pack()
- .spanned(self.span()));
- }
-
- show_script(
- styles,
- body,
- self.typographic.get(styles),
- self.baseline.get(styles),
- self.size.get(styles),
- ScriptKind::Super,
- )
- }
-}
-
-fn show_script(
- styles: StyleChain,
- body: Content,
- typographic: bool,
- baseline: Smart<Length>,
- size: Smart<TextSize>,
- kind: ScriptKind,
-) -> SourceResult<Content> {
- let font_size = styles.resolve(TextElem::size);
- Ok(body.set(
- TextElem::shift_settings,
- Some(ShiftSettings {
- typographic,
- shift: baseline.map(|l| -Em::from_length(l, font_size)),
- size: size.map(|t| Em::from_length(t.0, font_size)),
- kind,
- }),
- ))
-}
-
/// Configuration values for sub- or superscript text.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct ShiftSettings {
diff --git a/crates/typst-library/src/text/smallcaps.rs b/crates/typst-library/src/text/smallcaps.rs
index 1c283893..199222fe 100644
--- a/crates/typst-library/src/text/smallcaps.rs
+++ b/crates/typst-library/src/text/smallcaps.rs
@@ -1,7 +1,4 @@
-use crate::diag::SourceResult;
-use crate::engine::Engine;
-use crate::foundations::{elem, Content, Packed, Show, StyleChain};
-use crate::text::TextElem;
+use crate::foundations::{elem, Content};
/// Displays text in small capitals.
///
@@ -43,7 +40,7 @@ use crate::text::TextElem;
/// = Introduction
/// #lorem(40)
/// ```
-#[elem(title = "Small Capitals", Show)]
+#[elem(title = "Small Capitals")]
pub struct SmallcapsElem {
/// Whether to turn uppercase letters into small capitals as well.
///
@@ -61,15 +58,6 @@ pub struct SmallcapsElem {
pub body: Content,
}
-impl Show for Packed<SmallcapsElem> {
- #[typst_macros::time(name = "smallcaps", span = self.span())]
- fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
- let sc =
- if self.all.get(styles) { Smallcaps::All } else { Smallcaps::Minuscules };
- Ok(self.body.clone().set(TextElem::smallcaps, Some(sc)))
- }
-}
-
/// What becomes small capitals.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Smallcaps {
diff --git a/crates/typst-library/src/text/smartquote.rs b/crates/typst-library/src/text/smartquote.rs
index 24787d06..375b1cf0 100644
--- a/crates/typst-library/src/text/smartquote.rs
+++ b/crates/typst-library/src/text/smartquote.rs
@@ -5,9 +5,10 @@ use unicode_segmentation::UnicodeSegmentation;
use crate::diag::{bail, HintedStrResult, StrResult};
use crate::foundations::{
array, cast, dict, elem, Array, Dict, FromValue, Packed, PlainText, Smart, Str,
+ StyleChain,
};
use crate::layout::Dir;
-use crate::text::{Lang, Region};
+use crate::text::{Lang, Region, TextElem};
/// A language-aware quote that reacts to its context.
///
@@ -200,6 +201,16 @@ pub struct SmartQuotes<'s> {
}
impl<'s> SmartQuotes<'s> {
+ /// Retrieve the smart quotes as configured by the current styles.
+ pub fn get_in(styles: StyleChain<'s>) -> Self {
+ Self::get(
+ styles.get_ref(SmartQuoteElem::quotes),
+ styles.get(TextElem::lang),
+ styles.get(TextElem::region),
+ styles.get(SmartQuoteElem::alternative),
+ )
+ }
+
/// Create a new `Quotes` struct with the given quotes, optionally falling
/// back to the defaults for a language and region.
///