Wrap outline entry body in LRE/RLE + make smart quotes ignore directional control characters (#4491)

Co-authored-by: Laurenz <laurmaedje@gmail.com>
This commit is contained in:
+merlan #flirora 2024-07-14 09:48:40 -04:00 committed by GitHub
parent a3f3a1a833
commit 17ee3df1ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 39 additions and 8 deletions

View File

@ -201,7 +201,7 @@ pub fn collect<'a>(
); );
let peeked = iter.peek().and_then(|(child, _)| { let peeked = iter.peek().and_then(|(child, _)| {
if let Some(elem) = child.to_packed::<TextElem>() { 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>() { } else if child.is::<SmartQuoteElem>() {
Some('"') Some('"')
} else if child.is::<SpaceElem>() } else if child.is::<SpaceElem>()
@ -302,7 +302,7 @@ impl<'a> Collector<'a> {
} }
fn push_segment(&mut self, segment: Segment<'a>, is_quote: bool) { 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); self.quoter.last(last, is_quote);
} }

View File

@ -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)
}

View File

@ -10,7 +10,7 @@ use comemo::{Track, Tracked, TrackedMut};
use self::collect::{collect, Item, Segment, SpanMapper}; use self::collect::{collect, Item, Segment, SpanMapper};
use self::finalize::finalize; use self::finalize::finalize;
use self::line::{commit, line, Line}; 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::prepare::{prepare, Preparation};
use self::shaping::{ use self::shaping::{
cjk_punct_style, is_of_cj_script, shape_range, ShapedGlyph, ShapedText, cjk_punct_style, is_of_cj_script, shape_range, ShapedGlyph, ShapedText,

View File

@ -483,7 +483,7 @@ impl OutlineEntry {
impl Show for Packed<OutlineEntry> { impl Show for Packed<OutlineEntry> {
#[typst_macros::time(name = "outline.entry", span = self.span())] #[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 mut seq = vec![];
let elem = self.element(); let elem = self.element();
@ -500,7 +500,11 @@ impl Show for Packed<OutlineEntry> {
}; };
// The body text remains overridable. // 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. // Add filler symbols between the section name and page number.
if let Some(filler) = self.fill() { if let Some(filler) = self.fill() {

View File

@ -1299,3 +1299,13 @@ cast! {
ret 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}"));
}

View File

@ -123,7 +123,7 @@ impl SmartQuoter {
/// Process the last seen character. /// Process the last seen character.
pub fn last(&mut self, c: char, is_quote: bool) { 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(); self.last_num = c.is_numeric();
if !is_quote { if !is_quote {
self.prev_quote_type = None; self.prev_quote_type = None;
@ -150,7 +150,7 @@ impl SmartQuoter {
self.prev_quote_type = Some(double); self.prev_quote_type = Some(double);
quotes.open(double) quotes.open(double)
} else if self.quote_depth > 0 } 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; self.quote_depth -= 1;
quotes.close(double) 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) c.is_whitespace() || is_newline(c)
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

View File

@ -176,3 +176,10 @@ Ok ...
// Error: 2-55 heading must have a location // Error: 2-55 heading must have a location
// Hint: 2-55 try using a query or a show rule to customize the outline.entry instead // Hint: 2-55 try using a query or a show rule to customize the outline.entry instead
#outline.entry(1, heading[Hello], [World!], none, [1]) #outline.entry(1, heading[Hello], [World!], none, [1])
--- issue-4476-rtl-title-ending-in-ltr-text ---
#set text(lang: "he")
#outline()
= הוקוס Pocus
= זוהי כותרת שתורגמה על ידי מחשב

View File

@ -69,6 +69,11 @@ Some people's thought on this would be #[#set smartquote(enabled: false); "stran
"'test' statement" \ "'test' statement" \
"statement 'test'" "statement 'test'"
--- smartquote-with-embedding-chars ---
#set text(lang: "fr")
"#"\u{202A}"bonjour#"\u{202C}"" \
#"\u{202A}""bonjour"#"\u{202C}"
--- smartquote-custom --- --- smartquote-custom ---
// Use language quotes for missing keys, allow partial reset // Use language quotes for missing keys, allow partial reset
#set smartquote(quotes: "«»") #set smartquote(quotes: "«»")