summaryrefslogtreecommitdiff
path: root/library/src
diff options
context:
space:
mode:
Diffstat (limited to 'library/src')
-rw-r--r--library/src/lib.rs12
-rw-r--r--library/src/math/matrix.rs84
-rw-r--r--library/src/math/mod.rs186
-rw-r--r--library/src/math/style.rs161
-rw-r--r--library/src/math/tex.rs4
5 files changed, 386 insertions, 61 deletions
diff --git a/library/src/lib.rs b/library/src/lib.rs
index 29a6cc94..e41e7c0d 100644
--- a/library/src/lib.rs
+++ b/library/src/lib.rs
@@ -51,10 +51,22 @@ fn scope() -> Scope {
// Math.
std.def_node::<math::MathNode>("math");
std.def_node::<math::AtomNode>("atom");
+ std.def_node::<math::AccNode>("acc");
std.def_node::<math::FracNode>("frac");
+ std.def_node::<math::BinomNode>("binom");
std.def_node::<math::SqrtNode>("sqrt");
+ std.def_node::<math::FloorNode>("floor");
+ std.def_node::<math::CeilNode>("ceil");
std.def_node::<math::VecNode>("vec");
std.def_node::<math::CasesNode>("cases");
+ std.def_node::<math::SerifNode>("serif");
+ std.def_node::<math::SansNode>("sans");
+ std.def_node::<math::BoldNode>("bold");
+ std.def_node::<math::ItalNode>("ital");
+ std.def_node::<math::CalNode>("cal");
+ std.def_node::<math::FrakNode>("frak");
+ std.def_node::<math::MonoNode>("mono");
+ std.def_node::<math::BbNode>("bb");
// Layout.
std.def_node::<layout::PageNode>("page");
diff --git a/library/src/math/matrix.rs b/library/src/math/matrix.rs
new file mode 100644
index 00000000..d835b348
--- /dev/null
+++ b/library/src/math/matrix.rs
@@ -0,0 +1,84 @@
+use super::*;
+
+/// A column vector in a mathematical formula.
+#[derive(Debug, Hash)]
+pub struct VecNode(Vec<Content>);
+
+#[node(Texify)]
+impl VecNode {
+ /// The kind of delimiter.
+ pub const DELIM: Delimiter = Delimiter::Paren;
+
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ Ok(Self(args.all()?).pack())
+ }
+}
+
+impl Texify for VecNode {
+ fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
+ let kind = match t.styles.get(Self::DELIM) {
+ Delimiter::Paren => "pmatrix",
+ Delimiter::Bracket => "bmatrix",
+ Delimiter::Brace => "Bmatrix",
+ Delimiter::Bar => "vmatrix",
+ };
+
+ t.push_str("\\begin{");
+ t.push_str(kind);
+ t.push_str("}");
+
+ for component in &self.0 {
+ component.texify(t)?;
+ t.push_str("\\\\");
+ }
+ t.push_str("\\end{");
+ t.push_str(kind);
+ t.push_str("}");
+
+ Ok(())
+ }
+}
+
+/// A vector / matrix delimiter.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum Delimiter {
+ Paren,
+ Bracket,
+ Brace,
+ Bar,
+}
+
+castable! {
+ Delimiter,
+ Expected: "type of bracket or bar",
+ Value::Str(s) => match s.as_str() {
+ "(" => Self::Paren,
+ "[" => Self::Bracket,
+ "{" => Self::Brace,
+ "|" => Self::Bar,
+ _ => Err("expected \"(\", \"[\", \"{\", or \"|\"")?,
+ },
+}
+
+/// A case distinction in a mathematical formula.
+#[derive(Debug, Hash)]
+pub struct CasesNode(Vec<Content>);
+
+#[node(Texify)]
+impl CasesNode {
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ Ok(Self(args.all()?).pack())
+ }
+}
+
+impl Texify for CasesNode {
+ fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
+ t.push_str("\\begin{cases}");
+ for component in &self.0 {
+ component.texify(t)?;
+ t.push_str("\\\\");
+ }
+ t.push_str("\\end{cases}");
+ Ok(())
+ }
+}
diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs
index c613ea2a..4eb72e23 100644
--- a/library/src/math/mod.rs
+++ b/library/src/math/mod.rs
@@ -1,7 +1,12 @@
//! Mathematical formulas.
+mod matrix;
+mod style;
mod tex;
+pub use self::matrix::*;
+pub use self::style::*;
+
use typst::model::{Guard, SequenceNode};
use unicode_segmentation::UnicodeSegmentation;
@@ -272,6 +277,79 @@ impl Texify for AtomNode {
}
}
+/// An accented node.
+#[derive(Debug, Hash)]
+pub struct AccNode {
+ /// The accent base.
+ pub base: Content,
+ /// The Unicode accent character.
+ pub accent: char,
+}
+
+#[node(Texify)]
+impl AccNode {
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ let base = args.expect("base")?;
+ let Spanned { v, span } = args.expect::<Spanned<Content>>("accent")?;
+ let accent = match extract(&v) {
+ Some(Ok(c)) => c,
+ Some(Err(msg)) => bail!(span, "{}", msg),
+ None => bail!(span, "not an accent"),
+ };
+ Ok(Self { base, accent }.pack())
+ }
+}
+
+#[rustfmt::skip]
+fn extract(content: &Content) -> Option<Result<char, &'static str>> {
+ let MathNode { children, .. } = content.to::<MathNode>()?;
+ let [child] = children.as_slice() else { return None };
+ let c = if let Some(atom) = child.to::<AtomNode>() {
+ let mut chars = atom.0.chars();
+ chars.next().filter(|_| chars.next().is_none())?
+ } else if let Some(symbol) = child.to::<SymbolNode>() {
+ match symmie::get(&symbol.0) {
+ Some(c) => c,
+ None => return Some(Err("unknown symbol")),
+ }
+ } else {
+ return None;
+ };
+
+ Some(Ok(match c {
+ '`' | '\u{300}' => '\u{300}', // Grave
+ '´' | '\u{301}' => '\u{301}', // Acute
+ '^' | '\u{302}' => '\u{302}', // Circumflex
+ '~' | '\u{223C}' | '\u{303}' => '\u{303}', // Tilde
+ '¯' | '\u{304}' => '\u{304}', // Macron
+ '‾' | '\u{305}' => '\u{305}', // Overline
+ '˘' | '\u{306}' => '\u{306}', // Breve
+ '.' | '\u{22C5}' | '\u{307}' => '\u{307}', // Dot
+ '¨' | '\u{308}' => '\u{308}', // Diaeresis
+ 'ˇ' | '\u{30C}' => '\u{30C}', // Caron
+ '→' | '\u{20D7}' => '\u{20D7}', // Arrow
+ _ => return None,
+ }))
+}
+
+impl Texify for AccNode {
+ fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
+ if let Some(sym) = unicode_math::SYMBOLS.iter().find(|sym| {
+ sym.codepoint == self.accent
+ && sym.atom_type == unicode_math::AtomType::Accent
+ }) {
+ t.push_str("\\");
+ t.push_str(sym.name);
+ t.push_str("{");
+ self.base.texify(t)?;
+ t.push_str("}");
+ } else {
+ self.base.texify(t)?;
+ }
+ Ok(())
+ }
+}
+
/// A fraction in a mathematical formula.
#[derive(Debug, Hash)]
pub struct FracNode {
@@ -301,6 +379,35 @@ impl Texify for FracNode {
}
}
+/// A binomial in a mathematical formula.
+#[derive(Debug, Hash)]
+pub struct BinomNode {
+ /// The upper index.
+ pub upper: Content,
+ /// The lower index.
+ pub lower: Content,
+}
+
+#[node(Texify)]
+impl BinomNode {
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ let upper = args.expect("upper index")?;
+ let lower = args.expect("lower index")?;
+ Ok(Self { upper, lower }.pack())
+ }
+}
+
+impl Texify for BinomNode {
+ fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
+ t.push_str("\\binom{");
+ self.upper.texify(t)?;
+ t.push_str("}{");
+ self.lower.texify(t)?;
+ t.push_str("}");
+ Ok(())
+ }
+}
+
/// A sub- and/or superscript in a mathematical formula.
#[derive(Debug, Hash)]
pub struct ScriptNode {
@@ -348,7 +455,7 @@ impl Texify for AlignNode {
}
}
-/// A square root.
+/// A square root in a mathematical formula.
#[derive(Debug, Hash)]
pub struct SqrtNode(Content);
@@ -362,91 +469,48 @@ impl SqrtNode {
impl Texify for SqrtNode {
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
t.push_str("\\sqrt{");
- self.0.texify_unparen(t)?;
+ self.0.texify(t)?;
t.push_str("}");
Ok(())
}
}
-/// A column vector.
+/// A floored expression in a mathematical formula.
#[derive(Debug, Hash)]
-pub struct VecNode(Vec<Content>);
+pub struct FloorNode(Content);
#[node(Texify)]
-impl VecNode {
- /// The kind of delimiter.
- pub const DELIM: Delimiter = Delimiter::Paren;
-
+impl FloorNode {
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.all()?).pack())
+ Ok(Self(args.expect("body")?).pack())
}
}
-impl Texify for VecNode {
+impl Texify for FloorNode {
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
- let kind = match t.styles.get(Self::DELIM) {
- Delimiter::Paren => "pmatrix",
- Delimiter::Bracket => "bmatrix",
- Delimiter::Brace => "Bmatrix",
- Delimiter::Bar => "vmatrix",
- };
-
- t.push_str("\\begin{");
- t.push_str(kind);
- t.push_str("}");
-
- for component in &self.0 {
- component.texify_unparen(t)?;
- t.push_str("\\\\");
- }
- t.push_str("\\end{");
- t.push_str(kind);
- t.push_str("}");
-
+ t.push_str("\\left\\lfloor ");
+ self.0.texify(t)?;
+ t.push_str("\\right\\rfloor ");
Ok(())
}
}
-/// A vector / matrix delimiter.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Delimiter {
- Paren,
- Bracket,
- Brace,
- Bar,
-}
-
-castable! {
- Delimiter,
- Expected: "type of bracket or bar",
- Value::Str(s) => match s.as_str() {
- "(" => Self::Paren,
- "[" => Self::Bracket,
- "{" => Self::Brace,
- "|" => Self::Bar,
- _ => Err("expected \"(\", \"[\", \"{\", or \"|\"")?,
- },
-}
-
-/// A case distinction.
+/// A ceiled expression in a mathematical formula.
#[derive(Debug, Hash)]
-pub struct CasesNode(Vec<Content>);
+pub struct CeilNode(Content);
#[node(Texify)]
-impl CasesNode {
+impl CeilNode {
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- Ok(Self(args.all()?).pack())
+ Ok(Self(args.expect("body")?).pack())
}
}
-impl Texify for CasesNode {
+impl Texify for CeilNode {
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
- t.push_str("\\begin{cases}");
- for component in &self.0 {
- component.texify_unparen(t)?;
- t.push_str("\\\\");
- }
- t.push_str("\\end{cases}");
+ t.push_str("\\left\\lceil ");
+ self.0.texify(t)?;
+ t.push_str("\\right\\rceil ");
Ok(())
}
}
diff --git a/library/src/math/style.rs b/library/src/math/style.rs
new file mode 100644
index 00000000..3db2e631
--- /dev/null
+++ b/library/src/math/style.rs
@@ -0,0 +1,161 @@
+use super::*;
+
+/// Serif (roman) font style.
+#[derive(Debug, Hash)]
+pub struct SerifNode(Content);
+
+#[node(Texify)]
+impl SerifNode {
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ Ok(Self(args.expect("body")?).pack())
+ }
+}
+
+impl Texify for SerifNode {
+ fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
+ t.push_str("\\mathrm{");
+ self.0.texify_unparen(t)?;
+ t.push_str("}");
+ Ok(())
+ }
+}
+
+/// Sans-serif font style.
+#[derive(Debug, Hash)]
+pub struct SansNode(Content);
+
+#[node(Texify)]
+impl SansNode {
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ Ok(Self(args.expect("body")?).pack())
+ }
+}
+
+impl Texify for SansNode {
+ fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
+ t.push_str("\\mathsf{");
+ self.0.texify_unparen(t)?;
+ t.push_str("}");
+ Ok(())
+ }
+}
+
+/// Bold font style.
+#[derive(Debug, Hash)]
+pub struct BoldNode(Content);
+
+#[node(Texify)]
+impl BoldNode {
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ Ok(Self(args.expect("body")?).pack())
+ }
+}
+
+impl Texify for BoldNode {
+ fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
+ t.push_str("\\mathbf{");
+ self.0.texify_unparen(t)?;
+ t.push_str("}");
+ Ok(())
+ }
+}
+
+/// Italic font style.
+#[derive(Debug, Hash)]
+pub struct ItalNode(Content);
+
+#[node(Texify)]
+impl ItalNode {
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ Ok(Self(args.expect("body")?).pack())
+ }
+}
+
+impl Texify for ItalNode {
+ fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
+ t.push_str("\\mathit{");
+ self.0.texify_unparen(t)?;
+ t.push_str("}");
+ Ok(())
+ }
+}
+
+/// Calligraphic font style.
+#[derive(Debug, Hash)]
+pub struct CalNode(Content);
+
+#[node(Texify)]
+impl CalNode {
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ Ok(Self(args.expect("body")?).pack())
+ }
+}
+
+impl Texify for CalNode {
+ fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
+ t.push_str("\\mathcal{");
+ self.0.texify_unparen(t)?;
+ t.push_str("}");
+ Ok(())
+ }
+}
+
+/// Fraktur font style.
+#[derive(Debug, Hash)]
+pub struct FrakNode(Content);
+
+#[node(Texify)]
+impl FrakNode {
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ Ok(Self(args.expect("body")?).pack())
+ }
+}
+
+impl Texify for FrakNode {
+ fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
+ t.push_str("\\mathfrak{");
+ self.0.texify_unparen(t)?;
+ t.push_str("}");
+ Ok(())
+ }
+}
+
+/// Monospace font style.
+#[derive(Debug, Hash)]
+pub struct MonoNode(Content);
+
+#[node(Texify)]
+impl MonoNode {
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ Ok(Self(args.expect("body")?).pack())
+ }
+}
+
+impl Texify for MonoNode {
+ fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
+ t.push_str("\\mathtt{");
+ self.0.texify_unparen(t)?;
+ t.push_str("}");
+ Ok(())
+ }
+}
+
+/// Blackboard bold (double-struck) font style.
+#[derive(Debug, Hash)]
+pub struct BbNode(Content);
+
+#[node(Texify)]
+impl BbNode {
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ Ok(Self(args.expect("body")?).pack())
+ }
+}
+
+impl Texify for BbNode {
+ fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
+ t.push_str("\\mathbb{");
+ self.0.texify_unparen(t)?;
+ t.push_str("}");
+ Ok(())
+ }
+}
diff --git a/library/src/math/tex.rs b/library/src/math/tex.rs
index e8917f30..f17134b7 100644
--- a/library/src/math/tex.rs
+++ b/library/src/math/tex.rs
@@ -125,6 +125,10 @@ impl Backend for FrameBackend {
}
fn rule(&mut self, pos: Cursor, width: f64, height: f64) {
+ if height == 0.0 {
+ return;
+ }
+
self.frame.push(
self.transform(pos) + Point::with_y(Abs::pt(height) / 2.0),
Element::Shape(Shape {