summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMalo <57839069+MDLC01@users.noreply.github.com>2024-10-07 12:57:20 +0200
committerLaurenz <laurmaedje@gmail.com>2024-10-15 15:06:36 +0200
commita73d4327967860ea846616b91dc10738f01d7f65 (patch)
treea48941dbe5d02f1165c258ff1d60f12a64115a30
parent0e5144f9bafc54fd619cd36337907ecfb8c11bf4 (diff)
Refactor sub- and superscript substitution (#5120)
-rw-r--r--crates/typst/src/text/shift.rs139
1 files changed, 60 insertions, 79 deletions
diff --git a/crates/typst/src/text/shift.rs b/crates/typst/src/text/shift.rs
index 0029a303..003ecf47 100644
--- a/crates/typst/src/text/shift.rs
+++ b/crates/typst/src/text/shift.rs
@@ -51,19 +51,18 @@ impl Show for Packed<SubElem> {
#[typst_macros::time(name = "sub", span = self.span())]
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
let body = self.body().clone();
- let mut transformed = None;
+
if self.typographic(styles) {
- if let Some(text) = search_text(&body, true) {
+ if let Some(text) = convert_script(&body, true) {
if is_shapable(engine, &text, styles) {
- transformed = Some(TextElem::packed(text));
+ return Ok(TextElem::packed(text));
}
}
};
- Ok(transformed.unwrap_or_else(|| {
- body.styled(TextElem::set_baseline(self.baseline(styles)))
- .styled(TextElem::set_size(self.size(styles)))
- }))
+ Ok(body
+ .styled(TextElem::set_baseline(self.baseline(styles)))
+ .styled(TextElem::set_size(self.size(styles))))
}
}
@@ -111,38 +110,38 @@ impl Show for Packed<SuperElem> {
#[typst_macros::time(name = "super", span = self.span())]
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
let body = self.body().clone();
- let mut transformed = None;
+
if self.typographic(styles) {
- if let Some(text) = search_text(&body, false) {
+ if let Some(text) = convert_script(&body, false) {
if is_shapable(engine, &text, styles) {
- transformed = Some(TextElem::packed(text));
+ return Ok(TextElem::packed(text));
}
}
};
- Ok(transformed.unwrap_or_else(|| {
- body.styled(TextElem::set_baseline(self.baseline(styles)))
- .styled(TextElem::set_size(self.size(styles)))
- }))
+ Ok(body
+ .styled(TextElem::set_baseline(self.baseline(styles)))
+ .styled(TextElem::set_size(self.size(styles))))
}
}
/// Find and transform the text contained in `content` to the given script kind
/// if and only if it only consists of `Text`, `Space`, and `Empty` leaves.
-fn search_text(content: &Content, sub: bool) -> Option<EcoString> {
+fn convert_script(content: &Content, sub: bool) -> Option<EcoString> {
if content.is::<SpaceElem>() {
Some(' '.into())
} else if let Some(elem) = content.to_packed::<TextElem>() {
- convert_script(elem.text(), sub)
- } else if let Some(sequence) = content.to_packed::<SequenceElem>() {
- let mut full = EcoString::new();
- for item in &sequence.children {
- match search_text(item, sub) {
- Some(text) => full.push_str(&text),
- None => return None,
- }
+ if sub {
+ elem.text().chars().map(to_subscript_codepoint).collect()
+ } else {
+ elem.text().chars().map(to_superscript_codepoint).collect()
}
- Some(full)
+ } else if let Some(sequence) = content.to_packed::<SequenceElem>() {
+ sequence
+ .children
+ .iter()
+ .map(|item| convert_script(item, sub))
+ .collect()
} else {
None
}
@@ -165,65 +164,47 @@ fn is_shapable(engine: &Engine, text: &str, styles: StyleChain) -> bool {
false
}
-/// Convert a string to sub- or superscript codepoints if all characters
-/// can be mapped to such a codepoint.
-fn convert_script(text: &str, sub: bool) -> Option<EcoString> {
- let mut result = EcoString::with_capacity(text.len());
- let converter = if sub { to_subscript_codepoint } else { to_superscript_codepoint };
-
- for c in text.chars() {
- match converter(c) {
- Some(c) => result.push(c),
- None => return None,
- }
- }
-
- Some(result)
-}
-
/// Convert a character to its corresponding Unicode superscript.
fn to_superscript_codepoint(c: char) -> Option<char> {
- char::from_u32(match c {
- '0' => 0x2070,
- '1' => 0x00B9,
- '2' => 0x00B2,
- '3' => 0x00B3,
- '4'..='9' => 0x2070 + (c as u32 + 4 - '4' as u32),
- '+' => 0x207A,
- '-' => 0x207B,
- '=' => 0x207C,
- '(' => 0x207D,
- ')' => 0x207E,
- 'n' => 0x207F,
- 'i' => 0x2071,
- ' ' => 0x0020,
- _ => return None,
- })
+ match c {
+ '1' => Some('¹'),
+ '2' => Some('²'),
+ '3' => Some('³'),
+ '0' | '4'..='9' => char::from_u32(c as u32 - '0' as u32 + '⁰' as u32),
+ '+' => Some('⁺'),
+ '−' => Some('⁻'),
+ '=' => Some('⁼'),
+ '(' => Some('⁽'),
+ ')' => Some('⁾'),
+ 'n' => Some('ⁿ'),
+ 'i' => Some('ⁱ'),
+ ' ' => Some(' '),
+ _ => None,
+ }
}
/// Convert a character to its corresponding Unicode subscript.
fn to_subscript_codepoint(c: char) -> Option<char> {
- char::from_u32(match c {
- '0' => 0x2080,
- '1'..='9' => 0x2080 + (c as u32 - '0' as u32),
- '+' => 0x208A,
- '-' => 0x208B,
- '=' => 0x208C,
- '(' => 0x208D,
- ')' => 0x208E,
- 'a' => 0x2090,
- 'e' => 0x2091,
- 'o' => 0x2092,
- 'x' => 0x2093,
- 'h' => 0x2095,
- 'k' => 0x2096,
- 'l' => 0x2097,
- 'm' => 0x2098,
- 'n' => 0x2099,
- 'p' => 0x209A,
- 's' => 0x209B,
- 't' => 0x209C,
- ' ' => 0x0020,
- _ => return None,
- })
+ match c {
+ '0'..='9' => char::from_u32(c as u32 - '0' as u32 + '₀' as u32),
+ '+' => Some('₊'),
+ '−' => Some('₋'),
+ '=' => Some('₌'),
+ '(' => Some('₍'),
+ ')' => Some('₎'),
+ 'a' => Some('ₐ'),
+ 'e' => Some('ₑ'),
+ 'o' => Some('ₒ'),
+ 'x' => Some('ₓ'),
+ 'h' => Some('ₕ'),
+ 'k' => Some('ₖ'),
+ 'l' => Some('ₗ'),
+ 'm' => Some('ₘ'),
+ 'n' => Some('ₙ'),
+ 'p' => Some('ₚ'),
+ 's' => Some('ₛ'),
+ 't' => Some('ₜ'),
+ ' ' => Some(' '),
+ _ => None,
+ }
}