mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
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:
parent
a3f3a1a833
commit
17ee3df1ba
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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() {
|
||||
|
@ -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}"));
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
BIN
tests/ref/issue-4476-rtl-title-ending-in-ltr-text.png
Normal file
BIN
tests/ref/issue-4476-rtl-title-ending-in-ltr-text.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
BIN
tests/ref/smartquote-with-embedding-chars.png
Normal file
BIN
tests/ref/smartquote-with-embedding-chars.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 571 B |
@ -176,3 +176,10 @@ Ok ...
|
||||
// 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
|
||||
#outline.entry(1, heading[Hello], [World!], none, [1])
|
||||
|
||||
--- issue-4476-rtl-title-ending-in-ltr-text ---
|
||||
#set text(lang: "he")
|
||||
#outline()
|
||||
|
||||
= הוקוס Pocus
|
||||
= זוהי כותרת שתורגמה על ידי מחשב
|
||||
|
@ -69,6 +69,11 @@ Some people's thought on this would be #[#set smartquote(enabled: false); "stran
|
||||
"'test' statement" \
|
||||
"statement 'test'"
|
||||
|
||||
--- smartquote-with-embedding-chars ---
|
||||
#set text(lang: "fr")
|
||||
"#"\u{202A}"bonjour#"\u{202C}"" \
|
||||
#"\u{202A}""bonjour"#"\u{202C}"
|
||||
|
||||
--- smartquote-custom ---
|
||||
// Use language quotes for missing keys, allow partial reset
|
||||
#set smartquote(quotes: "«»")
|
||||
|
Loading…
x
Reference in New Issue
Block a user