XMLEditor that automatically applies RSID, author, and date to new elements. Automatically adds attributes to elements that support them when inserting new content: - w:rsidR, w:rsidRDefault, w:rsidP (for w:p and w:r elements) - w:author and w:date (for w:ins, w:del, w:comment elements)
| 45 | |
| 46 | |
| 47 | class DocxXMLEditor(XMLEditor): |
| 48 | """XMLEditor that automatically applies RSID, author, and date to new elements. |
| 49 | |
| 50 | Automatically adds attributes to elements that support them when inserting new content: |
| 51 | - w:rsidR, w:rsidRDefault, w:rsidP (for w:p and w:r elements) |
| 52 | - w:author and w:date (for w:ins, w:del, w:comment elements) |
| 53 | - w:id (for w:ins and w:del elements) |
| 54 | |
| 55 | Attributes: |
| 56 | dom (defusedxml.minidom.Document): The DOM document for direct manipulation |
| 57 | """ |
| 58 | |
| 59 | def __init__( |
| 60 | self, xml_path, rsid: str, author: str = "Claude", initials: str = "C" |
| 61 | ): |
| 62 | """Initialize with required RSID and optional author. |
| 63 | |
| 64 | Args: |
| 65 | xml_path: Path to XML file to edit |
| 66 | rsid: RSID to automatically apply to new elements |
| 67 | author: Author name for tracked changes and comments (default: "Claude") |
| 68 | initials: Author initials (default: "C") |
| 69 | """ |
| 70 | super().__init__(xml_path) |
| 71 | self.rsid = rsid |
| 72 | self.author = author |
| 73 | self.initials = initials |
| 74 | |
| 75 | def _get_next_change_id(self): |
| 76 | """Get the next available change ID by checking all tracked change elements.""" |
| 77 | max_id = -1 |
| 78 | for tag in ("w:ins", "w:del"): |
| 79 | elements = self.dom.getElementsByTagName(tag) |
| 80 | for elem in elements: |
| 81 | change_id = elem.getAttribute("w:id") |
| 82 | if change_id: |
| 83 | try: |
| 84 | max_id = max(max_id, int(change_id)) |
| 85 | except ValueError: |
| 86 | pass |
| 87 | return max_id + 1 |
| 88 | |
| 89 | def _ensure_w16du_namespace(self): |
| 90 | """Ensure w16du namespace is declared on the root element.""" |
| 91 | root = self.dom.documentElement |
| 92 | if not root.hasAttribute("xmlns:w16du"): # type: ignore |
| 93 | root.setAttribute( # type: ignore |
| 94 | "xmlns:w16du", |
| 95 | "http://schemas.microsoft.com/office/word/2023/wordml/word16du", |
| 96 | ) |
| 97 | |
| 98 | def _ensure_w16cex_namespace(self): |
| 99 | """Ensure w16cex namespace is declared on the root element.""" |
| 100 | root = self.dom.documentElement |
| 101 | if not root.hasAttribute("xmlns:w16cex"): # type: ignore |
| 102 | root.setAttribute( # type: ignore |
| 103 | "xmlns:w16cex", |
| 104 | "http://schemas.microsoft.com/office/word/2018/wordml/cex", |