docxdriver 0.4.2__tar.gz → 0.4.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.
Files changed (61) hide show
  1. {docxdriver-0.4.2 → docxdriver-0.4.3}/Cargo.lock +6 -6
  2. {docxdriver-0.4.2 → docxdriver-0.4.3}/Cargo.toml +1 -1
  3. {docxdriver-0.4.2 → docxdriver-0.4.3}/PKG-INFO +2 -1
  4. {docxdriver-0.4.2/crates/docxdriver-python → docxdriver-0.4.3}/README.md +1 -0
  5. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/api.rs +93 -2
  6. docxdriver-0.4.3/crates/docxdriver-core/src/commands/bookmarks.rs +879 -0
  7. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/commands/mod.rs +20 -0
  8. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/commands/typed.rs +21 -6
  9. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/facade.rs +7 -1
  10. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/segments.rs +5 -0
  11. {docxdriver-0.4.2 → docxdriver-0.4.3/crates/docxdriver-python}/README.md +1 -0
  12. {docxdriver-0.4.2 → docxdriver-0.4.3/crates/docxdriver-python}/python/docxdriver/document.py +35 -4
  13. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-python/python/docxdriver/plan.py +57 -0
  14. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-python/src/lib.rs +6 -0
  15. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-python/tests/test_document.py +30 -1
  16. {docxdriver-0.4.2/crates/docxdriver-python → docxdriver-0.4.3}/python/docxdriver/document.py +35 -4
  17. {docxdriver-0.4.2 → docxdriver-0.4.3}/python/docxdriver/plan.py +57 -0
  18. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/Cargo.toml +0 -0
  19. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/LICENSE +0 -0
  20. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/README.md +0 -0
  21. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/assets/blank.docx +0 -0
  22. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/commands/comments.rs +0 -0
  23. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/commands/equations.rs +0 -0
  24. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/commands/headers.rs +0 -0
  25. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/commands/layout.rs +0 -0
  26. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/commands/lists.rs +0 -0
  27. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/commands/reading.rs +0 -0
  28. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/commands/readonly.rs +0 -0
  29. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/commands/revisions.rs +0 -0
  30. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/commands/sections.rs +0 -0
  31. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/commands/table_structure.rs +0 -0
  32. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/commands/tables.rs +0 -0
  33. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/document.rs +0 -0
  34. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/html/assets.rs +0 -0
  35. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/html/build.rs +0 -0
  36. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/html/chrome.rs +0 -0
  37. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/html/compact.rs +0 -0
  38. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/html/ctx.rs +0 -0
  39. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/html/input.rs +0 -0
  40. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/html/mathml_omml.rs +0 -0
  41. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/html/mod.rs +0 -0
  42. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/html/omml_latex.rs +0 -0
  43. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/html/omml_mathml.rs +0 -0
  44. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/html/parse.rs +0 -0
  45. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/html/render.rs +0 -0
  46. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/html/styles.rs +0 -0
  47. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/html/surface.rs +0 -0
  48. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/integrity/mod.rs +0 -0
  49. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/integrity/schema.rs +0 -0
  50. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/integrity/tree.rs +0 -0
  51. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/integrity/wml-model.json +0 -0
  52. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/lib.rs +0 -0
  53. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/media.rs +0 -0
  54. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/outcome.rs +0 -0
  55. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-core/src/package.rs +0 -0
  56. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-python/.gitignore +0 -0
  57. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-python/Cargo.toml +0 -0
  58. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-python/python/docxdriver/__init__.py +0 -0
  59. {docxdriver-0.4.2 → docxdriver-0.4.3}/crates/docxdriver-python/tests/test_plan.py +0 -0
  60. {docxdriver-0.4.2 → docxdriver-0.4.3}/pyproject.toml +0 -0
  61. {docxdriver-0.4.2 → docxdriver-0.4.3}/python/docxdriver/__init__.py +0 -0
@@ -416,7 +416,7 @@ dependencies = [
416
416
 
417
417
  [[package]]
418
418
  name = "docxdriver-cli"
419
- version = "0.4.2"
419
+ version = "0.4.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.4.2"
430
+ version = "0.4.3"
431
431
  dependencies = [
432
432
  "hex",
433
433
  "latex2mathml",
@@ -445,7 +445,7 @@ dependencies = [
445
445
 
446
446
  [[package]]
447
447
  name = "docxdriver-mcp"
448
- version = "0.4.2"
448
+ version = "0.4.3"
449
449
  dependencies = [
450
450
  "axum",
451
451
  "cap-std",
@@ -463,14 +463,14 @@ dependencies = [
463
463
 
464
464
  [[package]]
465
465
  name = "docxdriver-monty-repl"
466
- version = "0.4.2"
466
+ version = "0.4.3"
467
467
  dependencies = [
468
468
  "docxdriver-core",
469
469
  ]
470
470
 
471
471
  [[package]]
472
472
  name = "docxdriver-python"
473
- version = "0.4.2"
473
+ version = "0.4.3"
474
474
  dependencies = [
475
475
  "docxdriver-core",
476
476
  "pyo3",
@@ -480,7 +480,7 @@ dependencies = [
480
480
 
481
481
  [[package]]
482
482
  name = "docxdriver-wasm"
483
- version = "0.4.2"
483
+ version = "0.4.3"
484
484
  dependencies = [
485
485
  "docxdriver-core",
486
486
  "js-sys",
@@ -3,7 +3,7 @@ resolver = "2"
3
3
  members = ["crates/docxdriver-python"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.4.2"
6
+ version = "0.4.3"
7
7
  edition = "2021"
8
8
  license = "MIT"
9
9
  repository = "https://github.com/jhnoel/docxdriver"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: docxdriver
3
- Version: 0.4.2
3
+ Version: 0.4.3
4
4
  Summary: Python SDK for the native Rust DOCX engine
5
5
  License-Expression: MIT
6
6
  Requires-Python: >=3.10
@@ -29,6 +29,7 @@ from docxdriver.plan import Plan, ReplaceText
29
29
  doc = Document(Path("contract.docx").read_bytes())
30
30
  doc.markup() # body with the ids plans address
31
31
  doc.comments(); doc.revisions(); doc.styles(); doc.assets()
32
+ doc.bookmarks() # same as doc.read(kind="bookmarks")
32
33
  doc.find("thirty")
33
34
  review = doc.review(Plan(author="Reviewer", ops=[
34
35
  ReplaceText(at="0F537164", select="thirty", with_="sixty"),
@@ -20,6 +20,7 @@ from docxdriver.plan import Plan, ReplaceText
20
20
  doc = Document(Path("contract.docx").read_bytes())
21
21
  doc.markup() # body with the ids plans address
22
22
  doc.comments(); doc.revisions(); doc.styles(); doc.assets()
23
+ doc.bookmarks() # same as doc.read(kind="bookmarks")
23
24
  doc.find("thirty")
24
25
  review = doc.review(Plan(author="Reviewer", ops=[
25
26
  ReplaceText(at="0F537164", select="thirty", with_="sixty"),
@@ -748,6 +748,35 @@ pub enum EditOp {
748
748
  CommentDelete {
749
749
  comment_id: String,
750
750
  },
751
+ /// Bookmark the paragraph at `at`: its whole content, or the `select`
752
+ /// span. Applied directly in both change modes (Word does not track
753
+ /// bookmarks). Body paragraphs only.
754
+ BookmarkAdd {
755
+ at: ParagraphAddress,
756
+ name: String,
757
+ #[serde(default)]
758
+ select: Option<String>,
759
+ #[serde(default)]
760
+ occurrence: Option<u32>,
761
+ },
762
+ /// Rename a body bookmark; `new_name` follows the add rules.
763
+ BookmarkRename {
764
+ name: String,
765
+ new_name: String,
766
+ },
767
+ /// Re-anchor a body bookmark, keeping its name and `w:id`.
768
+ BookmarkMove {
769
+ name: String,
770
+ at: ParagraphAddress,
771
+ #[serde(default)]
772
+ select: Option<String>,
773
+ #[serde(default)]
774
+ occurrence: Option<u32>,
775
+ },
776
+ /// Remove a body bookmark's start and end markers; the text stays.
777
+ BookmarkDelete {
778
+ name: String,
779
+ },
751
780
  RevisionSettle {
752
781
  target: RevisionTarget,
753
782
  action: RevisionAction,
@@ -1260,7 +1289,11 @@ fn validate_plan(mut p: Plan) -> Result<Plan, Diagnostic> {
1260
1289
  }
1261
1290
 
1262
1291
  fn validation_field(message: &str) -> Option<&'static str> {
1263
- let field = if message.contains("MathML") {
1292
+ let field = if message.starts_with("new_name ") {
1293
+ "new_name"
1294
+ } else if message.starts_with("name ") {
1295
+ "name"
1296
+ } else if message.contains("MathML") {
1264
1297
  "mathml"
1265
1298
  } else if message.contains("equation must") {
1266
1299
  "equation"
@@ -1566,6 +1599,31 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
1566
1599
  return Err("set_page_margins requires a margin".into());
1567
1600
  }
1568
1601
  }
1602
+ EditOp::BookmarkAdd {
1603
+ name,
1604
+ select,
1605
+ occurrence,
1606
+ ..
1607
+ } => {
1608
+ crate::commands::bookmarks::validate_new_name(name, "name")?;
1609
+ validate_bookmark_select(select, occurrence)?;
1610
+ }
1611
+ EditOp::BookmarkMove {
1612
+ name,
1613
+ select,
1614
+ occurrence,
1615
+ ..
1616
+ } => {
1617
+ crate::commands::bookmarks::validate_existing_name(name)?;
1618
+ validate_bookmark_select(select, occurrence)?;
1619
+ }
1620
+ EditOp::BookmarkRename { name, new_name } => {
1621
+ crate::commands::bookmarks::validate_existing_name(name)?;
1622
+ crate::commands::bookmarks::validate_new_name(new_name, "new_name")?;
1623
+ }
1624
+ EditOp::BookmarkDelete { name } => {
1625
+ crate::commands::bookmarks::validate_existing_name(name)?;
1626
+ }
1569
1627
  EditOp::CommentAdd {
1570
1628
  text,
1571
1629
  occurrence,
@@ -1620,6 +1678,18 @@ fn normalize_op(op: &mut EditOp) -> Result<(), String> {
1620
1678
  }
1621
1679
  Ok(())
1622
1680
  }
1681
+ fn validate_bookmark_select(select: &Option<String>, occurrence: &Option<u32>) -> Result<(), String> {
1682
+ if select.as_deref() == Some("") {
1683
+ return Err("select must be non-empty; omit it to bookmark the whole paragraph".into());
1684
+ }
1685
+ if occurrence == &Some(0) {
1686
+ return Err("occurrence must be positive".into());
1687
+ }
1688
+ if occurrence.is_some() && select.is_none() {
1689
+ return Err("occurrence requires select".into());
1690
+ }
1691
+ Ok(())
1692
+ }
1623
1693
  fn diag(code: impl Into<String>, message: impl Into<String>, path: Option<String>) -> Diagnostic {
1624
1694
  Diagnostic {
1625
1695
  code: code.into(),
@@ -1992,6 +2062,16 @@ fn explain_selector_drift(
1992
2062
  at,
1993
2063
  select: Some(select),
1994
2064
  ..
2065
+ }
2066
+ | EditOp::BookmarkAdd {
2067
+ at,
2068
+ select: Some(select),
2069
+ ..
2070
+ }
2071
+ | EditOp::BookmarkMove {
2072
+ at,
2073
+ select: Some(select),
2074
+ ..
1995
2075
  } => (at, vec![select.as_str()]),
1996
2076
  EditOp::ReviseText {
1997
2077
  at: Some(at),
@@ -2098,6 +2178,10 @@ fn operation_name(op: &EditOp) -> &'static str {
2098
2178
  EditOp::CommentReply { .. } => "comment_reply",
2099
2179
  EditOp::CommentSetStatus { .. } => "comment_set_status",
2100
2180
  EditOp::CommentDelete { .. } => "comment_delete",
2181
+ EditOp::BookmarkAdd { .. } => "bookmark_add",
2182
+ EditOp::BookmarkRename { .. } => "bookmark_rename",
2183
+ EditOp::BookmarkMove { .. } => "bookmark_move",
2184
+ EditOp::BookmarkDelete { .. } => "bookmark_delete",
2101
2185
  EditOp::RevisionSettle { .. } => "revision_settle",
2102
2186
  }
2103
2187
  }
@@ -2128,7 +2212,9 @@ fn resolve_typed_aliases(
2128
2212
  | EditOp::InsertTable { at, .. }
2129
2213
  | EditOp::SetSectionBoundary { at, .. }
2130
2214
  | EditOp::FormatParagraph { at, .. }
2131
- | EditOp::CommentAdd { at, .. } => resolve(at)?,
2215
+ | EditOp::CommentAdd { at, .. }
2216
+ | EditOp::BookmarkAdd { at, .. }
2217
+ | EditOp::BookmarkMove { at, .. } => resolve(at)?,
2132
2218
  EditOp::ReviseText { at: Some(at), .. } => resolve(at)?,
2133
2219
  EditOp::InsertParagraph { at, .. } => {
2134
2220
  if let InsertAnchor::Paragraph(addr) = at {
@@ -2478,6 +2564,11 @@ fn typed_read(input: Option<&[u8]>, kind: &str, view: Option<&str>) -> crate::ou
2478
2564
  "revisions",
2479
2565
  source,
2480
2566
  ),
2567
+ "bookmarks" => typed_listing(
2568
+ crate::commands::bookmarks::list_bookmarks(&state.package, &state.xml, source),
2569
+ "bookmarks",
2570
+ source,
2571
+ ),
2481
2572
  other => crate::outcome::Outcome::error(format!("unknown read kind: {other}")),
2482
2573
  }
2483
2574
  }