Text: right-to-left point text with a justified paragraph sits flush right, as After Effects

After Effects 2026 lays out right-to-left point text whose paragraph carries a
justified code (justify last left / centre / right, justify all) flush at
its start, the right, like right-aligned text (EXPORTER read-back t7: point
rows with codes 3-6 at -152..-8 from the anchor). EffectCraft placed it by
the last-line side (left / centre / left). Left-to-right point text and box
text are unchanged; AE's slightly wider right-to-left box wrapping (1-2 px
narrower word gaps) stays open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
vangel76
2026-10-19 16:41:15 +02:00
co-authored by Claude Opus 5.5
parent 8bc57bc7ad
commit 83b134cdc8
2 changed files with 34 additions and 1 deletions
+24 -1
View File
@@ -635,7 +635,11 @@ fn paragraph(text: &str, base: usize, runs: &[(Range<usize>, TextStyle)], primar
first_ascent(primaries[si].face, st.size).unwrap_or_else(|| fonts::face(primaries[si].face).metrics(st.size).ascent)
};
let eff_align = |last: bool| -> Align {
if (last || width.is_none()) && para.align == Align::Justify {
if rtl_para && width.is_none() && para.align == Align::Justify {
// Right-to-left point text with a justified paragraph sits flush at its start, the
// right (After Effects 2026, every justify code; left-to-right not measured).
Align::Right
} else if (last || width.is_none()) && para.align == Align::Justify {
if width.is_none() && para.justify_last == Align::Justify { Align::Left } else { para.justify_last }
} else {
para.align
@@ -1499,6 +1503,25 @@ mod tests {
assert!(r.lines[0].x + r.lines[0].width > 401.0, "closing quote hangs right");
}
/// After Effects 2026 (EXPORTER read-back t7): right-to-left point text with any justified
/// code (justify last left / centre / right, justify all) sits flush right at its anchor, as
/// right-aligned text does.
#[test]
fn rtl_point_text_with_a_justified_paragraph_is_flush_at_its_start() {
let text = "Right to left";
let s = st(30.0);
let at = |align: Align, last: Align| {
let p = ParagraphStyle { align, justify_last: last, rtl: Some(true), ..Default::default() };
let l = layout_rich_uncached(text, &[(0..text.len(), s.clone())], &[p]);
(l.lines[0].x, l.lines[0].x + l.lines[0].width)
};
let right = at(Align::Right, Align::Left);
for last in [Align::Left, Align::Center, Align::Right, Align::Justify] {
let got = at(Align::Justify, last);
assert!((got.0 - right.0).abs() < 1e-3 && (got.1 - right.1).abs() < 1e-3, "{last:?}: {got:?} vs right-aligned {right:?}");
}
}
#[test]
fn rtl_paragraph_carets_run_right_to_left() {
let text = "\u{5d0}\u{5d1}\u{5d2}";
+10
View File
@@ -80,6 +80,16 @@ every-line composer ignored the last line's slack and left "twelve" alone on a s
counts that slack for Justify All, and every word starts within 1 px of AE's. Regression test:
`justify_all_balances_the_lines_with_the_every_line_composer`.
## Right-to-left point text with a justified paragraph
Measured on 19 October 2026 with After Effects 2026 (26.2) (EXPORTER read-back t7): right-to-left
point text whose paragraph has a justified code (justify last left / centre / right, justify all)
sits flush right at its anchor, like right-aligned text. EffectCraft placed it by the last-line
side (left / centre / left). Now flush right; left-to-right point text is unchanged (not measured).
Regression test: `rtl_point_text_with_a_justified_paragraph_is_flush_at_its_start`. Still open:
AE wraps right-to-left box text slightly wider (its word gaps are 1–2 px narrower than in
left-to-right text with the same font), so a 300 px box fits a word more per line.
## Still unmeasured
Rendered frame comparisons now have a deterministic `effectcraft_effects::fidelity::compare_frames`