fix(edit): only a row aligned on its own is rewritten

A nonempty aligns counted as a style change, so every row of the
paragraph was rebuilt from the text state: rows left unselected lost
their source operators, and with TJ word gaps their words moved by
about a point and a half while the row's outer width stayed the same.
aligns no longer makes an edit non-plain; a row whose own line has an
alignment override is rewritten, and the others keep their operators.
This commit is contained in:
RiceDee committed 2026-10-25 16:57:41 +11:00
1 parent 2f50e1fde7
commit c9abf15e7c
2 files changed
+29 -3

No files matched your search

+20
View File
@@ -6975,6 +6975,26 @@ fn one_row_of_a_paragraph_takes_its_own_alignment() {
assert!((row("eight")[2] - block.rect[2]).abs() < 1.0, "the second row ends at the right edge: {:?} vs {}", row("eight"), block.rect[2]);
}
/// Only a row given an alignment of its own is rewritten. The others keep their source operators,
/// `TJ` word gaps included, so their words stay exactly where they were: rebuilt from the text,
/// the first row's words moved by about a point and a half while its outer width stayed the same.
#[test]
fn rows_without_their_own_alignment_keep_their_operators() {
let first = "[(One) -250 (two) -250 (three) -250 (four)] TJ";
let mut doc = text_page(&format!("BT /F1 10 Tf 12 TL 72 700 Td {first} T* [(five) -250 (six) -250 (seven) -250 (eight)] TJ T* (nine ten) Tj ET"));
let block = text::text_blocks(&doc, 0).unwrap()[0].clone();
assert_eq!(block.editor_text.matches('\n').count(), 2, "one paragraph of three rows: {:?}", block.editor_text);
let row = |doc: &Document, starts: &str| text::text_lines(doc, 0).unwrap().into_iter().find(|l| l.text.starts_with(starts)).expect("the row");
let ink = row(&doc, "One").ink;
let style = text::BlockStyle { aligns: vec![None, None, Some(added::Align::Right)], ..Default::default() };
text::rewrite_block(&mut doc, 0, 0, Some(&block.editor_text), &style).unwrap();
let doc = reopen(&doc);
assert_eq!(row(&doc, "One").ink, ink, "the first row's words stay where they were");
let content = String::from_utf8_lossy(&page_content_bytes(&doc, 0)).into_owned();
assert!(content.contains(first), "the first row keeps its operator: {content}");
assert!((row(&doc, "nine").rect[2] - block.rect[2]).abs() < 1.0, "the last row is right-aligned: {:?}", row(&doc, "nine").rect);
}
/// The same in a region of several styles: only the short first row is centred.
#[test]
fn one_row_of_a_styled_region_takes_its_own_alignment() {
+9 -3
View File
@@ -874,10 +874,16 @@ pub(super) fn rewrite_styled_block(
let pieces = Pieces::join(&streams.iter().map(|(_, d)| d.as_slice()).collect::<Vec<_>>());
let ops = pieces.parse().ops;
// A row the edit doesn't reach keeps its source operators, so its kerning, TJ word gaps and
// glyph widths stay exactly as they were instead of being rebuilt from the text state.
let plain = BlockStyle { ranges: Vec::new(), ..style.clone() } == BlockStyle::default();
// glyph widths stay exactly as they were instead of being rebuilt from the text state. A row
// aligned on its own is reached; the rows around it are not.
let plain = BlockStyle { ranges: Vec::new(), aligns: Vec::new(), ..style.clone() } == BlockStyle::default();
let mut kept = if plain {
untouched_rows(block, draft, &row_ranges).into_iter().zip(&rows).map(|(same, row)| same && row.iter().all(|c| c.1 < source_count)).collect()
untouched_rows(block, draft, &row_ranges)
.into_iter()
.zip(&rows)
.zip(&row_lines)
.map(|((same, row), line)| same && row.iter().all(|c| c.1 < source_count) && style.aligns.get(*line).copied().flatten().is_none())
.collect()
} else {
vec![false; rows.len()]
};