docxdriver 0.3.2__tar.gz → 0.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {docxdriver-0.3.2 → docxdriver-0.3.3}/Cargo.lock +5 -5
- {docxdriver-0.3.2 → docxdriver-0.3.3}/Cargo.toml +1 -1
- {docxdriver-0.3.2 → docxdriver-0.3.3}/PKG-INFO +1 -1
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/api.rs +38 -14
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/bin/generate_typed_schema.rs +3 -2
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/commands/layout.rs +11 -4
- docxdriver-0.3.3/crates/docxdriver-core/src/commands/lists.rs +531 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/commands/mod.rs +38 -9
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/commands/readonly.rs +1 -3
- docxdriver-0.3.3/crates/docxdriver-core/src/commands/sections.rs +183 -0
- docxdriver-0.3.3/crates/docxdriver-core/src/commands/table_structure.rs +563 -0
- docxdriver-0.3.3/crates/docxdriver-core/src/commands/tables.rs +925 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/document.rs +109 -16
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/html/build.rs +41 -22
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/html/ctx.rs +4 -1
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/html/input.rs +5 -1
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/html/render.rs +6 -5
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/tests/formatting_fidelity.rs +308 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/tests/html_e2e.rs +86 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/tests/typed_api.rs +54 -0
- docxdriver-0.3.3/crates/docxdriver-python/python/docxdriver/repl/prelude.py +3 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-python/tests/test_host.py +50 -1
- docxdriver-0.3.3/python/docxdriver/repl/prelude.py +3 -0
- docxdriver-0.3.2/crates/docxdriver-core/src/commands/lists.rs +0 -141
- docxdriver-0.3.2/crates/docxdriver-core/src/commands/table_structure.rs +0 -100
- docxdriver-0.3.2/crates/docxdriver-core/src/commands/tables.rs +0 -128
- docxdriver-0.3.2/crates/docxdriver-python/python/docxdriver/repl/prelude.py +0 -3
- docxdriver-0.3.2/python/docxdriver/repl/prelude.py +0 -3
- {docxdriver-0.3.2 → docxdriver-0.3.3}/README.md +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/Cargo.toml +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/README.md +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/assets/blank.docx +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/examples/list_scaling.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/examples/plan_scaling.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/commands/comments.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/commands/equations.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/commands/headers.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/commands/reading.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/commands/revisions.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/commands/typed.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/html/README.md +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/html/assets.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/html/chrome.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/html/compact.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/html/mathml_omml.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/html/mod.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/html/omml_latex.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/html/omml_mathml.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/html/parse.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/html/styles.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/html/surface.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/lib.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/media.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/outcome.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/package.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/segments.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/tests/README.md +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/tests/fixtures/projection-parity/features.json +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/tests/fixtures/projection-parity/parity.docx +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/tests/fixtures/projection-parity/prior-projection.json +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/tests/fixtures/ts-reference.docx +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/tests/interop.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/tests/package_layout.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/tests/projection_parity.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/tests/spike.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-python/.gitignore +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-python/Cargo.toml +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-python/README.md +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-python/python/docxdriver/__init__.py +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-python/python/docxdriver/repl/__init__.py +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-python/python/docxdriver/repl/host.py +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-python/python/docxdriver/repl/quotes.py +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-python/src/lib.rs +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/pyproject.toml +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/python/docxdriver/__init__.py +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/python/docxdriver/repl/__init__.py +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/python/docxdriver/repl/host.py +0 -0
- {docxdriver-0.3.2 → docxdriver-0.3.3}/python/docxdriver/repl/quotes.py +0 -0
|
@@ -416,7 +416,7 @@ dependencies = [
|
|
|
416
416
|
|
|
417
417
|
[[package]]
|
|
418
418
|
name = "docxdriver-cli"
|
|
419
|
-
version = "0.3.
|
|
419
|
+
version = "0.3.3"
|
|
420
420
|
dependencies = [
|
|
421
421
|
"clap",
|
|
422
422
|
"docxdriver-core",
|
|
@@ -427,7 +427,7 @@ dependencies = [
|
|
|
427
427
|
|
|
428
428
|
[[package]]
|
|
429
429
|
name = "docxdriver-core"
|
|
430
|
-
version = "0.3.
|
|
430
|
+
version = "0.3.3"
|
|
431
431
|
dependencies = [
|
|
432
432
|
"hex",
|
|
433
433
|
"latex2mathml",
|
|
@@ -444,7 +444,7 @@ dependencies = [
|
|
|
444
444
|
|
|
445
445
|
[[package]]
|
|
446
446
|
name = "docxdriver-mcp"
|
|
447
|
-
version = "0.3.
|
|
447
|
+
version = "0.3.3"
|
|
448
448
|
dependencies = [
|
|
449
449
|
"axum",
|
|
450
450
|
"cap-std",
|
|
@@ -462,7 +462,7 @@ dependencies = [
|
|
|
462
462
|
|
|
463
463
|
[[package]]
|
|
464
464
|
name = "docxdriver-python"
|
|
465
|
-
version = "0.3.
|
|
465
|
+
version = "0.3.3"
|
|
466
466
|
dependencies = [
|
|
467
467
|
"docxdriver-core",
|
|
468
468
|
"pyo3",
|
|
@@ -471,7 +471,7 @@ dependencies = [
|
|
|
471
471
|
|
|
472
472
|
[[package]]
|
|
473
473
|
name = "docxdriver-wasm"
|
|
474
|
-
version = "0.3.
|
|
474
|
+
version = "0.3.3"
|
|
475
475
|
dependencies = [
|
|
476
476
|
"docxdriver-core",
|
|
477
477
|
"js-sys",
|
|
@@ -159,9 +159,9 @@ pub struct RowFormat {
|
|
|
159
159
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
|
|
160
160
|
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
|
|
161
161
|
pub enum TableEdit {
|
|
162
|
-
InsertRows { row: u32, position: InsertPosition, count: u32 },
|
|
162
|
+
InsertRows { row: u32, position: InsertPosition, count: u32, #[serde(rename = "as", default)] alias: Option<String> },
|
|
163
163
|
DeleteRows { row: u32, count: u32 },
|
|
164
|
-
InsertColumns { column: u32, position: InsertPosition, count: u32 },
|
|
164
|
+
InsertColumns { column: u32, position: InsertPosition, count: u32, #[serde(rename = "as", default)] alias: Option<String> },
|
|
165
165
|
DeleteColumns { column: u32, count: u32 },
|
|
166
166
|
MergeCells { row: u32, column: u32, row_span: u32, column_span: u32 },
|
|
167
167
|
SplitCell { row: u32, column: u32 },
|
|
@@ -665,7 +665,9 @@ pub enum EditOp {
|
|
|
665
665
|
gutter: Option<Points>,
|
|
666
666
|
},
|
|
667
667
|
/// Selective section-property edits. Dimensions are points; omitted fields preserve source.
|
|
668
|
+
SetSectionBoundary { at: ParagraphAddress, #[serde(default)] remove: bool, #[serde(default)] break_type: Option<String> },
|
|
668
669
|
SetSectionLayout {
|
|
670
|
+
#[serde(default)] section_handle: Option<String>,
|
|
669
671
|
#[serde(default)]
|
|
670
672
|
section: Option<u32>,
|
|
671
673
|
#[serde(default)]
|
|
@@ -1480,7 +1482,7 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
|
|
|
1480
1482
|
return Err("levels requires a new list with 1..9 definitions covering the selected level".into());
|
|
1481
1483
|
}
|
|
1482
1484
|
for (i, def) in defs.iter().enumerate() {
|
|
1483
|
-
if !["bullet","decimal","lowerLetter","upperLetter","lowerRoman","upperRoman"].contains(&def.format.as_str()) || def.start ==
|
|
1485
|
+
if !["bullet","decimal","lowerLetter","upperLetter","lowerRoman","upperRoman"].contains(&def.format.as_str()) || def.start.is_some_and(|v|v == 0 || v > i32::MAX as u32) || def.label.is_empty() || def.label.len() > 255 || def.restart_after.is_some_and(|v| v as usize >= i) {
|
|
1484
1486
|
return Err("invalid list level format, label, start or restart ancestor".into());
|
|
1485
1487
|
}
|
|
1486
1488
|
if [def.indent_left,def.indent_right,def.first_line_indent].into_iter().flatten().any(|v| v.0.unsigned_abs() > 2_000_000) { return Err("list indentation is out of range".into()); }
|
|
@@ -1491,8 +1493,8 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
|
|
|
1491
1493
|
if def.marker_font.as_ref().is_some_and(|v| v.is_empty()) || def.marker_color.as_ref().is_some_and(|v| v.len() != 6 || !v.bytes().all(|c| c.is_ascii_hexdigit())) { return Err("invalid list marker font or six-digit RGB color".into()); }
|
|
1492
1494
|
}
|
|
1493
1495
|
}
|
|
1494
|
-
if at.is_empty() || *level > 8 ||
|
|
1495
|
-
return Err("set_list requires paragraphs, level 0..8 and
|
|
1496
|
+
if at.is_empty() || *level > 8 || start.is_some_and(|v|v == 0 || v > i32::MAX as u32) || num_id.is_some_and(|v|v == 0 || v > i32::MAX as u32) {
|
|
1497
|
+
return Err("set_list requires paragraphs, level 0..8 and IDs/starts in 1..2147483647".into());
|
|
1496
1498
|
}
|
|
1497
1499
|
if *remove && (num_id.is_some() || format.is_some() || start.is_some() || *level != 0) {
|
|
1498
1500
|
return Err("set_list remove cannot be combined with list settings".into());
|
|
@@ -1516,7 +1518,13 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
|
|
|
1516
1518
|
return Err("at must not be empty".into());
|
|
1517
1519
|
}
|
|
1518
1520
|
}
|
|
1519
|
-
EditOp::
|
|
1521
|
+
EditOp::SetSectionBoundary {remove,break_type,..} => {
|
|
1522
|
+
if *remove && break_type.is_some(){return Err("removing a boundary cannot set break_type".into());}
|
|
1523
|
+
if break_type.as_ref().is_some_and(|v|!matches!(v.as_str(),"nextPage"|"continuous"|"oddPage"|"evenPage"|"nextColumn")){return Err("unsupported section break_type".into());}
|
|
1524
|
+
}
|
|
1525
|
+
EditOp::SetSectionLayout { section, section_handle, page_size, width, height, orientation, columns, column_spacing, column_widths, column_separator, break_type, page_number_format, page_number_start, page_number_continue } => {
|
|
1526
|
+
if section.is_some() && section_handle.is_some() {return Err("section and section_handle are mutually exclusive".into());}
|
|
1527
|
+
if section_handle.as_ref().is_some_and(|v|!v.starts_with("section:")){return Err("section_handle must use an inventory handle".into());}
|
|
1520
1528
|
if section == &Some(0) { return Err("section must be positive".into()); }
|
|
1521
1529
|
if let Some(size) = page_size { if !matches!(size.as_str(), "letter" | "legal" | "a4" | "a3" | "a5") { return Err("page_size must be letter/legal/a4/a3/a5".into()); } }
|
|
1522
1530
|
if page_size.is_some() && (width.is_some() || height.is_some()) { return Err("page_size conflicts with width/height".into()); }
|
|
@@ -1524,11 +1532,13 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
|
|
|
1524
1532
|
if [width.as_ref(), height.as_ref()].into_iter().flatten().any(|value| value.0 <= 0 || value.0 > 31680) { return Err("page dimensions must be >0 and <=1584 points".into()); }
|
|
1525
1533
|
if orientation.as_ref().is_some_and(|v| !matches!(v.as_str(), "portrait" | "landscape")) { return Err("orientation must be portrait/landscape".into()); }
|
|
1526
1534
|
if columns.is_some_and(|v| v == 0 || v > 45) { return Err("columns must be between 1 and 45".into()); }
|
|
1527
|
-
if
|
|
1535
|
+
if column_spacing.is_some_and(|p| !(0..=31680).contains(&p.0)) { return Err("column_spacing must be between 0 and 1584 points".into()); }
|
|
1536
|
+
if column_widths.as_ref().is_some_and(|v| v.is_empty() || v.len() > 45 || v.iter().any(|p| p.0 <= 0 || p.0 > 31680)) { return Err("column_widths requires 1..45 positive widths of at most 1584 points".into()); }
|
|
1528
1537
|
if let (Some(count), Some(widths)) = (columns.as_ref(), column_widths.as_ref()) { if *count as usize != widths.len() { return Err("columns must match column_widths length".into()); } }
|
|
1529
1538
|
if break_type.as_ref().is_some_and(|v| !matches!(v.as_str(), "nextPage" | "continuous" | "oddPage" | "evenPage" | "nextColumn")) { return Err("unsupported section break_type".into()); }
|
|
1530
1539
|
if page_number_format.as_ref().is_some_and(|v| !matches!(v.as_str(), "decimal" | "upperRoman" | "lowerRoman" | "upperLetter" | "lowerLetter")) { return Err("unsupported page_number_format".into()); }
|
|
1531
|
-
if
|
|
1540
|
+
if page_number_continue == &Some(false) { return Err("page_number_continue only accepts true; use page_number_start to restart".into()); }
|
|
1541
|
+
if page_number_start.is_some_and(|value| value == 0 || value > i32::MAX as u32) { return Err("page_number_start must be between 1 and 2147483647".into()); }
|
|
1532
1542
|
if page_number_start.is_some() && page_number_continue == &Some(true) { return Err("page_number_start conflicts with page_number_continue".into()); }
|
|
1533
1543
|
if page_size.is_none() && width.is_none() && orientation.is_none() && columns.is_none() && column_spacing.is_none() && column_widths.is_none() && column_separator.is_none() && break_type.is_none() && page_number_format.is_none() && page_number_start.is_none() && page_number_continue.is_none() { return Err("set_section_layout requires a property".into()); }
|
|
1534
1544
|
}
|
|
@@ -1833,6 +1843,7 @@ fn fold_typed(
|
|
|
1833
1843
|
let resolved = match resolve_typed_aliases(op, &aliases) {
|
|
1834
1844
|
Ok(value) => value,
|
|
1835
1845
|
Err(error) => {
|
|
1846
|
+
attach_affected(&mut state, &mut reports, &affected_ids);
|
|
1836
1847
|
let report = PlanReport {
|
|
1837
1848
|
completed: index,
|
|
1838
1849
|
stopped_at: Some(index),
|
|
@@ -1872,7 +1883,7 @@ fn fold_typed(
|
|
|
1872
1883
|
report.summary.push_str(¬e);
|
|
1873
1884
|
}
|
|
1874
1885
|
}
|
|
1875
|
-
attach_affected(&state, &mut reports, &affected_ids);
|
|
1886
|
+
attach_affected(&mut state, &mut reports, &affected_ids);
|
|
1876
1887
|
let report = PlanReport {
|
|
1877
1888
|
completed: index,
|
|
1878
1889
|
stopped_at: Some(index),
|
|
@@ -1892,10 +1903,11 @@ fn fold_typed(
|
|
|
1892
1903
|
);
|
|
1893
1904
|
}
|
|
1894
1905
|
if let EditOp::InsertParagraph { alias: Some(alias), .. }
|
|
1895
|
-
| EditOp::InsertTable { alias: Some(alias), .. }
|
|
1906
|
+
| EditOp::InsertTable { alias: Some(alias), .. }
|
|
1907
|
+
| EditOp::EditTable { action: TableEdit::InsertRows { alias: Some(alias), .. } | TableEdit::InsertColumns { alias: Some(alias), .. }, .. } = op
|
|
1896
1908
|
{
|
|
1897
1909
|
if aliases.contains_key(alias) {
|
|
1898
|
-
attach_affected(&state, &mut reports, &affected_ids);
|
|
1910
|
+
attach_affected(&mut state, &mut reports, &affected_ids);
|
|
1899
1911
|
let report = PlanReport {
|
|
1900
1912
|
completed: index,
|
|
1901
1913
|
stopped_at: Some(index),
|
|
@@ -1917,7 +1929,7 @@ fn fold_typed(
|
|
|
1917
1929
|
)),
|
|
1918
1930
|
);
|
|
1919
1931
|
}
|
|
1920
|
-
if
|
|
1932
|
+
if matches!(op, EditOp::InsertTable { .. } | EditOp::EditTable { .. }) {
|
|
1921
1933
|
aliases.insert(alias.clone(), Value::String(format!("${alias}")));
|
|
1922
1934
|
}
|
|
1923
1935
|
if let Some(id) = outcome
|
|
@@ -1931,7 +1943,7 @@ fn fold_typed(
|
|
|
1931
1943
|
}
|
|
1932
1944
|
}
|
|
1933
1945
|
}
|
|
1934
|
-
attach_affected(&state, &mut reports, &affected_ids);
|
|
1946
|
+
attach_affected(&mut state, &mut reports, &affected_ids);
|
|
1935
1947
|
let report = PlanReport {
|
|
1936
1948
|
completed: plan.ops.len(),
|
|
1937
1949
|
stopped_at: None,
|
|
@@ -2013,10 +2025,11 @@ fn explain_selector_drift(
|
|
|
2013
2025
|
/// Fill each operation report with the rendered markup of the paragraphs it
|
|
2014
2026
|
/// affected, as they stand in `state`, from one render of the document.
|
|
2015
2027
|
fn attach_affected(
|
|
2016
|
-
state: &crate::commands::WorkState,
|
|
2028
|
+
state: &mut crate::commands::WorkState,
|
|
2017
2029
|
reports: &mut [OperationReport],
|
|
2018
2030
|
affected_ids: &[Vec<String>],
|
|
2019
2031
|
) {
|
|
2032
|
+
state.flush_lists();
|
|
2020
2033
|
let wanted = affected_ids.iter().flatten().cloned().collect();
|
|
2021
2034
|
let markup = crate::commands::affected_markup(state, &wanted);
|
|
2022
2035
|
for (report, ids) in reports.iter_mut().zip(affected_ids) {
|
|
@@ -2058,6 +2071,7 @@ fn operation_name(op: &EditOp) -> &'static str {
|
|
|
2058
2071
|
EditOp::InsertParagraph { .. } => "insert_paragraph",
|
|
2059
2072
|
EditOp::DeleteParagraphs { .. } => "delete_paragraphs",
|
|
2060
2073
|
EditOp::SetPageMargins { .. } => "set_page_margins",
|
|
2074
|
+
EditOp::SetSectionBoundary { .. } => "set_section_boundary",
|
|
2061
2075
|
EditOp::SetSectionLayout { .. } => "set_section_layout",
|
|
2062
2076
|
EditOp::SetFirstPageMode { .. } => "set_first_page_mode",
|
|
2063
2077
|
EditOp::SetEvenAndOddHeaders { .. } => "set_even_and_odd_headers",
|
|
@@ -2097,6 +2111,7 @@ fn resolve_typed_aliases(
|
|
|
2097
2111
|
| EditOp::ReplaceParagraph { at, .. }
|
|
2098
2112
|
| EditOp::FormatText { at, .. }
|
|
2099
2113
|
| EditOp::InsertTable { at, .. }
|
|
2114
|
+
| EditOp::SetSectionBoundary { at, .. }
|
|
2100
2115
|
| EditOp::FormatParagraph { at, .. }
|
|
2101
2116
|
| EditOp::CommentAdd { at, .. } => resolve(at)?,
|
|
2102
2117
|
EditOp::ReviseText { at: Some(at), .. } => resolve(at)?,
|
|
@@ -2110,6 +2125,12 @@ fn resolve_typed_aliases(
|
|
|
2110
2125
|
resolve(address)?;
|
|
2111
2126
|
}
|
|
2112
2127
|
}
|
|
2128
|
+
EditOp::SetSectionLayout { section_handle:Some(handle),.. } => {
|
|
2129
|
+
if let Some(alias)=handle.strip_prefix("section:$") {
|
|
2130
|
+
let Some(Value::String(id))=aliases.get(alias) else { return Err(diag("unknown_alias",format!("unknown section paragraph alias ${alias}"),None)); };
|
|
2131
|
+
*handle=format!("section:{id}");
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2113
2134
|
_ => {}
|
|
2114
2135
|
}
|
|
2115
2136
|
Ok(resolved)
|
|
@@ -2305,6 +2326,7 @@ fn typed_read(input: Option<&[u8]>, kind: &str, view: Option<&str>) -> crate::ou
|
|
|
2305
2326
|
map.insert("markup".into(), serde_json::json!(markup));
|
|
2306
2327
|
map.insert("css".into(), serde_json::json!(css));
|
|
2307
2328
|
map.insert("tables".into(), crate::commands::tables::inventory(&state.xml));
|
|
2329
|
+
map.insert("sections".into(), crate::commands::sections::inventory(&state.package,&state.xml,¶graph_ids,view==Some("original")));
|
|
2308
2330
|
map.insert("styles".into(), serde_json::json!(crate::html::styles::Styles::load(&state.package).catalog()));
|
|
2309
2331
|
if !equations.is_empty() {
|
|
2310
2332
|
map.insert("equations".into(), serde_json::json!(equations));
|
|
@@ -2379,6 +2401,7 @@ fn typed_read(input: Option<&[u8]>, kind: &str, view: Option<&str>) -> crate::ou
|
|
|
2379
2401
|
}).collect();
|
|
2380
2402
|
let mut result = result;
|
|
2381
2403
|
if let Some(map) = result.result.as_mut().and_then(Value::as_object_mut) {
|
|
2404
|
+
map.insert("sections".into(),crate::commands::sections::inventory(&state.package,&state.xml,¶graph_ids,view==Some("original")));
|
|
2382
2405
|
if let Some(Value::Array(paragraphs)) = map.get("paragraphs") {
|
|
2383
2406
|
let equations: Vec<Value> = paragraphs
|
|
2384
2407
|
.iter()
|
|
@@ -2409,6 +2432,7 @@ fn typed_read(input: Option<&[u8]>, kind: &str, view: Option<&str>) -> crate::ou
|
|
|
2409
2432
|
serde_json::json!(crate::html::assets::manifest(&state.package, false)),
|
|
2410
2433
|
);
|
|
2411
2434
|
map.insert("tables".into(), crate::commands::tables::inventory(&state.xml));
|
|
2435
|
+
map.insert("sections".into(), crate::commands::sections::inventory(&state.package,&state.xml,¶graph_ids,view==Some("original")));
|
|
2412
2436
|
map.insert("html".into(), Value::String(rendered.html));
|
|
2413
2437
|
}
|
|
2414
2438
|
if let Some(map) = result.result.as_mut().and_then(Value::as_object_mut) {
|
{docxdriver-0.3.2 → docxdriver-0.3.3}/crates/docxdriver-core/src/bin/generate_typed_schema.rs
RENAMED
|
@@ -104,7 +104,7 @@ export type CellPadding = { top?: Point; right?: Point; bottom?: Point; left?: P
|
|
|
104
104
|
export type TableFormat = { width?: TableWidth; column_widths?: Point[]; layout?: 'autofit'|'fixed'; alignment?: 'left'|'center'|'right'; borders?: TableBorders; cell_spacing?: Point; padding?: CellPadding; style?: string; clear?: string[] };
|
|
105
105
|
export type RowFormat = { height?: Point; height_rule?: 'auto'|'at_least'|'exact'; repeating_header?: boolean; allow_split?: boolean; clear?: string[] };
|
|
106
106
|
export type CellFormat = { width?: TableWidth; borders?: TableBorders; fill?: string; padding?: CellPadding; vertical_alignment?: 'top'|'center'|'bottom'; clear?: string[] };
|
|
107
|
-
export type TableEdit = { kind: 'insert_rows'; row: number; position: InsertPosition; count: number } | { kind: 'delete_rows'; row: number; count: number } | { kind: 'insert_columns'; column: number; position: InsertPosition; count: number } | { kind: 'delete_columns'; column: number; count: number } | { kind: 'merge_cells'; row: number; column: number; row_span: number; column_span: number } | { kind: 'split_cell'; row: number; column: number } | { kind: 'delete' };
|
|
107
|
+
export type TableEdit = { kind: 'insert_rows'; row: number; position: InsertPosition; count: number; as?: string } | { kind: 'delete_rows'; row: number; count: number } | { kind: 'insert_columns'; column: number; position: InsertPosition; count: number; as?: string } | { kind: 'delete_columns'; column: number; count: number } | { kind: 'merge_cells'; row: number; column: number; row_span: number; column_span: number } | { kind: 'split_cell'; row: number; column: number } | { kind: 'delete' };
|
|
108
108
|
export type EditOp =
|
|
109
109
|
| { op: 'replace_equation'; at: ParagraphAddress; mathml: string; equation?: number; display?: boolean }
|
|
110
110
|
| { op: 'delete_equation'; at: ParagraphAddress; equation?: number }
|
|
@@ -122,7 +122,8 @@ export type EditOp =
|
|
|
122
122
|
| { op: 'edit_table'; at: string; action: TableEdit }
|
|
123
123
|
| { op: 'insert_table'; at: ParagraphAddress; position: InsertPosition; rows: number; columns: number; as?: string }
|
|
124
124
|
| { op: 'set_page_margins'; section?: number; top?: Point; right?: Point; bottom?: Point; left?: Point; header?: Point; footer?: Point; gutter?: Point }
|
|
125
|
-
| { op: '
|
|
125
|
+
| { op: 'set_section_boundary'; at: ParagraphAddress; remove?: boolean; break_type?: 'nextPage'|'continuous'|'oddPage'|'evenPage'|'nextColumn' }
|
|
126
|
+
| { op: 'set_section_layout'; section?: number; section_handle?: string; page_size?: 'letter'|'legal'|'a4'|'a3'|'a5'; width?: Point; height?: Point; orientation?: 'portrait'|'landscape'; columns?: number; column_spacing?: Point; column_widths?: Point[]; column_separator?: boolean; break_type?: 'nextPage'|'continuous'|'oddPage'|'evenPage'|'nextColumn'; page_number_format?: 'decimal'|'upperRoman'|'lowerRoman'|'upperLetter'|'lowerLetter'; page_number_start?: number; page_number_continue?: boolean }
|
|
126
127
|
| { op: 'set_first_page_mode'; section?: number; enabled: boolean }
|
|
127
128
|
| { op: 'set_even_and_odd_headers'; even_and_odd: boolean }
|
|
128
129
|
| { op: 'set_header'; section?: number; kind?: ChromeKind; with: string }
|
|
@@ -346,19 +346,22 @@ pub fn set_section_layout(
|
|
|
346
346
|
if page_size.is_some() || width.is_some() || orientation.is_some() {
|
|
347
347
|
if (orient == "landscape" && w < h) || (orient == "portrait" && w > h) { std::mem::swap(&mut w, &mut h); }
|
|
348
348
|
}
|
|
349
|
-
let area = w
|
|
349
|
+
let area = w.saturating_sub(dimension("pgMar", "left", 1440)).saturating_sub(dimension("pgMar", "right", 1440)).saturating_sub(dimension("pgMar", "gutter", 0));
|
|
350
350
|
let cols = xml.child_w(sect, "cols");
|
|
351
351
|
let old_widths: Vec<i64> = cols.map(|n| xml.doc.children(n).filter(|&n| xml.is_w(n, "col")).filter_map(|n| xml.attr(n, "w").and_then(|v| v.parse().ok())).collect()).unwrap_or_default();
|
|
352
352
|
let count = columns.map(|v| v as i64).or_else(|| column_widths.as_ref().map(|v| v.len() as i64)).unwrap_or_else(|| dimension("cols", "num", if old_widths.is_empty() { 1 } else { old_widths.len() as i64 }));
|
|
353
353
|
let gap = column_spacing.map(|p| p.0).unwrap_or_else(|| dimension("cols", "space", 720));
|
|
354
|
+
let original_gaps = cols.map(|n| xml.doc.children(n).filter(|&n| xml.is_w(n, "col")).collect::<Vec<_>>()).unwrap_or_default();
|
|
355
|
+
let inherited_gap_total = original_gaps.iter().take(original_gaps.len().saturating_sub(1)).fold(0_i64, |total, &n| total.saturating_add(xml.attr(n, "space").and_then(|v| v.parse::<i64>().ok()).unwrap_or(gap)));
|
|
356
|
+
let gap_total = if column_spacing.is_none() && column_widths.is_none() && columns.is_none() && !old_widths.is_empty() { inherited_gap_total } else { gap.saturating_mul(count.saturating_sub(1)) };
|
|
354
357
|
let unequal: Vec<i64> = column_widths.as_ref().map(|v| v.iter().map(|p| p.0).collect()).unwrap_or_else(|| old_widths.clone());
|
|
355
358
|
let changes_columns = columns.is_some() || column_spacing.is_some() || column_widths.is_some() || column_separator.is_some();
|
|
356
359
|
if changes_columns || width.is_some() || orientation.is_some() || page_size.is_some() {
|
|
357
|
-
if area <= 0 ||
|
|
358
|
-
if !unequal.is_empty() && columns.is_none() && (unequal.iter().
|
|
360
|
+
if area <= 0 || gap_total >= area { return Err(Outcome::blocked("columns do not fit the section text area")); }
|
|
361
|
+
if !unequal.is_empty() && columns.is_none() && (unequal.iter().fold(0_i64, |total, width| total.saturating_add(*width)).saturating_add(gap_total) > area) {
|
|
359
362
|
return Err(Outcome::blocked("column widths and spacing exceed the section text area"));
|
|
360
363
|
}
|
|
361
|
-
if let Some(widths) = column_widths { if widths.iter().
|
|
364
|
+
if let Some(widths) = column_widths { if widths.iter().fold(0_i64, |total, p| total.saturating_add(p.0)).saturating_add(gap_total) > area { return Err(Outcome::blocked("column widths and spacing exceed the section text area")); } }
|
|
362
365
|
}
|
|
363
366
|
if tracked { snapshot_section_properties(xml, sect, author, ctx)?; }
|
|
364
367
|
if page_size.is_some() || width.is_some() || orientation.is_some() {
|
|
@@ -375,6 +378,10 @@ pub fn set_section_layout(
|
|
|
375
378
|
for child in xml.doc.children(node).collect::<Vec<_>>() { if xml.is_w(child, "col") { xml.doc.remove_node(child); } }
|
|
376
379
|
set_attr(xml, node, "w:equalWidth", if column_widths.is_some() { "0" } else { "1" });
|
|
377
380
|
}
|
|
381
|
+
if column_spacing.is_some() && column_widths.is_none() && columns.is_none() {
|
|
382
|
+
let children: Vec<_> = xml.doc.children(node).filter(|&n| xml.is_w(n, "col")).collect();
|
|
383
|
+
for (index, &col) in children.iter().enumerate() { set_twips_attr(xml, col, "space", if index + 1 == children.len() { 0 } else { gap }); }
|
|
384
|
+
}
|
|
378
385
|
if let Some(widths) = column_widths {
|
|
379
386
|
for (index, width) in widths.iter().enumerate() {
|
|
380
387
|
let col = xml.doc.create_element("w:col");
|