Page break (Ctrl+Enter) survives ODT/TXT/Markdown/HTML export and no longer glues words (#84) (#86)

This commit is contained in:
MarekSpodBiedry authored and GitHub committed 2026-10-10 06:16:15 -04:00
1 parent 6b1f7a2741
commit f71fa219ca
2 files changed
+35 -5

No files matched your search

+22 -5
View File
@@ -5,7 +5,7 @@
use std::sync::Arc;
use wordcraft_doc::numbering::{AbstractNum, Level, ListKind, Num, levels_for};
use wordcraft_doc::para::{InlineObject, OBJ};
use wordcraft_doc::para::{InlineObject, OBJ, PAGE_BREAK};
use wordcraft_doc::props::{
Border, BorderStyle, Borders, CellProps, CharProps, NumRef, ParaProps, RowProps, TextColor, Underline, VMerge, VertAlign,
};
@@ -590,9 +590,11 @@ fn media_ext(key: &str, data: &[u8]) -> String {
}
}
/// A document paragraph as a flow paragraph.
pub fn flow_para(doc: &Document, p: &Paragraph) -> Para {
/// A document paragraph as flow paragraphs: a page break character inside the text ends the
/// paragraph, and the text after it starts a new one that carries `page_break`.
pub fn flow_paras(doc: &Document, p: &Paragraph) -> Vec<Para> {
let (kind, list) = para_kind(doc, p);
let mut done: Vec<Para> = Vec::new();
let mut out = Para { kind, list, align: p.props.align, page_break: p.props.page_break_before.unwrap_or(false), inlines: Vec::new() };
let in_code = kind == Kind::Code;
let mut k = 0usize;
@@ -602,6 +604,20 @@ pub fn flow_para(doc: &Document, p: &Paragraph) -> Para {
let f = fmt_of(doc, props, in_code);
let mut buf = String::new();
for c in text.chars() {
if c == PAGE_BREAK {
if !hidden {
out.push_text(&std::mem::take(&mut buf), &f);
let next = Para { kind, list: None, align: p.props.align, page_break: true, inlines: Vec::new() };
let prev = std::mem::replace(&mut out, next);
if prev.inlines.is_empty() {
out.page_break |= prev.page_break;
out.list = prev.list;
} else {
done.push(prev);
}
}
continue;
}
if c != OBJ {
buf.push(c);
continue;
@@ -638,14 +654,15 @@ pub fn flow_para(doc: &Document, p: &Paragraph) -> Para {
out.push_text(&buf, &f);
}
}
out
done.push(out);
done
}
fn flow_blocks(doc: &Document, bl: &Blocks, depth: usize) -> Vec<FBlock> {
let mut out = Vec::new();
for b in bl {
match &**b {
Block::Para(p) => out.push(FBlock::Para(flow_para(doc, p))),
Block::Para(p) => out.extend(flow_paras(doc, p).into_iter().map(FBlock::Para)),
Block::Table(t) if depth < MAX_DEPTH => out.push(FBlock::Table(flow_table(doc, t, depth))),
Block::Table(t) => {
for r in &t.rows {
+13
View File
@@ -199,6 +199,19 @@ fn odt_round_trip() {
assert_eq!(back.core.creator, "Ada Writer");
}
#[test]
fn page_break_char_splits_paragraph() {
let mut d = Document::new();
d.body = vec![para_block(Paragraph::with_text("num two\u{c}Page two", CharProps::default()))];
let b = flat(&d);
assert!(matches!(&b[..], [FBlock::Para(a), FBlock::Para(c)] if a.text() == "num two" && !a.page_break && c.text() == "Page two" && c.page_break));
let txt = String::from_utf8(export("txt", &d).unwrap().unwrap()).unwrap();
assert!(!txt.contains("twoPage"), "{txt:?}");
let odt = import("odt", &export("odt", &d).unwrap().unwrap()).unwrap().unwrap();
let b = flat(&odt);
assert!(b.iter().any(|x| matches!(x, FBlock::Para(p) if p.text().trim() == "Page two" && p.page_break)));
}
#[test]
fn txt_round_trip() {
check_round_trip("txt", false);