From d5d5d93b91771e6f3bdeb00128c9c818b09507c0 Mon Sep 17 00:00:00 2001 From: Tobias Schmitz Date: Mon, 5 May 2025 17:27:24 +0200 Subject: [PATCH 1/4] fix: prohibit some line break opportunities between LTR-ISOLATE and OBJECT-REPLACEMENT-CHARACTER Don't provide a line breaking opportunity between and LTR-ISOLATE and an OBJECT-REPLACEMENT-CHARACTER representing and inline item, if the LTR-ISOLATE could end up as the only character on the previous line. --- crates/typst-layout/src/inline/linebreak.rs | 26 +++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/crates/typst-layout/src/inline/linebreak.rs b/crates/typst-layout/src/inline/linebreak.rs index 31512604f..ef8159035 100644 --- a/crates/typst-layout/src/inline/linebreak.rs +++ b/crates/typst-layout/src/inline/linebreak.rs @@ -690,13 +690,35 @@ fn breakpoints(p: &Preparation, mut f: impl FnMut(usize, Breakpoint)) { let breakpoint = if point == text.len() { Breakpoint::Mandatory } else { + const LTR_ISOLATE: char = '\u{2066}'; + const OBJ_REPLACE: char = '\u{FFFC}'; match lb.get(c) { - // Fix for: https://github.com/unicode-org/icu4x/issues/4146 - LineBreak::Glue | LineBreak::WordJoiner | LineBreak::ZWJ => continue, LineBreak::MandatoryBreak | LineBreak::CarriageReturn | LineBreak::LineFeed | LineBreak::NextLine => Breakpoint::Mandatory, + + // https://github.com/typst/typst/issues/5489 + // + // OBJECT-REPLACEMENT-CHARACTERs provide Contingent Break + // opportunities before and after by default. This behaviour + // is however tailorable, see: + // https://www.unicode.org/reports/tr14/#CB + // https://www.unicode.org/reports/tr14/#TailorableBreakingRules + // https://www.unicode.org/reports/tr14/#LB20 + // + // Don't provide a line breaking opportunity between a LTR- + // ISOLATE and an OBJECT-REPLACEMENT-CHARACTER representing + // and inline item, if the LTR-ISOLATE could end up as the + // only character on the previous line. + LineBreak::CombiningMark + if c == LTR_ISOLATE + && text[point..].starts_with(OBJ_REPLACE) + && last == (point - LTR_ISOLATE.len_utf8()) => + { + continue; + } + _ => Breakpoint::Normal, } }; From c6bf0200aaaca12aa052270d089251fdf46bc015 Mon Sep 17 00:00:00 2001 From: Tobias Schmitz Date: Wed, 7 May 2025 12:04:46 +0200 Subject: [PATCH 2/4] test: add test case --- tests/ref/issue-5489-matrix-stray-linebreak.png | Bin 0 -> 644 bytes tests/suite/layout/inline/linebreak.typ | 8 ++++++++ 2 files changed, 8 insertions(+) create mode 100644 tests/ref/issue-5489-matrix-stray-linebreak.png diff --git a/tests/ref/issue-5489-matrix-stray-linebreak.png b/tests/ref/issue-5489-matrix-stray-linebreak.png new file mode 100644 index 0000000000000000000000000000000000000000..2d278bd5c9cadb4b26a5f26dd0565f6a4bfafedf GIT binary patch literal 644 zcmV-~0(FAT!-&LSWKIn|;D1$L9BQHvo)N3y(o! zS^G3^ywWxJ5Y)`U>Po{Kj5rF?Ij!(01~NLFa2Ngby2A*Mfxxfs9@;=E=}=+VWfyTy zDLf8Rpj);=g+Xcye&U=~c=84WJ<1lSysCzuK@Zu@B(?CoAF_@h2uUI05ri6KJ>i?Z zaPIuG{t1Zu^VGu81pscAW#cuE;v-~jkKzL$T8L5$;~^CL!D~H2uJmddiVWMpND28$Q z9Fi2@V5|9T^`HX94_n7lygY3NE&yP;c+c?b=?&7-`o^8CFR7l>TLXtnp#%lTCG97n eg|)C29=6|_=gtD%*Z=JR0000 Date: Thu, 8 May 2025 17:07:11 +0200 Subject: [PATCH 3/4] Update crates/typst-layout/src/inline/linebreak.rs Co-authored-by: Max --- crates/typst-layout/src/inline/linebreak.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/typst-layout/src/inline/linebreak.rs b/crates/typst-layout/src/inline/linebreak.rs index ef8159035..63754f358 100644 --- a/crates/typst-layout/src/inline/linebreak.rs +++ b/crates/typst-layout/src/inline/linebreak.rs @@ -712,9 +712,8 @@ fn breakpoints(p: &Preparation, mut f: impl FnMut(usize, Breakpoint)) { // and inline item, if the LTR-ISOLATE could end up as the // only character on the previous line. LineBreak::CombiningMark - if c == LTR_ISOLATE - && text[point..].starts_with(OBJ_REPLACE) - && last == (point - LTR_ISOLATE.len_utf8()) => + if text[point..].starts_with(OBJ_REPLACE) + && last == (point - c.len_utf8()) => { continue; } From 6848875bd8c9a80829892537da27458296c4eaa3 Mon Sep 17 00:00:00 2001 From: Tobias Schmitz Date: Thu, 8 May 2025 17:08:54 +0200 Subject: [PATCH 4/4] refactor: remove unused constant and update comment --- crates/typst-layout/src/inline/linebreak.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/typst-layout/src/inline/linebreak.rs b/crates/typst-layout/src/inline/linebreak.rs index 63754f358..1684a7807 100644 --- a/crates/typst-layout/src/inline/linebreak.rs +++ b/crates/typst-layout/src/inline/linebreak.rs @@ -690,7 +690,6 @@ fn breakpoints(p: &Preparation, mut f: impl FnMut(usize, Breakpoint)) { let breakpoint = if point == text.len() { Breakpoint::Mandatory } else { - const LTR_ISOLATE: char = '\u{2066}'; const OBJ_REPLACE: char = '\u{FFFC}'; match lb.get(c) { LineBreak::MandatoryBreak @@ -708,9 +707,10 @@ fn breakpoints(p: &Preparation, mut f: impl FnMut(usize, Breakpoint)) { // https://www.unicode.org/reports/tr14/#LB20 // // Don't provide a line breaking opportunity between a LTR- - // ISOLATE and an OBJECT-REPLACEMENT-CHARACTER representing - // and inline item, if the LTR-ISOLATE could end up as the - // only character on the previous line. + // ISOLATE (or any other Combining Mark) and an OBJECT- + // REPLACEMENT-CHARACTER representing and inline item, if the + // LTR-ISOLATE could end up as the only character on the + // previous line. LineBreak::CombiningMark if text[point..].starts_with(OBJ_REPLACE) && last == (point - c.len_utf8()) =>