Place: a Word .doc paragraph's own indent holds over its style's

A paragraph style can set a logical start or end indent and the paragraph
itself only a physical left or right one, or the other way round. The
indent applied last now holds, after the sides are mapped for the
paragraph's direction; before, the logical indent always held, so a
paragraph's own indent was lost under its style's.
This commit is contained in:
dotCooCoo
2026-11-02 12:03:58 +07:00
committed by bflatastic
parent ecd184510c
commit 22218e9097
2 changed files with 53 additions and 6 deletions
+26 -6
View File
@@ -890,6 +890,11 @@ struct Para {
/// The logical start and end indents (`sprmPDxaLeft`, `sprmPDxaRight`).
start: Option<i16>,
end: Option<i16>,
/// When `left`, `right`, `start` and `end` were last set, counted in [`Para::applied`]: of a
/// physical and a logical indent on the same side, the one set later holds.
set_at: [u32; 4],
/// The indent properties applied so far.
applied: u32,
first: i16,
before: u16,
after: u16,
@@ -929,10 +934,19 @@ impl Para {
_ => self.align,
}
}
0x840F => self.left = word.map(|v| v as i16),
0x840E => self.right = word.map(|v| v as i16),
0x845E => self.start = word.map(|v| v as i16),
0x845D => self.end = word.map(|v| v as i16),
0x840F | 0x840E | 0x845E | 0x845D => {
let (field, slot) = match sprm {
0x840F => (&mut self.left, 0),
0x840E => (&mut self.right, 1),
0x845E => (&mut self.start, 2),
_ => (&mut self.end, 3),
};
*field = word.map(|v| v as i16);
self.applied = self.applied.saturating_add(1);
if let Some(at) = self.set_at.get_mut(slot) {
*at = self.applied;
}
}
0x8411 | 0x8460 => self.first = word.map_or(self.first, |v| v as i16),
0xA413 => self.before = word.unwrap_or(self.before),
0xA414 => self.after = word.unwrap_or(self.after),
@@ -947,8 +961,14 @@ impl Para {
/// The paragraph attributes of this formatting.
fn style(&self) -> ParaStyle {
let rtl = self.rtl == Some(true);
let (physical_start, physical_end) = if rtl { (self.right, self.left) } else { (self.left, self.right) };
let (start, end) = (self.start.or(physical_start).unwrap_or_default(), self.end.or(physical_end).unwrap_or_default());
let [left_at, right_at, start_at, end_at] = self.set_at;
let (physical_start, physical_end) =
if rtl { ((self.right, right_at), (self.left, left_at)) } else { ((self.left, left_at), (self.right, right_at)) };
let side = |logical: (Option<i16>, u32), physical: (Option<i16>, u32)| {
let (first, second) = if physical.1 > logical.1 { (physical.0, logical.0) } else { (logical.0, physical.0) };
first.or(second).unwrap_or_default()
};
let (start, end) = (side((self.start, start_at), physical_start), side((self.end, end_at), physical_end));
let (left, right) = if rtl { (end, start) } else { (start, end) };
let points = |v: f64| (v / 20.0).clamp(-1296.0, 1296.0);
ParaStyle {
+27
View File
@@ -746,6 +746,33 @@ fn doc_places_as_area_type_with_its_styles_and_formatting() {
assert_eq!((q["count"].as_u64(), q["files"][0]["format"].as_str()), (Some(1), Some("doc")));
}
#[test]
fn doc_indents_set_later_hold_over_the_styles_physical_or_logical_ones() {
let twips = |v: i16| v.to_le_bytes();
// Normal sets a logical start indent of 36 pt. The first paragraph sets only a physical left
// indent of 18 pt; the second sets a logical start indent of 9 pt over a style with a physical
// left indent; the third sets a physical right indent of 27 pt and then right-to-left.
let styles = vec![Some((1, 0x0FFF, prls(&[(0x845E, &twips(720))]), vec![])), Some((1, 0, prls(&[(0x840F, &twips(540))]), vec![]))];
let doc = Word97 {
pieces: vec![("one\rtwo\rthree\r", true)],
papx: vec![
(3, 0, prls(&[(0x840F, &twips(360))])),
(7, 1, prls(&[(0x845E, &twips(180))])),
(13, 0, prls(&[(0x840E, &twips(540)), (0x2441, &[1])])),
],
styles,
fonts: vec!["Arial"],
..Word97::default()
};
let mut s = session();
let file = json!({"name": "indents.doc", "dataBase64": vectorcraft_format::base64_encode(&doc.build())});
let r = s.execute("file.place", &file).unwrap();
let n = s.doc().unwrap().doc.node(NodeId(r["ids"][0].as_u64().unwrap())).unwrap().clone();
let NodeKind::Text(t) = &n.kind else { panic!("not type") };
let summary: Vec<_> = t.paragraph_styles().iter().map(|p| (p.left_indent, p.right_indent)).collect();
assert_eq!(summary, [(18.0, 0.0), (9.0, 0.0), (0.0, 27.0)]);
}
#[test]
fn doc_tables_breaks_and_other_stories_place_as_paragraphs_and_tabs_with_warnings() {
let in_table = prls(&[(0x2416, &[1])]);