summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
author+merlan #flirora <uruwi@protonmail.com>2024-07-14 09:48:40 -0400
committerGitHub <noreply@github.com>2024-07-14 13:48:40 +0000
commit17ee3df1ba99183fc074e91dfba3e9189dae1c0c (patch)
treef2b9f3ba7e437dc176fe4056f4689e5f8a012595 /crates
parenta3f3a1a83330e3fe9a686fbe4c0eda9f1e9e99b2 (diff)
Wrap outline entry body in LRE/RLE + make smart quotes ignore directional control characters (#4491)
Co-authored-by: Laurenz <laurmaedje@gmail.com>
Diffstat (limited to 'crates')
-rw-r--r--crates/typst/src/layout/inline/collect.rs4
-rw-r--r--crates/typst/src/layout/inline/linebreak.rs5
-rw-r--r--crates/typst/src/layout/inline/mod.rs2
-rw-r--r--crates/typst/src/model/outline.rs8
-rw-r--r--crates/typst/src/text/mod.rs10
-rw-r--r--crates/typst/src/text/smartquote.rs6
6 files changed, 27 insertions, 8 deletions
diff --git a/crates/typst/src/layout/inline/collect.rs b/crates/typst/src/layout/inline/collect.rs
index f1607460..b6a847f5 100644
--- a/crates/typst/src/layout/inline/collect.rs
+++ b/crates/typst/src/layout/inline/collect.rs
@@ -201,7 +201,7 @@ pub fn collect<'a>(
);
let peeked = iter.peek().and_then(|(child, _)| {
if let Some(elem) = child.to_packed::<TextElem>() {
- elem.text().chars().next()
+ elem.text().chars().find(|c| !is_default_ignorable(*c))
} else if child.is::<SmartQuoteElem>() {
Some('"')
} else if child.is::<SpaceElem>()
@@ -302,7 +302,7 @@ impl<'a> Collector<'a> {
}
fn push_segment(&mut self, segment: Segment<'a>, is_quote: bool) {
- if let Some(last) = self.full.chars().last() {
+ if let Some(last) = self.full.chars().rev().find(|c| !is_default_ignorable(*c)) {
self.quoter.last(last, is_quote);
}
diff --git a/crates/typst/src/layout/inline/linebreak.rs b/crates/typst/src/layout/inline/linebreak.rs
index 9deaa92a..075d24b3 100644
--- a/crates/typst/src/layout/inline/linebreak.rs
+++ b/crates/typst/src/layout/inline/linebreak.rs
@@ -953,3 +953,8 @@ where
}
}
}
+
+/// Whether a codepoint is Unicode `Default_Ignorable`.
+pub fn is_default_ignorable(c: char) -> bool {
+ DEFAULT_IGNORABLE_DATA.as_borrowed().contains(c)
+}
diff --git a/crates/typst/src/layout/inline/mod.rs b/crates/typst/src/layout/inline/mod.rs
index f89de169..821b4f57 100644
--- a/crates/typst/src/layout/inline/mod.rs
+++ b/crates/typst/src/layout/inline/mod.rs
@@ -10,7 +10,7 @@ use comemo::{Track, Tracked, TrackedMut};
use self::collect::{collect, Item, Segment, SpanMapper};
use self::finalize::finalize;
use self::line::{commit, line, Line};
-use self::linebreak::{linebreak, Breakpoint};
+use self::linebreak::{is_default_ignorable, linebreak, Breakpoint};
use self::prepare::{prepare, Preparation};
use self::shaping::{
cjk_punct_style, is_of_cj_script, shape_range, ShapedGlyph, ShapedText,
diff --git a/crates/typst/src/model/outline.rs b/crates/typst/src/model/outline.rs
index 09047285..ec1e5f1b 100644
--- a/crates/typst/src/model/outline.rs
+++ b/crates/typst/src/model/outline.rs
@@ -483,7 +483,7 @@ impl OutlineEntry {
impl Show for Packed<OutlineEntry> {
#[typst_macros::time(name = "outline.entry", span = self.span())]
- fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
+ fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
let mut seq = vec![];
let elem = self.element();
@@ -500,7 +500,11 @@ impl Show for Packed<OutlineEntry> {
};
// The body text remains overridable.
- seq.push(self.body().clone().linked(Destination::Location(location)));
+ crate::text::isolate(
+ self.body().clone().linked(Destination::Location(location)),
+ styles,
+ &mut seq,
+ );
// Add filler symbols between the section name and page number.
if let Some(filler) = self.fill() {
diff --git a/crates/typst/src/text/mod.rs b/crates/typst/src/text/mod.rs
index 7648f08f..d42e4df8 100644
--- a/crates/typst/src/text/mod.rs
+++ b/crates/typst/src/text/mod.rs
@@ -1299,3 +1299,13 @@ cast! {
ret
},
}
+
+/// Pushes `text` wrapped in LRE/RLE + PDF to `out`.
+pub(crate) fn isolate(text: Content, styles: StyleChain, out: &mut Vec<Content>) {
+ out.push(TextElem::packed(match TextElem::dir_in(styles) {
+ Dir::RTL => "\u{202B}",
+ _ => "\u{202A}",
+ }));
+ out.push(text);
+ out.push(TextElem::packed("\u{202C}"));
+}
diff --git a/crates/typst/src/text/smartquote.rs b/crates/typst/src/text/smartquote.rs
index 236d0636..797f0804 100644
--- a/crates/typst/src/text/smartquote.rs
+++ b/crates/typst/src/text/smartquote.rs
@@ -123,7 +123,7 @@ impl SmartQuoter {
/// Process the last seen character.
pub fn last(&mut self, c: char, is_quote: bool) {
- self.expect_opening = is_ignorable(c) || is_opening_bracket(c);
+ self.expect_opening = is_exterior_to_quote(c) || is_opening_bracket(c);
self.last_num = c.is_numeric();
if !is_quote {
self.prev_quote_type = None;
@@ -150,7 +150,7 @@ impl SmartQuoter {
self.prev_quote_type = Some(double);
quotes.open(double)
} else if self.quote_depth > 0
- && (peeked.is_ascii_punctuation() || is_ignorable(peeked))
+ && (peeked.is_ascii_punctuation() || is_exterior_to_quote(peeked))
{
self.quote_depth -= 1;
quotes.close(double)
@@ -168,7 +168,7 @@ impl Default for SmartQuoter {
}
}
-fn is_ignorable(c: char) -> bool {
+fn is_exterior_to_quote(c: char) -> bool {
c.is_whitespace() || is_newline(c)
}