Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ rows are the enforced release-mode bounds plus one dated observation.

| Measurement | Value | Version | Platform | Build mode | Input | Command | Statistic | Measured on |
|---|---|---|---|---|---|---|---|---|
| Crates.io archive: rdocx | 1,092,256 compressed bytes, 6,498,484 member bytes, 36 members | 0.14.0 | macOS 26.6.2, Apple M5 Max, arm64 | `cargo package --locked --no-verify` | Tracked `rdocx` package inventory | `python3 scripts/readme_doctests.py --record-measurements` | gzip archive bytes, tar member bytes, tar member count | 2026-09-26 |
| Crates.io archive: rdocx | 1,107,322 compressed bytes, 6,568,455 member bytes, 36 members | 0.14.0 | macOS 26.6.2, Apple M5 Max, arm64 | `cargo package --locked --no-verify` | Tracked `rdocx` package inventory | `python3 scripts/readme_doctests.py --record-measurements` | gzip archive bytes, tar member bytes, tar member count | 2026-09-26 |
| Large-document layout throughput | minimum 250 pages/s, observed 31,019.1 pages/s | rdocx 0.14.0 | macOS 26.6.2, Apple M5 Max, arm64 | release, one test thread | 1,000 one-page paragraphs with deterministic fonts | `cargo test -p rdocx --test regression_test --release a_thousand_page_document_paginates_and_renders_within_the_declared_limits -- --ignored --exact --nocapture --test-threads=1` | pages per wall-clock second | 2026-09-19 |
| Large-document layout peak allocation | maximum 64 MiB, observed 29.03 MiB | rdocx 0.14.0 | macOS 26.6.2, Apple M5 Max, arm64 | release, one test thread | 1,000 one-page paragraphs with deterministic fonts | `cargo test -p rdocx --test regression_test --release a_thousand_page_document_paginates_and_renders_within_the_declared_limits -- --ignored --exact --nocapture --test-threads=1` | peak live allocation | 2026-09-19 |
| Large-document PDF throughput | minimum 1,000 pages/s, observed 60,058.0 pages/s | rdocx 0.14.0 | macOS 26.6.2, Apple M5 Max, arm64 | release, one test thread | 1,000 deterministic layout pages | `cargo test -p rdocx --test regression_test --release a_thousand_page_document_paginates_and_renders_within_the_declared_limits -- --ignored --exact --nocapture --test-threads=1` | pages per wall-clock second | 2026-09-19 |
Expand Down
2 changes: 1 addition & 1 deletion crates/rdocx-oxml/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ schema order, and retains unmodelled XML alongside typed edits.

| Measurement | Value | Version | Platform | Build mode | Input | Command | Statistic | Measured on |
|---|---|---|---|---|---|---|---|---|
| Crates.io archive: rdocx-oxml | 367,500 compressed bytes, 2,380,047 member bytes, 32 members | 0.14.0 | macOS 26.6.2, Apple M5 Max, arm64 | `cargo package --locked --no-verify` | Tracked `rdocx-oxml` package inventory | `python3 scripts/readme_doctests.py --record-measurements` | gzip archive bytes, tar member bytes, tar member count | 2026-09-19 |
| Crates.io archive: rdocx-oxml | 367,851 compressed bytes, 2,381,304 member bytes, 32 members | 0.14.0 | macOS 26.6.2, Apple M5 Max, arm64 | `cargo package --locked --no-verify` | Tracked `rdocx-oxml` package inventory | `python3 scripts/readme_doctests.py --record-measurements` | gzip archive bytes, tar member bytes, tar member count | 2026-09-19 |

## Use it when

Expand Down
37 changes: 34 additions & 3 deletions crates/rdocx-oxml/src/placeholder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@ fn replace_in_single_run(
new_text.push_str(replacement);
new_text.push_str(&t.text[byte_end..]);
t.text = new_text;
t.preserve_space = t.text.starts_with(' ') || t.text.ends_with(' ');
// Keep the flag the producer wrote. Dropping it rewrites an unchanged
// run, which a later comparison against the source then reports.
t.preserve_space = t.preserve_space || t.text.starts_with(' ') || t.text.ends_with(' ');
}
}

Expand All @@ -176,7 +178,7 @@ fn replace_across_runs(
new_text.push_str(&t.text[..first_byte_offset]);
new_text.push_str(replacement);
t.text = new_text;
t.preserve_space = t.text.starts_with(' ') || t.text.ends_with(' ');
t.preserve_space = t.preserve_space || t.text.starts_with(' ') || t.text.ends_with(' ');
}

// Handle the last run: replace from start to match end within that content item.
Expand All @@ -189,7 +191,7 @@ fn replace_across_runs(
let ch_len = remaining.chars().next().map(|c| c.len_utf8()).unwrap_or(0);
let byte_end = last_byte_offset + ch_len;
t.text = t.text[byte_end..].to_string();
t.preserve_space = t.text.starts_with(' ') || t.text.ends_with(' ');
t.preserve_space = t.preserve_space || t.text.starts_with(' ') || t.text.ends_with(' ');
}

// Clear text content from runs strictly between first and last.
Expand Down Expand Up @@ -796,6 +798,35 @@ mod tests {
assert_eq!(p.runs[1].properties.as_ref().unwrap().italic, Some(true));
}

#[test]
fn replace_keeps_the_producer_space_flag() {
let mut preserved = CT_R::new("WORD");
if let RunContent::Text(text) = &mut preserved.content[0] {
text.preserve_space = true;
}
let mut p = CT_P::new();
p.runs.push(preserved.clone());
p.runs.push(preserved);
p.add_run("tail");

assert_eq!(replace_in_paragraph(&mut p, "WORD", "WORD"), 2);
assert_eq!(replace_in_paragraph(&mut p, "DWO", "D-WO"), 1);
assert_eq!(replace_in_paragraph(&mut p, "tail", " end"), 1);
let flags = p
.runs
.iter()
.map(|run| match &run.content[0] {
RunContent::Text(text) => (text.text.as_str(), text.preserve_space),
_ => unreachable!(),
})
.collect::<Vec<_>>();
assert_eq!(
flags,
[("WORD-WO", true), ("RD", true), (" end", true)],
"a rewritten run keeps a producer flag and gains one at a text edge"
);
}

#[test]
fn replace_multiple_occurrences() {
let mut p = make_para(&["{{x}} and {{x}}"]);
Expand Down
Loading
Loading