diff options
| author | Michael Williamson <mike@zwobble.org> | 2025-06-08 00:22:01 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2025-06-08 00:22:01 +0100 |
| commit | 80e9fd089494996def1e2529d1fa127d3ee796dd (patch) | |
| tree | b54aa0b0959e5c83c43a78958669070e95e3fae8 /mammoth | |
| parent | cc1ad1d283264284e10beeff33b140abc16156d3 (diff) | |
Handle SDTs representing checkboxes wrapped in other elements
Diffstat (limited to 'mammoth')
| -rw-r--r-- | mammoth/documents.py | 3 | ||||
| -rw-r--r-- | mammoth/docx/body_xml.py | 43 | ||||
| -rw-r--r-- | mammoth/transforms.py | 8 |
3 files changed, 45 insertions, 9 deletions
diff --git a/mammoth/documents.py b/mammoth/documents.py index f9c0665..e874775 100644 --- a/mammoth/documents.py +++ b/mammoth/documents.py @@ -92,6 +92,9 @@ class TableCellUnmerged: rowspan = cobble.field() vmerge = cobble.field() + def copy(self, **kwargs): + return cobble.copy(self, **kwargs) + @cobble.data class Break(Element): break_type = cobble.field() diff --git a/mammoth/docx/body_xml.py b/mammoth/docx/body_xml.py index 3de2c60..5cef312 100644 --- a/mammoth/docx/body_xml.py +++ b/mammoth/docx/body_xml.py @@ -5,6 +5,7 @@ import sys from .. import documents from .. import results from .. import lists +from .. import transforms from . import complex_fields from .dingbats import dingbats from .xmlparser import node_types, XmlElement, null_xml_element @@ -577,17 +578,49 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f return read_child_elements(element.find_child_or_null("mc:Fallback")) def read_sdt(element): - checkbox = element.find_child_or_null("w:sdtPr").find_child("wordml:checkbox") + content_result = read_child_elements(element.find_child_or_null("w:sdtContent")) + + def handle_content(content): + # From the WordML standard: https://learn.microsoft.com/en-us/openspecs/office_standards/ms-docx/3350cb64-931f-41f7-8824-f18b2568ce66 + # + # > A CT_SdtCheckbox element that specifies that the parent + # > structured document tag is a checkbox when displayed in the + # > document. The parent structured document tag contents MUST + # > contain a single character and optionally an additional + # > character in a deleted run. + checkbox = element.find_child_or_null("w:sdtPr").find_child("wordml:checkbox") + + if checkbox is None: + return content - if checkbox is not None: checked_element = checkbox.find_child("wordml:checked") is_checked = ( checked_element is not None and read_boolean_attribute_value(checked_element.attributes.get("wordml:val")) ) - return _success(documents.checkbox(checked=is_checked)) - else: - return read_child_elements(element.find_child_or_null("w:sdtContent")) + document_checkbox = documents.checkbox(checked=is_checked) + + has_checkbox = False + + def transform_text(text): + nonlocal has_checkbox + if len(text.value) > 0 and not has_checkbox: + has_checkbox = True + return document_checkbox + else: + return text + + replaced_content = list(map( + transforms.element_of_type(documents.Text, transform_text), + content, + )) + + if has_checkbox: + return replaced_content + else: + return document_checkbox + + return content_result.map(handle_content) handlers = { "w:t": text, diff --git a/mammoth/transforms.py b/mammoth/transforms.py index 62bfc1f..cf96cd3 100644 --- a/mammoth/transforms.py +++ b/mammoth/transforms.py @@ -15,18 +15,18 @@ def element_of_type(element_type, transform): return transform(element) else: return element - + return _each_element(transform_element) def _each_element(transform_element): def transform_element_and_children(element): - if isinstance(element, documents.HasChildren): + if isinstance(element, (documents.HasChildren, documents.TableCellUnmerged)): children = list(map(transform_element_and_children, element.children)) element = element.copy(children=children) - + return transform_element(element) - + return transform_element_and_children |
