summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--crates/typst-layout/src/math/fragment.rs9
-rw-r--r--crates/typst-library/src/math/matrix.rs6
-rw-r--r--crates/typst-syntax/src/parser.rs3
-rw-r--r--crates/typst-utils/Cargo.toml1
-rw-r--r--crates/typst-utils/src/lib.rs26
-rw-r--r--tests/ref/issue-4985-up-tack-is-normal-perp-is-relation.pngbin0 -> 360 bytes
-rw-r--r--tests/suite/math/class.typ5
8 files changed, 41 insertions, 10 deletions
diff --git a/Cargo.lock b/Cargo.lock
index ada3a3d4..d2e410e1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3106,6 +3106,7 @@ dependencies = [
"rayon",
"siphasher 1.0.1",
"thin-vec",
+ "unicode-math-class",
]
[[package]]
diff --git a/crates/typst-layout/src/math/fragment.rs b/crates/typst-layout/src/math/fragment.rs
index 81b726ba..1b508a34 100644
--- a/crates/typst-layout/src/math/fragment.rs
+++ b/crates/typst-layout/src/math/fragment.rs
@@ -13,6 +13,7 @@ use typst_library::math::{EquationElem, MathSize};
use typst_library::text::{Font, Glyph, Lang, Region, TextElem, TextItem};
use typst_library::visualize::Paint;
use typst_syntax::Span;
+use typst_utils::default_math_class;
use unicode_math_class::MathClass;
use super::{stretch_glyph, MathContext, Scaled};
@@ -275,11 +276,7 @@ impl GlyphFragment {
span: Span,
) -> Self {
let class = EquationElem::class_in(styles)
- .or_else(|| match c {
- ':' => Some(MathClass::Relation),
- '.' | '/' | '⋯' | '⋱' | '⋰' | '⋮' => Some(MathClass::Normal),
- _ => unicode_math_class::class(c),
- })
+ .or_else(|| default_math_class(c))
.unwrap_or(MathClass::Normal);
let mut fragment = Self {
@@ -629,7 +626,7 @@ pub enum Limits {
impl Limits {
/// The default limit configuration if the given character is the base.
pub fn for_char(c: char) -> Self {
- match unicode_math_class::class(c) {
+ match default_math_class(c) {
Some(MathClass::Large) => {
if is_integral_char(c) {
Limits::Never
diff --git a/crates/typst-library/src/math/matrix.rs b/crates/typst-library/src/math/matrix.rs
index c74eb8fa..b6c4654e 100644
--- a/crates/typst-library/src/math/matrix.rs
+++ b/crates/typst-library/src/math/matrix.rs
@@ -1,6 +1,6 @@
use smallvec::{smallvec, SmallVec};
use typst_syntax::Spanned;
-use typst_utils::Numeric;
+use typst_utils::{default_math_class, Numeric};
use unicode_math_class::MathClass;
use crate::diag::{bail, At, HintedStrResult, StrResult};
@@ -292,7 +292,7 @@ impl Delimiter {
pub fn char(c: char) -> StrResult<Self> {
if !matches!(
- unicode_math_class::class(c),
+ default_math_class(c),
Some(MathClass::Opening | MathClass::Closing | MathClass::Fence),
) {
bail!("invalid delimiter: \"{}\"", c)
@@ -311,7 +311,7 @@ impl Delimiter {
Some(']') => Self(Some('[')),
Some('{') => Self(Some('}')),
Some('}') => Self(Some('{')),
- Some(c) => match unicode_math_class::class(c) {
+ Some(c) => match default_math_class(c) {
Some(MathClass::Opening) => Self(char::from_u32(c as u32 + 1)),
Some(MathClass::Closing) => Self(char::from_u32(c as u32 - 1)),
_ => Self(Some(c)),
diff --git a/crates/typst-syntax/src/parser.rs b/crates/typst-syntax/src/parser.rs
index 55d5550b..e187212d 100644
--- a/crates/typst-syntax/src/parser.rs
+++ b/crates/typst-syntax/src/parser.rs
@@ -3,6 +3,7 @@ use std::mem;
use std::ops::{Index, IndexMut, Range};
use ecow::{eco_format, EcoString};
+use typst_utils::default_math_class;
use unicode_math_class::MathClass;
use crate::set::{syntax_set, SyntaxSet};
@@ -468,7 +469,7 @@ fn math_class(text: &str) -> Option<MathClass> {
chars
.next()
.filter(|_| chars.next().is_none())
- .and_then(unicode_math_class::class)
+ .and_then(default_math_class)
}
/// Parse an argument list in math: `(a, b; c, d; size: #50%)`.
diff --git a/crates/typst-utils/Cargo.toml b/crates/typst-utils/Cargo.toml
index 5f828cff..360e07d8 100644
--- a/crates/typst-utils/Cargo.toml
+++ b/crates/typst-utils/Cargo.toml
@@ -18,6 +18,7 @@ portable-atomic = { workspace = true }
rayon = { workspace = true }
siphasher = { workspace = true }
thin-vec = { workspace = true }
+unicode-math-class = { workspace = true }
[lints]
workspace = true
diff --git a/crates/typst-utils/src/lib.rs b/crates/typst-utils/src/lib.rs
index b59fe2f7..34d6a943 100644
--- a/crates/typst-utils/src/lib.rs
+++ b/crates/typst-utils/src/lib.rs
@@ -31,6 +31,7 @@ use std::ops::{Add, Deref, Div, Mul, Neg, Sub};
use std::sync::Arc;
use siphasher::sip128::{Hasher128, SipHasher13};
+use unicode_math_class::MathClass;
/// Turn a closure into a struct implementing [`Debug`].
pub fn debug<F>(f: F) -> impl Debug
@@ -337,3 +338,28 @@ pub trait Numeric:
/// Whether `self` consists only of finite parts.
fn is_finite(self) -> bool;
}
+
+/// Returns the default math class of a character in Typst, if it has one.
+///
+/// This is determined by the Unicode math class, with some manual overrides.
+pub fn default_math_class(c: char) -> Option<MathClass> {
+ match c {
+ // Better spacing.
+ // https://github.com/typst/typst/commit/2e039cb052fcb768027053cbf02ce396f6d7a6be
+ ':' => Some(MathClass::Relation),
+
+ // Better spacing when used alongside + PLUS SIGN.
+ // https://github.com/typst/typst/pull/1726
+ '⋯' | '⋱' | '⋰' | '⋮' => Some(MathClass::Normal),
+
+ // Better spacing.
+ // https://github.com/typst/typst/pull/1855
+ '.' | '/' => Some(MathClass::Normal),
+
+ // ⊥ UP TACK should not be a relation, contrary to ⟂ PERPENDICULAR.
+ // https://github.com/typst/typst/pull/5714
+ '\u{22A5}' => Some(MathClass::Normal),
+
+ c => unicode_math_class::class(c),
+ }
+}
diff --git a/tests/ref/issue-4985-up-tack-is-normal-perp-is-relation.png b/tests/ref/issue-4985-up-tack-is-normal-perp-is-relation.png
new file mode 100644
index 00000000..acadc3be
--- /dev/null
+++ b/tests/ref/issue-4985-up-tack-is-normal-perp-is-relation.png
Binary files differ
diff --git a/tests/suite/math/class.typ b/tests/suite/math/class.typ
index d25071db..9f014810 100644
--- a/tests/suite/math/class.typ
+++ b/tests/suite/math/class.typ
@@ -45,3 +45,8 @@ $class("large", ->)_a$
$limits(class("normal", ->))_a$
$ scripts(class("relation", x))_a $
+
+--- issue-4985-up-tack-is-normal-perp-is-relation ---
+$ top = 1 \
+ bot = 2 \
+ a perp b $