summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2024-11-30 13:56:29 +0000
committerMichael Williamson <mike@zwobble.org>2024-11-30 13:56:29 +0000
commitfe6fb7ea7380280c8589548fd61b121a917a356a (patch)
tree810e648b0132ff4931e91d2cd986689d6600e450
parentfe7708b65b7d479e795f379e11ab486d2d08af4e (diff)
Read complex field checkboxes, always unchecked
-rw-r--r--mammoth/docx/body_xml.py42
-rw-r--r--mammoth/docx/complex_fields.py18
-rw-r--r--mammoth/docx/xmlparser.py4
-rw-r--r--tests/docx/body_xml_tests.py47
-rw-r--r--tests/docx/document_matchers.py3
5 files changed, 98 insertions, 16 deletions
diff --git a/mammoth/docx/body_xml.py b/mammoth/docx/body_xml.py
index e1e70a5..45dc59f 100644
--- a/mammoth/docx/body_xml.py
+++ b/mammoth/docx/body_xml.py
@@ -7,7 +7,7 @@ from .. import results
from .. import lists
from . import complex_fields
from .dingbats import dingbats
-from .xmlparser import node_types, XmlElement
+from .xmlparser import node_types, XmlElement, null_xml_element
from .styles_xml import Styles
from .uris import replace_fragment, uri_to_zip_entry_name
@@ -192,29 +192,45 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f
def read_fld_char(element):
fld_char_type = element.attributes.get("w:fldCharType")
if fld_char_type == "begin":
- complex_field_stack.append(complex_fields.unknown)
+ complex_field_stack.append(complex_fields.begin(fld_char=element))
del current_instr_text[:]
+
elif fld_char_type == "end":
- complex_field_stack.pop()
+ complex_field = complex_field_stack.pop()
+ if isinstance(complex_field, complex_fields.Begin):
+ complex_field = parse_current_instr_text(complex_field)
+
+ if isinstance(complex_field, complex_fields.Checkbox):
+ return _success(documents.checkbox(checked=complex_field.checked))
+
elif fld_char_type == "separate":
- instr_text = "".join(current_instr_text)
- hyperlink_kwargs = parse_hyperlink_field_code(instr_text)
- if hyperlink_kwargs is None:
- complex_field = complex_fields.unknown
- else:
- complex_field = complex_fields.hyperlink(hyperlink_kwargs)
- complex_field_stack.pop()
+ complex_field_separate = complex_field_stack.pop()
+ complex_field = parse_current_instr_text(complex_field_separate)
complex_field_stack.append(complex_field)
return _empty_result
- def parse_hyperlink_field_code(instr_text):
+ def parse_current_instr_text(complex_field):
+ instr_text = "".join(current_instr_text)
+
+ if isinstance(complex_field, complex_fields.Begin):
+ fld_char = complex_field.fld_char
+ else:
+ fld_char = null_xml_element
+
+ return parse_instr_text(instr_text, fld_char=fld_char)
+
+ def parse_instr_text(instr_text, *, fld_char):
external_link_result = re.match(r'\s*HYPERLINK "(.*)"', instr_text)
if external_link_result is not None:
- return dict(href=external_link_result.group(1))
+ return complex_fields.hyperlink(dict(href=external_link_result.group(1)))
internal_link_result = re.match(r'\s*HYPERLINK\s+\\l\s+"(.*)"', instr_text)
if internal_link_result is not None:
- return dict(anchor=internal_link_result.group(1))
+ return complex_fields.hyperlink(dict(anchor=internal_link_result.group(1)))
+
+ checkbox_result = re.match(r'\s*FORMCHECKBOX\s*', instr_text)
+ if checkbox_result is not None:
+ return complex_fields.checkbox(checked=False)
return None
diff --git a/mammoth/docx/complex_fields.py b/mammoth/docx/complex_fields.py
index 75bc05a..4112cba 100644
--- a/mammoth/docx/complex_fields.py
+++ b/mammoth/docx/complex_fields.py
@@ -2,6 +2,15 @@ class unknown(object):
pass
+class Begin:
+ def __init__(self, *, fld_char):
+ self.fld_char = fld_char
+
+
+def begin(*, fld_char):
+ return Begin(fld_char=fld_char)
+
+
class Hyperlink(object):
def __init__(self, kwargs):
self.kwargs = kwargs
@@ -9,3 +18,12 @@ class Hyperlink(object):
def hyperlink(kwargs):
return Hyperlink(kwargs=kwargs)
+
+
+class Checkbox:
+ def __init__(self, *, checked):
+ self.checked = checked
+
+
+def checkbox(*, checked):
+ return Checkbox(checked=checked)
diff --git a/mammoth/docx/xmlparser.py b/mammoth/docx/xmlparser.py
index bb84afd..2e22cec 100644
--- a/mammoth/docx/xmlparser.py
+++ b/mammoth/docx/xmlparser.py
@@ -10,7 +10,7 @@ class XmlElement(object):
children = cobble.field()
def find_child_or_null(self, name):
- return self.find_child(name) or _null_xml_element
+ return self.find_child(name) or null_xml_element
def find_child(self, name):
for child in self.children:
@@ -50,7 +50,7 @@ class NullXmlElement(object):
return None
-_null_xml_element = NullXmlElement()
+null_xml_element = NullXmlElement()
@cobble.data
diff --git a/tests/docx/body_xml_tests.py b/tests/docx/body_xml_tests.py
index 117d966..cb5a9b5 100644
--- a/tests/docx/body_xml_tests.py
+++ b/tests/docx/body_xml_tests.py
@@ -18,6 +18,7 @@ from .document_matchers import (
is_empty_run,
is_run,
is_hyperlink,
+ is_checkbox,
is_text,
is_table,
is_row,
@@ -630,6 +631,52 @@ class ComplexFieldTests(object):
)))
+class CheckboxTests:
+ def test_complex_field_checkbox_without_separate_is_read(self):
+ element = xml_element("w:p", {}, [
+ xml_element("w:r", {}, [
+ xml_element("w:fldChar", {"w:fldCharType": "begin"})
+ ]),
+ xml_element("w:instrText", {}, [
+ xml_text(' FORMCHECKBOX ')
+ ]),
+ xml_element("w:r", {}, [
+ xml_element("w:fldChar", {"w:fldCharType": "end"})
+ ])
+ ])
+
+ paragraph = _read_and_get_document_xml_element(element);
+
+ assert_that(paragraph, is_paragraph(children=is_sequence(
+ is_empty_run,
+ is_run(children=is_sequence(is_checkbox())),
+ )))
+
+ def test_complex_field_checkbox_with_separate_is_read(self):
+ element = xml_element("w:p", {}, [
+ xml_element("w:r", {}, [
+ xml_element("w:fldChar", {"w:fldCharType": "begin"})
+ ]),
+ xml_element("w:instrText", {}, [
+ xml_text(' FORMCHECKBOX ')
+ ]),
+ xml_element("w:r", {}, [
+ xml_element("w:fldChar", {"w:fldCharType": "separate"})
+ ]),
+ xml_element("w:r", {}, [
+ xml_element("w:fldChar", {"w:fldCharType": "end"})
+ ])
+ ])
+
+ paragraph = _read_and_get_document_xml_element(element);
+
+ assert_that(paragraph, is_paragraph(children=is_sequence(
+ is_empty_run,
+ is_empty_run,
+ is_run(children=is_sequence(is_checkbox())),
+ )))
+
+
def test_can_read_tab_element():
element = xml_element("w:tab")
tab = _read_and_get_document_xml_element(element)
diff --git a/tests/docx/document_matchers.py b/tests/docx/document_matchers.py
index d2e5d5c..6b02cf9 100644
--- a/tests/docx/document_matchers.py
+++ b/tests/docx/document_matchers.py
@@ -9,13 +9,14 @@ def create_element_matcher(element_type):
instance_of(element_type),
has_attrs(**kwargs),
)
-
+
return matcher
is_paragraph = create_element_matcher(documents.Paragraph)
is_run = create_element_matcher(documents.Run)
is_hyperlink = create_element_matcher(documents.Hyperlink)
+is_checkbox = create_element_matcher(documents.Checkbox)
is_table = create_element_matcher(documents.Table)
is_row = create_element_matcher(documents.TableRow)