summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormkorje <me@mkor.je>2024-08-05 11:28:17 +0000
committerGitHub <noreply@github.com>2024-08-05 11:28:17 +0000
commit810491c9d31b614a435020f888fbd380e8e039a1 (patch)
tree2e0a19135467000abe1fc55a51fba191293783bd
parent211b546e4e4c75caaa6ee31493d52802b898aa2a (diff)
Add missing under/over-delimiters (#4637)
-rw-r--r--crates/typst/src/math/mod.rs4
-rw-r--r--crates/typst/src/math/underover.rs208
-rw-r--r--docs/reference/groups.yml4
-rw-r--r--tests/ref/math-underover-parens.pngbin0 -> 726 bytes
-rw-r--r--tests/ref/math-underover-shells.pngbin0 -> 551 bytes
-rw-r--r--tests/suite/math/underover.typ14
6 files changed, 191 insertions, 39 deletions
diff --git a/crates/typst/src/math/mod.rs b/crates/typst/src/math/mod.rs
index 04db9efb..966945ce 100644
--- a/crates/typst/src/math/mod.rs
+++ b/crates/typst/src/math/mod.rs
@@ -176,6 +176,10 @@ pub fn module() -> Module {
math.define_elem::<OverbraceElem>();
math.define_elem::<UnderbracketElem>();
math.define_elem::<OverbracketElem>();
+ math.define_elem::<UnderparenElem>();
+ math.define_elem::<OverparenElem>();
+ math.define_elem::<UndershellElem>();
+ math.define_elem::<OvershellElem>();
math.define_elem::<CancelElem>();
math.define_elem::<FracElem>();
math.define_elem::<BinomElem>();
diff --git a/crates/typst/src/math/underover.rs b/crates/typst/src/math/underover.rs
index defa46c1..68a39983 100644
--- a/crates/typst/src/math/underover.rs
+++ b/crates/typst/src/math/underover.rs
@@ -2,9 +2,9 @@ use crate::diag::SourceResult;
use crate::foundations::{elem, Content, Packed, StyleChain};
use crate::layout::{Abs, Em, FixedAlignment, Frame, FrameItem, Point, Size};
use crate::math::{
- alignments, scaled_font_size, style_cramped, style_for_subscript, AlignmentResult,
- FrameFragment, GlyphFragment, LayoutMath, LeftRightAlternator, MathContext, MathRun,
- Scaled,
+ alignments, scaled_font_size, style_cramped, style_for_subscript,
+ style_for_superscript, AlignmentResult, FrameFragment, GlyphFragment, LayoutMath,
+ LeftRightAlternator, MathContext, MathRun, Scaled,
};
use crate::syntax::Span;
use crate::text::TextElem;
@@ -12,11 +12,13 @@ use crate::visualize::{FixedStroke, Geometry};
const BRACE_GAP: Em = Em::new(0.25);
const BRACKET_GAP: Em = Em::new(0.25);
+const PAREN_GAP: Em = Em::new(0.25);
+const SHELL_GAP: Em = Em::new(0.25);
/// A marker to distinguish under- vs. overlines.
-enum LineKind {
- Over,
+enum Position {
Under,
+ Over,
}
/// A horizontal line under content.
@@ -34,7 +36,7 @@ pub struct UnderlineElem {
impl LayoutMath for Packed<UnderlineElem> {
#[typst_macros::time(name = "math.underline", span = self.span())]
fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
- layout_underoverline(ctx, styles, self.body(), self.span(), LineKind::Under)
+ layout_underoverline(ctx, styles, self.body(), self.span(), Position::Under)
}
}
@@ -53,7 +55,7 @@ pub struct OverlineElem {
impl LayoutMath for Packed<OverlineElem> {
#[typst_macros::time(name = "math.overline", span = self.span())]
fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
- layout_underoverline(ctx, styles, self.body(), self.span(), LineKind::Over)
+ layout_underoverline(ctx, styles, self.body(), self.span(), Position::Over)
}
}
@@ -63,11 +65,11 @@ fn layout_underoverline(
styles: StyleChain,
body: &Content,
span: Span,
- line: LineKind,
+ position: Position,
) -> SourceResult<()> {
let (extra_height, content, line_pos, content_pos, baseline, bar_height);
- match line {
- LineKind::Under => {
+ match position {
+ Position::Under => {
let sep = scaled!(ctx, styles, underbar_extra_descender);
bar_height = scaled!(ctx, styles, underbar_rule_thickness);
let gap = scaled!(ctx, styles, underbar_vertical_gap);
@@ -79,7 +81,7 @@ fn layout_underoverline(
content_pos = Point::zero();
baseline = content.ascent()
}
- LineKind::Over => {
+ Position::Over => {
let sep = scaled!(ctx, styles, overbar_extra_ascender);
bar_height = scaled!(ctx, styles, overbar_rule_thickness);
let gap = scaled!(ctx, styles, overbar_vertical_gap);
@@ -145,7 +147,7 @@ impl LayoutMath for Packed<UnderbraceElem> {
&self.annotation(styles),
'⏟',
BRACE_GAP,
- false,
+ Position::Under,
self.span(),
)
}
@@ -177,7 +179,7 @@ impl LayoutMath for Packed<OverbraceElem> {
&self.annotation(styles),
'⏞',
BRACE_GAP,
- true,
+ Position::Over,
self.span(),
)
}
@@ -200,7 +202,7 @@ pub struct UnderbracketElem {
}
impl LayoutMath for Packed<UnderbracketElem> {
- #[typst_macros::time(name = "math.underbrace", span = self.span())]
+ #[typst_macros::time(name = "math.underbracket", span = self.span())]
fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
layout_underoverspreader(
ctx,
@@ -209,7 +211,7 @@ impl LayoutMath for Packed<UnderbracketElem> {
&self.annotation(styles),
'⎵',
BRACKET_GAP,
- false,
+ Position::Under,
self.span(),
)
}
@@ -241,7 +243,135 @@ impl LayoutMath for Packed<OverbracketElem> {
&self.annotation(styles),
'⎴',
BRACKET_GAP,
- true,
+ Position::Over,
+ self.span(),
+ )
+ }
+}
+
+/// A horizontal parenthesis under content, with an optional annotation below.
+///
+/// ```example
+/// $ underparen(1 + 2 + ... + 5, "numbers") $
+/// ```
+#[elem(LayoutMath)]
+pub struct UnderparenElem {
+ /// The content above the parenthesis.
+ #[required]
+ pub body: Content,
+
+ /// The optional content below the parenthesis.
+ #[positional]
+ pub annotation: Option<Content>,
+}
+
+impl LayoutMath for Packed<UnderparenElem> {
+ #[typst_macros::time(name = "math.underparen", span = self.span())]
+ fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
+ layout_underoverspreader(
+ ctx,
+ styles,
+ self.body(),
+ &self.annotation(styles),
+ '⏝',
+ PAREN_GAP,
+ Position::Under,
+ self.span(),
+ )
+ }
+}
+
+/// A horizontal parenthesis over content, with an optional annotation above.
+///
+/// ```example
+/// $ overparen(1 + 2 + ... + 5, "numbers") $
+/// ```
+#[elem(LayoutMath)]
+pub struct OverparenElem {
+ /// The content below the parenthesis.
+ #[required]
+ pub body: Content,
+
+ /// The optional content above the parenthesis.
+ #[positional]
+ pub annotation: Option<Content>,
+}
+
+impl LayoutMath for Packed<OverparenElem> {
+ #[typst_macros::time(name = "math.overparen", span = self.span())]
+ fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
+ layout_underoverspreader(
+ ctx,
+ styles,
+ self.body(),
+ &self.annotation(styles),
+ '⏜',
+ PAREN_GAP,
+ Position::Over,
+ self.span(),
+ )
+ }
+}
+
+/// A horizontal tortoise shell bracket under content, with an optional annotation below.
+///
+/// ```example
+/// $ undershell(1 + 2 + ... + 5, "numbers") $
+/// ```
+#[elem(LayoutMath)]
+pub struct UndershellElem {
+ /// The content above the tortoise shell bracket.
+ #[required]
+ pub body: Content,
+
+ /// The optional content below the tortoise shell bracket.
+ #[positional]
+ pub annotation: Option<Content>,
+}
+
+impl LayoutMath for Packed<UndershellElem> {
+ #[typst_macros::time(name = "math.undershell", span = self.span())]
+ fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
+ layout_underoverspreader(
+ ctx,
+ styles,
+ self.body(),
+ &self.annotation(styles),
+ '⏡',
+ SHELL_GAP,
+ Position::Under,
+ self.span(),
+ )
+ }
+}
+
+/// A horizontal tortoise shell bracket over content, with an optional annotation above.
+///
+/// ```example
+/// $ overshell(1 + 2 + ... + 5, "numbers") $
+/// ```
+#[elem(LayoutMath)]
+pub struct OvershellElem {
+ /// The content below the tortoise shell bracket.
+ #[required]
+ pub body: Content,
+
+ /// The optional content above the tortoise shell bracket.
+ #[positional]
+ pub annotation: Option<Content>,
+}
+
+impl LayoutMath for Packed<OvershellElem> {
+ #[typst_macros::time(name = "math.overshell", span = self.span())]
+ fn layout_math(&self, ctx: &mut MathContext, styles: StyleChain) -> SourceResult<()> {
+ layout_underoverspreader(
+ ctx,
+ styles,
+ self.body(),
+ &self.annotation(styles),
+ '⏠',
+ SHELL_GAP,
+ Position::Over,
self.span(),
)
}
@@ -256,7 +386,7 @@ fn layout_underoverspreader(
annotation: &Option<Content>,
c: char,
gap: Em,
- reverse: bool,
+ position: Position,
span: Span,
) -> SourceResult<()> {
let font_size = scaled_font_size(ctx, styles);
@@ -267,30 +397,30 @@ fn layout_underoverspreader(
let glyph = GlyphFragment::new(ctx, styles, c, span);
let stretched = glyph.stretch_horizontal(ctx, body.width(), Abs::zero());
- let mut rows = vec![MathRun::new(vec![body]), stretched.into()];
-
- let (sup_style, sub_style);
- let row_styles = if reverse {
- sup_style = style_for_subscript(styles);
- styles.chain(&sup_style)
- } else {
- sub_style = style_for_subscript(styles);
- styles.chain(&sub_style)
+ let mut rows = vec![];
+ let baseline = match position {
+ Position::Under => {
+ rows.push(MathRun::new(vec![body]));
+ rows.push(stretched.into());
+ if let Some(annotation) = annotation {
+ let under_style = style_for_subscript(styles);
+ let annotation_styles = styles.chain(&under_style);
+ rows.push(ctx.layout_into_run(annotation, annotation_styles)?);
+ }
+ 0
+ }
+ Position::Over => {
+ if let Some(annotation) = annotation {
+ let over_style = style_for_superscript(styles);
+ let annotation_styles = styles.chain(&over_style);
+ rows.push(ctx.layout_into_run(annotation, annotation_styles)?);
+ }
+ rows.push(stretched.into());
+ rows.push(MathRun::new(vec![body]));
+ rows.len() - 1
+ }
};
- rows.extend(
- annotation
- .as_ref()
- .map(|annotation| ctx.layout_into_run(annotation, row_styles))
- .transpose()?,
- );
-
- let mut baseline = 0;
- if reverse {
- rows.reverse();
- baseline = rows.len() - 1;
- }
-
let frame = stack(
rows,
FixedAlignment::Center,
diff --git a/docs/reference/groups.yml b/docs/reference/groups.yml
index 0cbf656a..f957051c 100644
--- a/docs/reference/groups.yml
+++ b/docs/reference/groups.yml
@@ -46,6 +46,10 @@
"overbrace",
"underbracket",
"overbracket",
+ "underparen",
+ "overparen",
+ "undershell",
+ "overshell",
]
details: |
Delimiters above or below parts of an equation.
diff --git a/tests/ref/math-underover-parens.png b/tests/ref/math-underover-parens.png
new file mode 100644
index 00000000..a461ee07
--- /dev/null
+++ b/tests/ref/math-underover-parens.png
Binary files differ
diff --git a/tests/ref/math-underover-shells.png b/tests/ref/math-underover-shells.png
new file mode 100644
index 00000000..fb327af6
--- /dev/null
+++ b/tests/ref/math-underover-shells.png
Binary files differ
diff --git a/tests/suite/math/underover.typ b/tests/suite/math/underover.typ
index 0768bf73..5749d9fa 100644
--- a/tests/suite/math/underover.typ
+++ b/tests/suite/math/underover.typ
@@ -19,3 +19,17 @@ $ x = overbracket(
$ underbracket([1, 2/3], "relevant stuff")
arrow.l.r.double.long
overbracket([4/5,6], "irrelevant stuff") $
+
+--- math-underover-parens ---
+// Test parentheses.
+$ overparen(
+ underparen(x + y, "long comment"),
+ 1 + 2 + ... + 5
+) $
+
+--- math-underover-shells ---
+// Test tortoise shell brackets.
+$ undershell(
+ 1 + overshell(2 + ..., x + y),
+ "all stuff"
+) $