From 5df664d1a7435f77d91f103e545254fd50b522e9 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sat, 17 Oct 2026 12:41:08 -0700 Subject: [PATCH] Place: justified RTF paragraphs marked right to left end their last line on the right MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit File › Place set every justified paragraph (`\qj`) of an RTF file as Justify with last line aligned left, so a right-to-left paragraph (`\rtlpar`) ended its last line on the left. Such a paragraph now places as Justify with last line aligned right, in either order of the two control words. `\qj` in a left-to-right paragraph still places as Justify with last line aligned left, and `\ql`, `\qr`, `\qc` and `\qd` keep their alignments in both directions. --- crates/engine/src/cmd/place/rtf.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/cmd/place/rtf.rs b/crates/engine/src/cmd/place/rtf.rs index 2832081a2..9bfeb67f7 100644 --- a/crates/engine/src/cmd/place/rtf.rs +++ b/crates/engine/src/cmd/place/rtf.rs @@ -14,6 +14,8 @@ //! superscript and subscript, baseline shift (`\up`, `\dn`) and the color table's color (`\cf`). //! - Paragraph formatting, reset by `\pard`: alignment (`\ql`, `\qc`, `\qr`, `\qj`, `\qd`), left, //! right and first-line indents, space before and after, and direction (`\rtlpar`, `\ltrpar`). +//! `\ql` and `\qr` are the left and right edges in either direction, and a justified (`\qj`) +//! paragraph marked right to left (`\rtlpar`) ends its last line on the right. //! //! Other control words are skipped, and so are hidden text (`\v`) and the groups of destinations //! that are not the document's text: every `\*` destination, the style sheet, document @@ -608,7 +610,11 @@ impl Reader { .paras .iter() .map(|p| ParaStyle { - justify: p.justify, + // `\qj` in an `\rtlpar` paragraph justifies it with its last line on the right. + justify: match p.justify { + Justify::JustifyLeft if p.rtl == Some(true) => Justify::JustifyRight, + j => j, + }, left_indent: twips(p.left), right_indent: twips(p.right), first_line_indent: twips(p.first), @@ -682,6 +688,18 @@ mod tests { assert_eq!(s.paras.last().map(|p| p.left_indent), Some(20.0)); } + #[test] + fn justified_right_to_left_paragraphs_end_their_last_line_on_the_right() { + let s = read(concat!( + r"{\rtf1\pard\rtlpar\qj a\par\pard\qj\rtlpar b\par\pard\qj c\par\pard\rtlpar\ltrpar\qj d\par", + r"\pard\rtlpar\ql e\par\pard\rtlpar\qr f\par\pard\rtlpar\qc g\par\pard\rtlpar\qd h}" + )); + let justify: Vec = s.paras.iter().map(|p| p.justify).collect(); + use Justify::*; + assert_eq!(justify, [JustifyRight, JustifyRight, JustifyLeft, JustifyLeft, Left, Right, Center, JustifyAll]); + assert_eq!(s.paras.first().and_then(|p| p.direction), Some(ParaDirection::RightToLeft)); + } + #[test] fn deep_nesting_and_stray_braces_end_cleanly() { let deep = format!("{{\\rtf1 {}x{}}}", "{".repeat(5000), "}".repeat(5000));