mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Extend math library
This commit is contained in:
parent
e1c0cda6c8
commit
11c7ceb29e
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1027,7 +1027,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "symmie"
|
name = "symmie"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/typst/symmie#8504bf7ec0d8996d160832c2724ba024ab6e988a"
|
source = "git+https://github.com/typst/symmie#6280fb20455cb63e6886ba5bb35b95a4b376da68"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
|
@ -51,10 +51,22 @@ fn scope() -> Scope {
|
|||||||
// Math.
|
// Math.
|
||||||
std.def_node::<math::MathNode>("math");
|
std.def_node::<math::MathNode>("math");
|
||||||
std.def_node::<math::AtomNode>("atom");
|
std.def_node::<math::AtomNode>("atom");
|
||||||
|
std.def_node::<math::AccNode>("acc");
|
||||||
std.def_node::<math::FracNode>("frac");
|
std.def_node::<math::FracNode>("frac");
|
||||||
|
std.def_node::<math::BinomNode>("binom");
|
||||||
std.def_node::<math::SqrtNode>("sqrt");
|
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::VecNode>("vec");
|
||||||
std.def_node::<math::CasesNode>("cases");
|
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.
|
// Layout.
|
||||||
std.def_node::<layout::PageNode>("page");
|
std.def_node::<layout::PageNode>("page");
|
||||||
|
84
library/src/math/matrix.rs
Normal file
84
library/src/math/matrix.rs
Normal file
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,12 @@
|
|||||||
//! Mathematical formulas.
|
//! Mathematical formulas.
|
||||||
|
|
||||||
|
mod matrix;
|
||||||
|
mod style;
|
||||||
mod tex;
|
mod tex;
|
||||||
|
|
||||||
|
pub use self::matrix::*;
|
||||||
|
pub use self::style::*;
|
||||||
|
|
||||||
use typst::model::{Guard, SequenceNode};
|
use typst::model::{Guard, SequenceNode};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
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.
|
/// A fraction in a mathematical formula.
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, Hash)]
|
||||||
pub struct FracNode {
|
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.
|
/// A sub- and/or superscript in a mathematical formula.
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, Hash)]
|
||||||
pub struct ScriptNode {
|
pub struct ScriptNode {
|
||||||
@ -348,7 +455,7 @@ impl Texify for AlignNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A square root.
|
/// A square root in a mathematical formula.
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, Hash)]
|
||||||
pub struct SqrtNode(Content);
|
pub struct SqrtNode(Content);
|
||||||
|
|
||||||
@ -362,91 +469,48 @@ impl SqrtNode {
|
|||||||
impl Texify for SqrtNode {
|
impl Texify for SqrtNode {
|
||||||
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
||||||
t.push_str("\\sqrt{");
|
t.push_str("\\sqrt{");
|
||||||
self.0.texify_unparen(t)?;
|
self.0.texify(t)?;
|
||||||
t.push_str("}");
|
t.push_str("}");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A column vector.
|
/// A floored expression in a mathematical formula.
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, Hash)]
|
||||||
pub struct VecNode(Vec<Content>);
|
pub struct FloorNode(Content);
|
||||||
|
|
||||||
#[node(Texify)]
|
#[node(Texify)]
|
||||||
impl VecNode {
|
impl FloorNode {
|
||||||
/// The kind of delimiter.
|
|
||||||
pub const DELIM: Delimiter = Delimiter::Paren;
|
|
||||||
|
|
||||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
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<()> {
|
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
||||||
let kind = match t.styles.get(Self::DELIM) {
|
t.push_str("\\left\\lfloor ");
|
||||||
Delimiter::Paren => "pmatrix",
|
self.0.texify(t)?;
|
||||||
Delimiter::Bracket => "bmatrix",
|
t.push_str("\\right\\rfloor ");
|
||||||
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("}");
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A vector / matrix delimiter.
|
/// A ceiled expression in a mathematical formula.
|
||||||
#[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.
|
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, Hash)]
|
||||||
pub struct CasesNode(Vec<Content>);
|
pub struct CeilNode(Content);
|
||||||
|
|
||||||
#[node(Texify)]
|
#[node(Texify)]
|
||||||
impl CasesNode {
|
impl CeilNode {
|
||||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
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<()> {
|
fn texify(&self, t: &mut Texifier) -> SourceResult<()> {
|
||||||
t.push_str("\\begin{cases}");
|
t.push_str("\\left\\lceil ");
|
||||||
for component in &self.0 {
|
self.0.texify(t)?;
|
||||||
component.texify_unparen(t)?;
|
t.push_str("\\right\\rceil ");
|
||||||
t.push_str("\\\\");
|
|
||||||
}
|
|
||||||
t.push_str("\\end{cases}");
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
161
library/src/math/style.rs
Normal file
161
library/src/math/style.rs
Normal file
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
@ -125,6 +125,10 @@ impl Backend for FrameBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn rule(&mut self, pos: Cursor, width: f64, height: f64) {
|
fn rule(&mut self, pos: Cursor, width: f64, height: f64) {
|
||||||
|
if height == 0.0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.frame.push(
|
self.frame.push(
|
||||||
self.transform(pos) + Point::with_y(Abs::pt(height) / 2.0),
|
self.transform(pos) + Point::with_y(Abs::pt(height) / 2.0),
|
||||||
Element::Shape(Shape {
|
Element::Shape(Shape {
|
||||||
|
@ -434,7 +434,7 @@ impl Eval for ast::MathNode {
|
|||||||
Self::Expr(v) => {
|
Self::Expr(v) => {
|
||||||
if let ast::Expr::Ident(ident) = v {
|
if let ast::Expr::Ident(ident) = v {
|
||||||
if self.as_untyped().len() == ident.len()
|
if self.as_untyped().len() == ident.len()
|
||||||
&& !vm.scopes.get(ident).is_ok()
|
&& matches!(vm.scopes.get(ident), Ok(Value::Func(_)) | Err(_))
|
||||||
{
|
{
|
||||||
let node = (vm.items.symbol)(ident.get().clone() + ":op".into());
|
let node = (vm.items.symbol)(ident.get().clone() + ":op".into());
|
||||||
return Ok(node.spanned(self.span()));
|
return Ok(node.spanned(self.span()));
|
||||||
|
@ -7,6 +7,10 @@ pub fn applicable(target: &Content, styles: StyleChain) -> bool {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if target.has::<dyn Show>() && target.is_pristine() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Find out how many recipes there are.
|
// Find out how many recipes there are.
|
||||||
let mut n = styles.recipes().count();
|
let mut n = styles.recipes().count();
|
||||||
|
|
||||||
|
BIN
tests/ref/math/accents.png
Normal file
BIN
tests/ref/math/accents.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 5.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 8.1 KiB |
BIN
tests/ref/math/style.png
Normal file
BIN
tests/ref/math/style.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
28
tests/typ/math/accents.typ
Normal file
28
tests/typ/math/accents.typ
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Test math accents.
|
||||||
|
|
||||||
|
---
|
||||||
|
#set page(width: auto)
|
||||||
|
|
||||||
|
$ acc(a,`),
|
||||||
|
acc(a,´),
|
||||||
|
acc(a,\^),
|
||||||
|
acc(a,~),
|
||||||
|
acc(a,¯),
|
||||||
|
acc(a,‾),
|
||||||
|
acc(a,˘),
|
||||||
|
acc(a,.),
|
||||||
|
acc(a,¨),
|
||||||
|
acc(a,ˇ),
|
||||||
|
acc(a,->) $
|
||||||
|
|
||||||
|
$ acc(a, grave),
|
||||||
|
acc(a, acute),
|
||||||
|
acc(a, circum),
|
||||||
|
acc(a, tilde),
|
||||||
|
acc(a, macron),
|
||||||
|
acc(a, overline),
|
||||||
|
acc(a, breve),
|
||||||
|
acc(a, dot),
|
||||||
|
acc(a, dia),
|
||||||
|
acc(a, caron),
|
||||||
|
acc(a, arrow) $
|
@ -4,12 +4,11 @@
|
|||||||
$ v = vec(1, 2+3, 4) $
|
$ v = vec(1, 2+3, 4) $
|
||||||
|
|
||||||
---
|
---
|
||||||
#set vec(delim: "|")
|
$ binom(n, 1) = 1/2 n (n-1) $
|
||||||
$ vec(1, 2) $
|
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 17-20 expected "(", "[", "{", or "|"
|
#set vec(delim: "|")
|
||||||
#set vec(delim: "%")
|
$ vec(1, 2) $
|
||||||
|
|
||||||
---
|
---
|
||||||
$ f(x, y) := cases(
|
$ f(x, y) := cases(
|
||||||
@ -18,3 +17,11 @@ $ f(x, y) := cases(
|
|||||||
3 "if" x "is even",
|
3 "if" x "is even",
|
||||||
4 "else",
|
4 "else",
|
||||||
) $
|
) $
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 17-20 expected "(", "[", "{", or "|"
|
||||||
|
#set vec(delim: "%")
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 9-12 missing argument: lower index
|
||||||
|
$ binom(x^2) $
|
||||||
|
@ -11,9 +11,13 @@ $ a^2 + b^2 = c^2 $
|
|||||||
Prove by induction:
|
Prove by induction:
|
||||||
$ sum_(k=0)^n k = (n(n+1))/2 $
|
$ sum_(k=0)^n k = (n(n+1))/2 $
|
||||||
|
|
||||||
|
---
|
||||||
|
We know that:
|
||||||
|
$ floor(x/2) <= ceil(x/2) $
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test that blackboard style looks nice.
|
// Test that blackboard style looks nice.
|
||||||
$ f: NN arrow RR $
|
$ f: NN -> RR $
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 1:3 expected dollar sign
|
// Error: 1:3 expected dollar sign
|
||||||
|
15
tests/typ/math/style.typ
Normal file
15
tests/typ/math/style.typ
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#let part = $ a b A B $
|
||||||
|
#let kinds = (serif, sans, cal, frak, mono, bb)
|
||||||
|
#let modifiers = (v => v, ital, bold, v => ital(bold(v)))
|
||||||
|
|
||||||
|
#let cells = ([:triangle:nested:], [--], [`ital`], [`bold`], [both])
|
||||||
|
#for k in kinds {
|
||||||
|
cells.push(raw(repr(k).trim("<function ").trim(">")))
|
||||||
|
for m in modifiers {
|
||||||
|
cells.push($ #m(#k(part)) $)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#set page(width: auto)
|
||||||
|
#set par(align: center)
|
||||||
|
#table(columns: 1 + modifiers.len(), ..cells)
|
Loading…
x
Reference in New Issue
Block a user