docxdriver 0.3.0__tar.gz → 0.3.2__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.0 → docxdriver-0.3.2}/Cargo.lock +5 -5
- {docxdriver-0.3.0 → docxdriver-0.3.2}/Cargo.toml +1 -1
- {docxdriver-0.3.0 → docxdriver-0.3.2}/PKG-INFO +1 -1
- docxdriver-0.3.2/crates/docxdriver-core/examples/list_scaling.rs +71 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/api.rs +348 -7
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/bin/generate_typed_schema.rs +23 -2
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/commands/comments.rs +68 -20
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/commands/headers.rs +2 -20
- docxdriver-0.3.2/crates/docxdriver-core/src/commands/layout.rs +395 -0
- docxdriver-0.3.2/crates/docxdriver-core/src/commands/lists.rs +141 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/commands/mod.rs +88 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/commands/readonly.rs +9 -2
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/commands/revisions.rs +35 -2
- docxdriver-0.3.2/crates/docxdriver-core/src/commands/table_structure.rs +100 -0
- docxdriver-0.3.2/crates/docxdriver-core/src/commands/tables.rs +128 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/commands/typed.rs +360 -178
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/document.rs +46 -21
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/html/build.rs +48 -23
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/html/chrome.rs +3 -1
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/html/ctx.rs +44 -5
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/html/input.rs +66 -9
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/html/mod.rs +2 -8
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/html/parse.rs +11 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/html/render.rs +14 -4
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/html/styles.rs +92 -30
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/html/surface.rs +24 -1
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/segments.rs +1 -23
- docxdriver-0.3.2/crates/docxdriver-core/tests/formatting_fidelity.rs +840 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/tests/html_e2e.rs +13 -22
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/tests/typed_api.rs +244 -16
- docxdriver-0.3.2/crates/docxdriver-python/python/docxdriver/repl/prelude.py +3 -0
- docxdriver-0.3.2/python/docxdriver/repl/prelude.py +3 -0
- docxdriver-0.3.0/crates/docxdriver-core/src/commands/layout.rs +0 -243
- docxdriver-0.3.0/crates/docxdriver-python/python/docxdriver/repl/prelude.py +0 -3
- docxdriver-0.3.0/python/docxdriver/repl/prelude.py +0 -3
- {docxdriver-0.3.0 → docxdriver-0.3.2}/README.md +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/Cargo.toml +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/README.md +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/assets/blank.docx +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/examples/plan_scaling.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/commands/equations.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/commands/reading.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/html/README.md +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/html/assets.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/html/compact.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/html/mathml_omml.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/html/omml_latex.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/html/omml_mathml.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/lib.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/media.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/outcome.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/src/package.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/tests/README.md +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/tests/fixtures/projection-parity/features.json +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/tests/fixtures/projection-parity/parity.docx +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/tests/fixtures/projection-parity/prior-projection.json +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/tests/fixtures/ts-reference.docx +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/tests/interop.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/tests/package_layout.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/tests/projection_parity.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-core/tests/spike.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-python/.gitignore +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-python/Cargo.toml +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-python/README.md +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-python/python/docxdriver/__init__.py +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-python/python/docxdriver/repl/__init__.py +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-python/python/docxdriver/repl/host.py +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-python/python/docxdriver/repl/quotes.py +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-python/src/lib.rs +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/crates/docxdriver-python/tests/test_host.py +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/pyproject.toml +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/python/docxdriver/__init__.py +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/python/docxdriver/repl/__init__.py +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/python/docxdriver/repl/host.py +0 -0
- {docxdriver-0.3.0 → docxdriver-0.3.2}/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.2"
|
|
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.2"
|
|
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.2"
|
|
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.2"
|
|
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.2"
|
|
475
475
|
dependencies = [
|
|
476
476
|
"docxdriver-core",
|
|
477
477
|
"js-sys",
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//! List definition scaling probe: `cargo run --release -p docxdriver-core --example list_scaling -- PARAS OPS`.
|
|
2
|
+
use std::io::Write;
|
|
3
|
+
use std::time::Instant;
|
|
4
|
+
|
|
5
|
+
use docxdriver_core::{
|
|
6
|
+
execute_plan, ChangeMode, EditOp, ParagraphAddress, Plan, PlanRequest, PlanResult, SourceHash,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
fn package(body: &str) -> Vec<u8> {
|
|
10
|
+
let mut w = zip::ZipWriter::new(std::io::Cursor::new(Vec::new()));
|
|
11
|
+
let o = zip::write::SimpleFileOptions::default();
|
|
12
|
+
let parts = [
|
|
13
|
+
("[Content_Types].xml", "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\"><Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/><Default Extension=\"xml\" ContentType=\"application/xml\"/><Override PartName=\"/word/document.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml\"/></Types>".to_string()),
|
|
14
|
+
("_rels/.rels", "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\" Target=\"word/document.xml\"/></Relationships>".to_string()),
|
|
15
|
+
("word/document.xml", body.to_string()),
|
|
16
|
+
("word/_rels/document.xml.rels", "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"></Relationships>".to_string()),
|
|
17
|
+
];
|
|
18
|
+
for (n, c) in parts {
|
|
19
|
+
w.start_file(n, o).unwrap();
|
|
20
|
+
w.write_all(c.as_bytes()).unwrap();
|
|
21
|
+
}
|
|
22
|
+
w.finish().unwrap().into_inner()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
fn main() {
|
|
26
|
+
let args: Vec<usize> = std::env::args()
|
|
27
|
+
.skip(1)
|
|
28
|
+
.map(|a| a.parse().unwrap())
|
|
29
|
+
.collect();
|
|
30
|
+
let (paras, ops) = (
|
|
31
|
+
args.first().copied().unwrap_or(1500),
|
|
32
|
+
args.get(1).copied().unwrap_or(300),
|
|
33
|
+
);
|
|
34
|
+
let body: String = (0..paras)
|
|
35
|
+
.map(|i| format!("<w:p w14:paraId=\"{:08X}\"><w:r><w:t>Paragraph {i} says the tenant shall pay rent within thirty days of notice.</w:t></w:r></w:p>", i + 1))
|
|
36
|
+
.collect();
|
|
37
|
+
let bytes = package(&format!("<?xml version=\"1.0\" encoding=\"UTF-8\"?><w:document xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:w14=\"http://schemas.microsoft.com/office/word/2010/wordml\"><w:body>{body}</w:body></w:document>"));
|
|
38
|
+
let operations = (0..ops)
|
|
39
|
+
.map(|i| EditOp::SetList {
|
|
40
|
+
at: vec![ParagraphAddress::id(format!(
|
|
41
|
+
"{:08X}",
|
|
42
|
+
(i * paras / ops) + 1
|
|
43
|
+
))],
|
|
44
|
+
num_id: None,
|
|
45
|
+
format: Some("decimal".into()),
|
|
46
|
+
level: 0,
|
|
47
|
+
start: None,
|
|
48
|
+
remove: false,
|
|
49
|
+
levels: None,
|
|
50
|
+
})
|
|
51
|
+
.collect();
|
|
52
|
+
let plan = Plan {
|
|
53
|
+
base: SourceHash::from_bytes(&bytes),
|
|
54
|
+
author: "bench".into(),
|
|
55
|
+
change_mode: ChangeMode::Track,
|
|
56
|
+
ops: operations,
|
|
57
|
+
};
|
|
58
|
+
let t = Instant::now();
|
|
59
|
+
let result = execute_plan(
|
|
60
|
+
&bytes,
|
|
61
|
+
&PlanRequest {
|
|
62
|
+
plan,
|
|
63
|
+
preview_key: None,
|
|
64
|
+
},
|
|
65
|
+
);
|
|
66
|
+
let ok = matches!(result, PlanResult::Previewed { .. });
|
|
67
|
+
println!(
|
|
68
|
+
"paras={paras} ops={ops} previewed={ok} elapsed={:?}",
|
|
69
|
+
t.elapsed()
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -10,6 +10,164 @@ use serde_json::{Map, Value};
|
|
|
10
10
|
use sha2::{Digest, Sha256};
|
|
11
11
|
use std::collections::BTreeMap;
|
|
12
12
|
|
|
13
|
+
/// The exact ECMA-376 ST_HighlightColor palette; None is an explicit off override.
|
|
14
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
|
15
|
+
#[serde(rename_all = "camelCase")]
|
|
16
|
+
pub enum HighlightColor {
|
|
17
|
+
Black,
|
|
18
|
+
Blue,
|
|
19
|
+
Cyan,
|
|
20
|
+
Green,
|
|
21
|
+
Magenta,
|
|
22
|
+
Red,
|
|
23
|
+
Yellow,
|
|
24
|
+
White,
|
|
25
|
+
DarkBlue,
|
|
26
|
+
DarkCyan,
|
|
27
|
+
DarkGreen,
|
|
28
|
+
DarkMagenta,
|
|
29
|
+
DarkRed,
|
|
30
|
+
DarkYellow,
|
|
31
|
+
DarkGray,
|
|
32
|
+
LightGray,
|
|
33
|
+
None,
|
|
34
|
+
}
|
|
35
|
+
impl HighlightColor {
|
|
36
|
+
pub fn as_str(self) -> &'static str { match self {
|
|
37
|
+
Self::Black => "black",
|
|
38
|
+
Self::Blue => "blue",
|
|
39
|
+
Self::Cyan => "cyan",
|
|
40
|
+
Self::Green => "green",
|
|
41
|
+
Self::Magenta => "magenta",
|
|
42
|
+
Self::Red => "red",
|
|
43
|
+
Self::Yellow => "yellow",
|
|
44
|
+
Self::White => "white",
|
|
45
|
+
Self::DarkBlue => "darkBlue",
|
|
46
|
+
Self::DarkCyan => "darkCyan",
|
|
47
|
+
Self::DarkGreen => "darkGreen",
|
|
48
|
+
Self::DarkMagenta => "darkMagenta",
|
|
49
|
+
Self::DarkRed => "darkRed",
|
|
50
|
+
Self::DarkYellow => "darkYellow",
|
|
51
|
+
Self::DarkGray => "darkGray",
|
|
52
|
+
Self::LightGray => "lightGray",
|
|
53
|
+
Self::None => "none",
|
|
54
|
+
}}
|
|
55
|
+
pub fn css(self) -> &'static str { match self {
|
|
56
|
+
Self::Black => "#000000",
|
|
57
|
+
Self::Blue => "#0000FF",
|
|
58
|
+
Self::Cyan => "#00FFFF",
|
|
59
|
+
Self::Green => "#00FF00",
|
|
60
|
+
Self::Magenta => "#FF00FF",
|
|
61
|
+
Self::Red => "#FF0000",
|
|
62
|
+
Self::Yellow => "#FFFF00",
|
|
63
|
+
Self::White => "#FFFFFF",
|
|
64
|
+
Self::DarkBlue => "#000080",
|
|
65
|
+
Self::DarkCyan => "#008080",
|
|
66
|
+
Self::DarkGreen => "#008000",
|
|
67
|
+
Self::DarkMagenta => "#800080",
|
|
68
|
+
Self::DarkRed => "#800000",
|
|
69
|
+
Self::DarkYellow => "#808000",
|
|
70
|
+
Self::DarkGray => "#808080",
|
|
71
|
+
Self::LightGray => "#C0C0C0",
|
|
72
|
+
Self::None => "transparent",
|
|
73
|
+
}}
|
|
74
|
+
pub fn parse(value: &str) -> Result<Self, String> { match value {
|
|
75
|
+
"black" => Ok(Self::Black),
|
|
76
|
+
"blue" => Ok(Self::Blue),
|
|
77
|
+
"cyan" => Ok(Self::Cyan),
|
|
78
|
+
"green" => Ok(Self::Green),
|
|
79
|
+
"magenta" => Ok(Self::Magenta),
|
|
80
|
+
"red" => Ok(Self::Red),
|
|
81
|
+
"yellow" => Ok(Self::Yellow),
|
|
82
|
+
"white" => Ok(Self::White),
|
|
83
|
+
"darkBlue" => Ok(Self::DarkBlue),
|
|
84
|
+
"darkCyan" => Ok(Self::DarkCyan),
|
|
85
|
+
"darkGreen" => Ok(Self::DarkGreen),
|
|
86
|
+
"darkMagenta" => Ok(Self::DarkMagenta),
|
|
87
|
+
"darkRed" => Ok(Self::DarkRed),
|
|
88
|
+
"darkYellow" => Ok(Self::DarkYellow),
|
|
89
|
+
"darkGray" => Ok(Self::DarkGray),
|
|
90
|
+
"lightGray" => Ok(Self::LightGray),
|
|
91
|
+
"none" => Ok(Self::None),
|
|
92
|
+
_ => Err(format!("unknown highlight palette value {value:?}")),
|
|
93
|
+
}}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/// Table widths use auto, points, or percent (0 < value <= 100 for percent).
|
|
97
|
+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
|
|
98
|
+
#[serde(tag = "unit", rename_all = "snake_case", deny_unknown_fields)]
|
|
99
|
+
pub enum TableWidth { Auto, Points { value: Points }, Percent { value: f64 } }
|
|
100
|
+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
|
|
101
|
+
#[serde(deny_unknown_fields)]
|
|
102
|
+
pub struct TableBorder {
|
|
103
|
+
pub style: String,
|
|
104
|
+
#[serde(default)] pub color: Option<String>,
|
|
105
|
+
#[serde(default)] pub width: Option<Points>,
|
|
106
|
+
}
|
|
107
|
+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Default)]
|
|
108
|
+
#[serde(deny_unknown_fields)]
|
|
109
|
+
pub struct TableBorders {
|
|
110
|
+
#[serde(default)] pub top: Option<TableBorder>,
|
|
111
|
+
#[serde(default)] pub left: Option<TableBorder>,
|
|
112
|
+
#[serde(default)] pub bottom: Option<TableBorder>,
|
|
113
|
+
#[serde(default)] pub right: Option<TableBorder>,
|
|
114
|
+
#[serde(default)] pub inside_horizontal: Option<TableBorder>,
|
|
115
|
+
#[serde(default)] pub inside_vertical: Option<TableBorder>,
|
|
116
|
+
}
|
|
117
|
+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Default)]
|
|
118
|
+
#[serde(deny_unknown_fields)]
|
|
119
|
+
pub struct CellPadding {
|
|
120
|
+
#[serde(default)] pub top: Option<Points>,
|
|
121
|
+
#[serde(default)] pub right: Option<Points>,
|
|
122
|
+
#[serde(default)] pub bottom: Option<Points>,
|
|
123
|
+
#[serde(default)] pub left: Option<Points>,
|
|
124
|
+
}
|
|
125
|
+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Default)]
|
|
126
|
+
#[serde(deny_unknown_fields)]
|
|
127
|
+
pub struct TableFormat {
|
|
128
|
+
#[serde(default)] pub width: Option<TableWidth>,
|
|
129
|
+
#[serde(default)] pub column_widths: Option<Vec<Points>>,
|
|
130
|
+
#[serde(default)] pub layout: Option<String>,
|
|
131
|
+
#[serde(default)] pub alignment: Option<String>,
|
|
132
|
+
#[serde(default)] pub borders: Option<TableBorders>,
|
|
133
|
+
#[serde(default)] pub cell_spacing: Option<Points>,
|
|
134
|
+
#[serde(default)] pub padding: Option<CellPadding>,
|
|
135
|
+
#[serde(default)] pub style: Option<String>,
|
|
136
|
+
#[serde(default)] pub clear: Vec<String>,
|
|
137
|
+
}
|
|
138
|
+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Default)]
|
|
139
|
+
#[serde(deny_unknown_fields)]
|
|
140
|
+
pub struct CellFormat {
|
|
141
|
+
#[serde(default)] pub width: Option<TableWidth>,
|
|
142
|
+
#[serde(default)] pub borders: Option<TableBorders>,
|
|
143
|
+
#[serde(default)] pub fill: Option<String>,
|
|
144
|
+
#[serde(default)] pub padding: Option<CellPadding>,
|
|
145
|
+
#[serde(default)] pub vertical_alignment: Option<String>,
|
|
146
|
+
#[serde(default)] pub clear: Vec<String>,
|
|
147
|
+
}
|
|
148
|
+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Default)]
|
|
149
|
+
#[serde(deny_unknown_fields)]
|
|
150
|
+
pub struct RowFormat {
|
|
151
|
+
#[serde(default)] pub height: Option<Points>,
|
|
152
|
+
#[serde(default)] pub height_rule: Option<String>,
|
|
153
|
+
#[serde(default)] pub repeating_header: Option<bool>,
|
|
154
|
+
#[serde(default)] pub allow_split: Option<bool>,
|
|
155
|
+
#[serde(default)] pub clear: Vec<String>,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// Direct structural table edits. Row and column positions are one-based.
|
|
159
|
+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
|
|
160
|
+
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
|
|
161
|
+
pub enum TableEdit {
|
|
162
|
+
InsertRows { row: u32, position: InsertPosition, count: u32 },
|
|
163
|
+
DeleteRows { row: u32, count: u32 },
|
|
164
|
+
InsertColumns { column: u32, position: InsertPosition, count: u32 },
|
|
165
|
+
DeleteColumns { column: u32, count: u32 },
|
|
166
|
+
MergeCells { row: u32, column: u32, row_span: u32, column_span: u32 },
|
|
167
|
+
SplitCell { row: u32, column: u32 },
|
|
168
|
+
Delete,
|
|
169
|
+
}
|
|
170
|
+
|
|
13
171
|
pub const EDIT_PROTOCOL_VERSION: &str = "1";
|
|
14
172
|
|
|
15
173
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, JsonSchema)]
|
|
@@ -98,6 +256,34 @@ impl<'de> Deserialize<'de> for Points {
|
|
|
98
256
|
}
|
|
99
257
|
}
|
|
100
258
|
|
|
259
|
+
/// Signed paragraph indentation, in twentieths of a point. Other dimensions use `Points`.
|
|
260
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, JsonSchema)]
|
|
261
|
+
#[schemars(with = "f64")]
|
|
262
|
+
pub struct SignedPoints(pub i64);
|
|
263
|
+
impl SignedPoints {
|
|
264
|
+
pub fn from_points(value: f64) -> Result<Self, String> {
|
|
265
|
+
if !value.is_finite() || value.abs() > 100000.0 {
|
|
266
|
+
return Err("indentation must be finite and between -100000 and 100000 points".into());
|
|
267
|
+
}
|
|
268
|
+
let n = (value * 20.0).round();
|
|
269
|
+
if (value * 20.0 - n).abs() > 1e-8 {
|
|
270
|
+
return Err("point values must be multiples of 0.05".into());
|
|
271
|
+
}
|
|
272
|
+
Ok(Self(n as i64))
|
|
273
|
+
}
|
|
274
|
+
pub fn as_points(self) -> f64 { self.0 as f64 / 20.0 }
|
|
275
|
+
}
|
|
276
|
+
impl Serialize for SignedPoints {
|
|
277
|
+
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
|
|
278
|
+
s.serialize_f64(self.as_points())
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
impl<'de> Deserialize<'de> for SignedPoints {
|
|
282
|
+
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
|
|
283
|
+
Self::from_points(f64::deserialize(d)?).map_err(serde::de::Error::custom)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
101
287
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, JsonSchema)]
|
|
102
288
|
#[schemars(with = "String")]
|
|
103
289
|
pub enum ParagraphAddress {
|
|
@@ -317,6 +503,24 @@ pub enum LineSpacing {
|
|
|
317
503
|
AtLeast(Points),
|
|
318
504
|
}
|
|
319
505
|
|
|
506
|
+
/// One bounded level in a new multilevel list definition. Array position is level (0..8).
|
|
507
|
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
|
508
|
+
#[serde(deny_unknown_fields)]
|
|
509
|
+
pub struct ListLevel {
|
|
510
|
+
pub format: String,
|
|
511
|
+
pub label: String,
|
|
512
|
+
#[serde(default)] pub start: Option<u32>,
|
|
513
|
+
/// Restart after this zero-based ancestor level; omitted uses Word's default.
|
|
514
|
+
#[serde(default)] pub restart_after: Option<u8>,
|
|
515
|
+
#[serde(default)] pub indent_left: Option<SignedPoints>,
|
|
516
|
+
#[serde(default)] pub indent_right: Option<SignedPoints>,
|
|
517
|
+
#[serde(default)] pub first_line_indent: Option<SignedPoints>,
|
|
518
|
+
#[serde(default)] pub marker_bold: Option<bool>,
|
|
519
|
+
#[serde(default)] pub marker_italic: Option<bool>,
|
|
520
|
+
#[serde(default)] pub marker_font: Option<String>,
|
|
521
|
+
#[serde(default)] pub marker_color: Option<String>,
|
|
522
|
+
}
|
|
523
|
+
|
|
320
524
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)]
|
|
321
525
|
#[serde(tag = "op", rename_all = "snake_case", deny_unknown_fields)]
|
|
322
526
|
pub enum EditOp {
|
|
@@ -359,6 +563,11 @@ pub enum EditOp {
|
|
|
359
563
|
at: ParagraphAddress,
|
|
360
564
|
with: String,
|
|
361
565
|
},
|
|
566
|
+
EditTable { at: String, action: TableEdit },
|
|
567
|
+
InsertTable { at: ParagraphAddress, position: InsertPosition, rows: u32, columns: u32, #[serde(rename = "as", default)] alias: Option<String> },
|
|
568
|
+
FormatTable { at: String, format: TableFormat },
|
|
569
|
+
FormatRow { at: String, format: RowFormat },
|
|
570
|
+
FormatCell { at: String, format: CellFormat },
|
|
362
571
|
FormatText {
|
|
363
572
|
at: ParagraphAddress,
|
|
364
573
|
select: String,
|
|
@@ -379,6 +588,8 @@ pub enum EditOp {
|
|
|
379
588
|
#[serde(default)]
|
|
380
589
|
color: Option<String>,
|
|
381
590
|
#[serde(default)]
|
|
591
|
+
highlight: Option<HighlightColor>,
|
|
592
|
+
#[serde(default)]
|
|
382
593
|
font_size: Option<Points>,
|
|
383
594
|
#[serde(default)]
|
|
384
595
|
clear: Vec<String>,
|
|
@@ -390,9 +601,12 @@ pub enum EditOp {
|
|
|
390
601
|
#[serde(default)]
|
|
391
602
|
alignment: Option<String>,
|
|
392
603
|
#[serde(default)]
|
|
393
|
-
indent_left: Option<
|
|
604
|
+
indent_left: Option<SignedPoints>,
|
|
394
605
|
#[serde(default)]
|
|
395
|
-
indent_right: Option<
|
|
606
|
+
indent_right: Option<SignedPoints>,
|
|
607
|
+
/// Signed first-line offset: negative means hanging; zero explicitly disables it.
|
|
608
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
609
|
+
first_line_indent: Option<SignedPoints>,
|
|
396
610
|
#[serde(default)]
|
|
397
611
|
space_before: Option<Points>,
|
|
398
612
|
#[serde(default)]
|
|
@@ -402,6 +616,23 @@ pub enum EditOp {
|
|
|
402
616
|
#[serde(default)]
|
|
403
617
|
clear: Vec<String>,
|
|
404
618
|
},
|
|
619
|
+
/// Assign selected paragraphs to a new or existing list. `remove` explicitly
|
|
620
|
+
/// suppresses inherited numbering; new definitions remain after rejection.
|
|
621
|
+
SetList {
|
|
622
|
+
#[serde(default)]
|
|
623
|
+
levels: Option<Vec<ListLevel>>,
|
|
624
|
+
at: Vec<ParagraphAddress>,
|
|
625
|
+
#[serde(default)]
|
|
626
|
+
num_id: Option<u32>,
|
|
627
|
+
#[serde(default)]
|
|
628
|
+
format: Option<String>,
|
|
629
|
+
#[serde(default)]
|
|
630
|
+
level: u8,
|
|
631
|
+
#[serde(default)]
|
|
632
|
+
start: Option<u32>,
|
|
633
|
+
#[serde(default)]
|
|
634
|
+
remove: bool,
|
|
635
|
+
},
|
|
405
636
|
InsertParagraph {
|
|
406
637
|
at: InsertAnchor,
|
|
407
638
|
position: InsertPosition,
|
|
@@ -433,6 +664,40 @@ pub enum EditOp {
|
|
|
433
664
|
#[serde(default)]
|
|
434
665
|
gutter: Option<Points>,
|
|
435
666
|
},
|
|
667
|
+
/// Selective section-property edits. Dimensions are points; omitted fields preserve source.
|
|
668
|
+
SetSectionLayout {
|
|
669
|
+
#[serde(default)]
|
|
670
|
+
section: Option<u32>,
|
|
671
|
+
#[serde(default)]
|
|
672
|
+
page_size: Option<String>,
|
|
673
|
+
#[serde(default)]
|
|
674
|
+
width: Option<Points>,
|
|
675
|
+
#[serde(default)]
|
|
676
|
+
height: Option<Points>,
|
|
677
|
+
#[serde(default)]
|
|
678
|
+
orientation: Option<String>,
|
|
679
|
+
#[serde(default)]
|
|
680
|
+
columns: Option<u32>,
|
|
681
|
+
#[serde(default)]
|
|
682
|
+
column_spacing: Option<Points>,
|
|
683
|
+
#[serde(default)]
|
|
684
|
+
column_widths: Option<Vec<Points>>,
|
|
685
|
+
#[serde(default)]
|
|
686
|
+
column_separator: Option<bool>,
|
|
687
|
+
#[serde(default)]
|
|
688
|
+
break_type: Option<String>,
|
|
689
|
+
#[serde(default)]
|
|
690
|
+
page_number_format: Option<String>,
|
|
691
|
+
#[serde(default)]
|
|
692
|
+
page_number_start: Option<u32>,
|
|
693
|
+
#[serde(default)]
|
|
694
|
+
page_number_continue: Option<bool>,
|
|
695
|
+
},
|
|
696
|
+
SetFirstPageMode {
|
|
697
|
+
#[serde(default)]
|
|
698
|
+
section: Option<u32>,
|
|
699
|
+
enabled: bool,
|
|
700
|
+
},
|
|
436
701
|
SetEvenAndOddHeaders {
|
|
437
702
|
even_and_odd: bool,
|
|
438
703
|
},
|
|
@@ -1093,6 +1358,14 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
|
|
|
1093
1358
|
}
|
|
1094
1359
|
}
|
|
1095
1360
|
}
|
|
1361
|
+
EditOp::EditTable { at, action } => crate::commands::table_structure::validate(at, action)?,
|
|
1362
|
+
EditOp::InsertTable { rows, columns, alias, .. } => {
|
|
1363
|
+
if *rows == 0 || *columns == 0 || *rows > 100 || *columns > 100 { return Err("insert_table dimensions must be 1..100".into()); }
|
|
1364
|
+
if alias.as_ref().is_some_and(|a| !valid_alias(a)) { return Err("invalid alias".into()); }
|
|
1365
|
+
}
|
|
1366
|
+
EditOp::FormatTable { at, format } => crate::commands::tables::validate_table(at, format)?,
|
|
1367
|
+
EditOp::FormatRow { at, format } => crate::commands::tables::validate_row(at, format)?,
|
|
1368
|
+
EditOp::FormatCell { at, format } => crate::commands::tables::validate_cell(at, format)?,
|
|
1096
1369
|
EditOp::ReplaceParagraph { .. } => {}
|
|
1097
1370
|
EditOp::FormatText {
|
|
1098
1371
|
select,
|
|
@@ -1104,6 +1377,7 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
|
|
|
1104
1377
|
superscript,
|
|
1105
1378
|
subscript,
|
|
1106
1379
|
color,
|
|
1380
|
+
highlight,
|
|
1107
1381
|
font_size,
|
|
1108
1382
|
clear,
|
|
1109
1383
|
..
|
|
@@ -1133,6 +1407,7 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
|
|
|
1133
1407
|
&& strike.is_none()
|
|
1134
1408
|
&& superscript.is_none()
|
|
1135
1409
|
&& subscript.is_none()
|
|
1410
|
+
&& highlight.is_none()
|
|
1136
1411
|
&& color.is_none()
|
|
1137
1412
|
&& font_size.is_none()
|
|
1138
1413
|
&& clear.is_empty()
|
|
@@ -1147,6 +1422,7 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
|
|
|
1147
1422
|
"strike",
|
|
1148
1423
|
"superscript",
|
|
1149
1424
|
"subscript",
|
|
1425
|
+
"highlight",
|
|
1150
1426
|
"color",
|
|
1151
1427
|
"font_size",
|
|
1152
1428
|
]
|
|
@@ -1160,12 +1436,16 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
|
|
|
1160
1436
|
alignment,
|
|
1161
1437
|
indent_left,
|
|
1162
1438
|
indent_right,
|
|
1439
|
+
first_line_indent,
|
|
1163
1440
|
space_before,
|
|
1164
1441
|
space_after,
|
|
1165
1442
|
line_spacing,
|
|
1166
1443
|
clear,
|
|
1167
1444
|
..
|
|
1168
1445
|
} => {
|
|
1446
|
+
if [*indent_left, *indent_right, *first_line_indent].into_iter().flatten().any(|v| v.0.unsigned_abs() > 2_000_000) {
|
|
1447
|
+
return Err("paragraph indentation must be between -100000 and 100000 points".into());
|
|
1448
|
+
}
|
|
1169
1449
|
if let Some(s) = style {
|
|
1170
1450
|
if s.is_empty() {
|
|
1171
1451
|
return Err("style must be non-empty".into());
|
|
@@ -1183,6 +1463,7 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
|
|
|
1183
1463
|
}
|
|
1184
1464
|
if indent_left.is_none()
|
|
1185
1465
|
&& indent_right.is_none()
|
|
1466
|
+
&& first_line_indent.is_none()
|
|
1186
1467
|
&& space_before.is_none()
|
|
1187
1468
|
&& space_after.is_none()
|
|
1188
1469
|
&& line_spacing.is_none()
|
|
@@ -1193,6 +1474,36 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
|
|
|
1193
1474
|
return Err("format_paragraph requires a property".into());
|
|
1194
1475
|
}
|
|
1195
1476
|
}
|
|
1477
|
+
EditOp::SetList { at, num_id, format, level, start, remove, levels } => {
|
|
1478
|
+
if let Some(defs) = levels {
|
|
1479
|
+
if defs.is_empty() || defs.len() > 9 || num_id.is_some() || *remove || format.is_some() || *level as usize >= defs.len() {
|
|
1480
|
+
return Err("levels requires a new list with 1..9 definitions covering the selected level".into());
|
|
1481
|
+
}
|
|
1482
|
+
for (i, def) in defs.iter().enumerate() {
|
|
1483
|
+
if !["bullet","decimal","lowerLetter","upperLetter","lowerRoman","upperRoman"].contains(&def.format.as_str()) || def.start == Some(0) || def.label.is_empty() || def.label.len() > 255 || def.restart_after.is_some_and(|v| v as usize >= i) {
|
|
1484
|
+
return Err("invalid list level format, label, start or restart ancestor".into());
|
|
1485
|
+
}
|
|
1486
|
+
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()); }
|
|
1487
|
+
let bytes = def.label.as_bytes();
|
|
1488
|
+
for j in 0..bytes.len() {
|
|
1489
|
+
if bytes[j] == b'%' && (j + 1 == bytes.len() || !(b'1'..=b'1' + i as u8).contains(&bytes[j+1])) { return Err("list labels may only reference this level or ancestor %1..%9 counters".into()); }
|
|
1490
|
+
}
|
|
1491
|
+
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
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
if at.is_empty() || *level > 8 || *start == Some(0) || *num_id == Some(0) {
|
|
1495
|
+
return Err("set_list requires paragraphs, level 0..8 and positive IDs/starts".into());
|
|
1496
|
+
}
|
|
1497
|
+
if *remove && (num_id.is_some() || format.is_some() || start.is_some() || *level != 0) {
|
|
1498
|
+
return Err("set_list remove cannot be combined with list settings".into());
|
|
1499
|
+
}
|
|
1500
|
+
if let Some(fmt) = format {
|
|
1501
|
+
if !["bullet", "decimal", "lowerLetter", "upperLetter", "lowerRoman", "upperRoman"].contains(&fmt.as_str()) {
|
|
1502
|
+
return Err("unsupported set_list format".into());
|
|
1503
|
+
}
|
|
1504
|
+
if num_id.is_some() { return Err("format cannot restyle an existing list; create a new list".into()); }
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1196
1507
|
EditOp::InsertParagraph { alias, .. } => {
|
|
1197
1508
|
if let Some(a) = alias {
|
|
1198
1509
|
if !valid_alias(a) {
|
|
@@ -1205,6 +1516,22 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
|
|
|
1205
1516
|
return Err("at must not be empty".into());
|
|
1206
1517
|
}
|
|
1207
1518
|
}
|
|
1519
|
+
EditOp::SetSectionLayout { section, page_size, width, height, orientation, columns, column_spacing, column_widths, column_separator, break_type, page_number_format, page_number_start, page_number_continue } => {
|
|
1520
|
+
if section == &Some(0) { return Err("section must be positive".into()); }
|
|
1521
|
+
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
|
+
if page_size.is_some() && (width.is_some() || height.is_some()) { return Err("page_size conflicts with width/height".into()); }
|
|
1523
|
+
if width.is_some() != height.is_some() { return Err("width and height must be supplied together".into()); }
|
|
1524
|
+
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
|
+
if orientation.as_ref().is_some_and(|v| !matches!(v.as_str(), "portrait" | "landscape")) { return Err("orientation must be portrait/landscape".into()); }
|
|
1526
|
+
if columns.is_some_and(|v| v == 0 || v > 45) { return Err("columns must be between 1 and 45".into()); }
|
|
1527
|
+
if column_widths.as_ref().is_some_and(|v| v.is_empty() || v.len() > 45 || v.iter().any(|p| p.0 <= 0)) { return Err("column_widths requires 1..45 positive widths".into()); }
|
|
1528
|
+
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
|
+
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
|
+
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 page_number_start == &Some(0) { return Err("page_number_start must be positive".into()); }
|
|
1532
|
+
if page_number_start.is_some() && page_number_continue == &Some(true) { return Err("page_number_start conflicts with page_number_continue".into()); }
|
|
1533
|
+
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
|
+
}
|
|
1208
1535
|
EditOp::SetPageMargins {
|
|
1209
1536
|
section,
|
|
1210
1537
|
top,
|
|
@@ -1271,7 +1598,8 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
|
|
|
1271
1598
|
..
|
|
1272
1599
|
}
|
|
1273
1600
|
| EditOp::SetEvenAndOddHeaders { .. } => {}
|
|
1274
|
-
EditOp::
|
|
1601
|
+
EditOp::SetFirstPageMode { section, .. }
|
|
1602
|
+
| EditOp::SetHeader { section, .. }
|
|
1275
1603
|
| EditOp::SetFooter { section, .. }
|
|
1276
1604
|
| EditOp::ClearHeader { section, .. }
|
|
1277
1605
|
| EditOp::ClearFooter { section, .. } => {
|
|
@@ -1563,9 +1891,8 @@ fn fold_typed(
|
|
|
1563
1891
|
)),
|
|
1564
1892
|
);
|
|
1565
1893
|
}
|
|
1566
|
-
if let EditOp::InsertParagraph {
|
|
1567
|
-
alias: Some(alias), ..
|
|
1568
|
-
} = op
|
|
1894
|
+
if let EditOp::InsertParagraph { alias: Some(alias), .. }
|
|
1895
|
+
| EditOp::InsertTable { alias: Some(alias), .. } = op
|
|
1569
1896
|
{
|
|
1570
1897
|
if aliases.contains_key(alias) {
|
|
1571
1898
|
attach_affected(&state, &mut reports, &affected_ids);
|
|
@@ -1590,6 +1917,9 @@ fn fold_typed(
|
|
|
1590
1917
|
)),
|
|
1591
1918
|
);
|
|
1592
1919
|
}
|
|
1920
|
+
if let EditOp::InsertTable { .. } = op {
|
|
1921
|
+
aliases.insert(alias.clone(), Value::String(format!("${alias}")));
|
|
1922
|
+
}
|
|
1593
1923
|
if let Some(id) = outcome
|
|
1594
1924
|
.result
|
|
1595
1925
|
.as_ref()
|
|
@@ -1717,11 +2047,19 @@ fn operation_name(op: &EditOp) -> &'static str {
|
|
|
1717
2047
|
EditOp::ReplaceText { .. } => "replace_text",
|
|
1718
2048
|
EditOp::ReviseText { .. } => "revise_text",
|
|
1719
2049
|
EditOp::ReplaceParagraph { .. } => "replace_paragraph",
|
|
2050
|
+
EditOp::EditTable { .. } => "edit_table",
|
|
2051
|
+
EditOp::InsertTable { .. } => "insert_table",
|
|
2052
|
+
EditOp::FormatTable { .. } => "format_table",
|
|
2053
|
+
EditOp::FormatRow { .. } => "format_row",
|
|
2054
|
+
EditOp::FormatCell { .. } => "format_cell",
|
|
1720
2055
|
EditOp::FormatText { .. } => "format_text",
|
|
1721
2056
|
EditOp::FormatParagraph { .. } => "format_paragraph",
|
|
2057
|
+
EditOp::SetList { .. } => "set_list",
|
|
1722
2058
|
EditOp::InsertParagraph { .. } => "insert_paragraph",
|
|
1723
2059
|
EditOp::DeleteParagraphs { .. } => "delete_paragraphs",
|
|
1724
2060
|
EditOp::SetPageMargins { .. } => "set_page_margins",
|
|
2061
|
+
EditOp::SetSectionLayout { .. } => "set_section_layout",
|
|
2062
|
+
EditOp::SetFirstPageMode { .. } => "set_first_page_mode",
|
|
1725
2063
|
EditOp::SetEvenAndOddHeaders { .. } => "set_even_and_odd_headers",
|
|
1726
2064
|
EditOp::SetHeader { .. } => "set_header",
|
|
1727
2065
|
EditOp::SetFooter { .. } => "set_footer",
|
|
@@ -1758,6 +2096,7 @@ fn resolve_typed_aliases(
|
|
|
1758
2096
|
| EditOp::ReplaceText { at, .. }
|
|
1759
2097
|
| EditOp::ReplaceParagraph { at, .. }
|
|
1760
2098
|
| EditOp::FormatText { at, .. }
|
|
2099
|
+
| EditOp::InsertTable { at, .. }
|
|
1761
2100
|
| EditOp::FormatParagraph { at, .. }
|
|
1762
2101
|
| EditOp::CommentAdd { at, .. } => resolve(at)?,
|
|
1763
2102
|
EditOp::ReviseText { at: Some(at), .. } => resolve(at)?,
|
|
@@ -1766,7 +2105,7 @@ fn resolve_typed_aliases(
|
|
|
1766
2105
|
resolve(addr)?;
|
|
1767
2106
|
}
|
|
1768
2107
|
}
|
|
1769
|
-
EditOp::DeleteParagraphs { at } => {
|
|
2108
|
+
EditOp::DeleteParagraphs { at } | EditOp::SetList { at, .. } => {
|
|
1770
2109
|
for address in at {
|
|
1771
2110
|
resolve(address)?;
|
|
1772
2111
|
}
|
|
@@ -1965,6 +2304,7 @@ fn typed_read(input: Option<&[u8]>, kind: &str, view: Option<&str>) -> crate::ou
|
|
|
1965
2304
|
let (markup, css) = crate::html::compact::agent_html(&rendered.html);
|
|
1966
2305
|
map.insert("markup".into(), serde_json::json!(markup));
|
|
1967
2306
|
map.insert("css".into(), serde_json::json!(css));
|
|
2307
|
+
map.insert("tables".into(), crate::commands::tables::inventory(&state.xml));
|
|
1968
2308
|
map.insert("styles".into(), serde_json::json!(crate::html::styles::Styles::load(&state.package).catalog()));
|
|
1969
2309
|
if !equations.is_empty() {
|
|
1970
2310
|
map.insert("equations".into(), serde_json::json!(equations));
|
|
@@ -2068,6 +2408,7 @@ fn typed_read(input: Option<&[u8]>, kind: &str, view: Option<&str>) -> crate::ou
|
|
|
2068
2408
|
"assets".into(),
|
|
2069
2409
|
serde_json::json!(crate::html::assets::manifest(&state.package, false)),
|
|
2070
2410
|
);
|
|
2411
|
+
map.insert("tables".into(), crate::commands::tables::inventory(&state.xml));
|
|
2071
2412
|
map.insert("html".into(), Value::String(rendered.html));
|
|
2072
2413
|
}
|
|
2073
2414
|
if let Some(map) = result.result.as_mut().and_then(Value::as_object_mut) {
|