aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2021-08-01 17:20:33 +0100
committerMichael Williamson <mike@zwobble.org>2021-08-01 17:20:33 +0100
commitb537a3713700b4fc40a23d0ca51b11846d562328 (patch)
tree19a8ddebc9ac8f36c1fc01b806982b46bcded2fc
parent30d24cb9dee83df7a1b108fbc83cdfec31b81b9a (diff)
Handle internal hyperlinks created with complex fields
-rw-r--r--NEWS2
-rw-r--r--mammoth/docx/body_xml.py30
-rw-r--r--mammoth/docx/complex_fields.py8
-rw-r--r--tests/docx/body_xml_tests.py27
4 files changed, 49 insertions, 18 deletions
diff --git a/NEWS b/NEWS
index 5b65b2c..4800200 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,8 @@
* When extracting raw text, convert tab elements to tab characters.
+* Handle internal hyperlinks created with complex fields.
+
# 1.4.16
* Handle w:num with invalid w:abstractNumId.
diff --git a/mammoth/docx/body_xml.py b/mammoth/docx/body_xml.py
index 8329bcf..4c66ae8 100644
--- a/mammoth/docx/body_xml.py
+++ b/mammoth/docx/body_xml.py
@@ -96,11 +96,11 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f
is_small_caps = read_boolean_element(properties.find_child("w:smallCaps"))
def add_complex_field_hyperlink(children):
- hyperlink_href = current_hyperlink_href()
- if hyperlink_href is None:
+ hyperlink_kwargs = current_hyperlink_kwargs()
+ if hyperlink_kwargs is None:
return children
else:
- return [documents.hyperlink(href=hyperlink_href, children=children)]
+ return [documents.hyperlink(children=children, **hyperlink_kwargs)]
return _ReadResult.map_results(
_read_run_style(properties),
@@ -155,10 +155,10 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f
current_instr_text = []
complex_field_stack = []
- def current_hyperlink_href():
+ def current_hyperlink_kwargs():
for complex_field in reversed(complex_field_stack):
if isinstance(complex_field, complex_fields.Hyperlink):
- return complex_field.href
+ return complex_field.kwargs
return None
@@ -171,21 +171,25 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f
complex_field_stack.pop()
elif fld_char_type == "separate":
instr_text = "".join(current_instr_text)
- hyperlink_href = parse_hyperlink_field_code(instr_text)
- if hyperlink_href is None:
+ 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_href)
+ complex_field = complex_fields.hyperlink(hyperlink_kwargs)
complex_field_stack.pop()
complex_field_stack.append(complex_field)
return _empty_result
def parse_hyperlink_field_code(instr_text):
- result = re.match(r'\s*HYPERLINK "(.*)"', instr_text)
- if result is None:
- return None
- else:
- return result.group(1)
+ 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))
+
+ 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 None
def read_instr_text(element):
current_instr_text.append(_inner_text(element))
diff --git a/mammoth/docx/complex_fields.py b/mammoth/docx/complex_fields.py
index d1d95af..75bc05a 100644
--- a/mammoth/docx/complex_fields.py
+++ b/mammoth/docx/complex_fields.py
@@ -3,9 +3,9 @@ class unknown(object):
class Hyperlink(object):
- def __init__(self, href):
- self.href = href
+ def __init__(self, kwargs):
+ self.kwargs = kwargs
-def hyperlink(href):
- return Hyperlink(href)
+def hyperlink(kwargs):
+ return Hyperlink(kwargs=kwargs)
diff --git a/tests/docx/body_xml_tests.py b/tests/docx/body_xml_tests.py
index 664b438..4cd54eb 100644
--- a/tests/docx/body_xml_tests.py
+++ b/tests/docx/body_xml_tests.py
@@ -418,7 +418,7 @@ class ComplexFieldTests(object):
return self._is_hyperlinked_run(children=[])
@istest
- def runs_in_a_complex_field_for_hyperlinks_are_read_as_hyperlinks(self):
+ def runs_in_a_complex_field_for_hyperlinks_without_switch_are_read_as_external_hyperlinks(self):
element = xml_element("w:p", {}, [
self._BEGIN_COMPLEX_FIELD,
self._HYPERLINK_INSTRTEXT,
@@ -441,6 +441,31 @@ class ComplexFieldTests(object):
)))
@istest
+ def runs_in_a_complex_field_for_hyperlinks_with_l_switch_are_read_as_internal_hyperlinks(self):
+ element = xml_element("w:p", {}, [
+ self._BEGIN_COMPLEX_FIELD,
+ xml_element("w:instrText", {}, [
+ xml_text(' HYPERLINK \\l "InternalLink"'),
+ ]),
+ self._SEPARATE_COMPLEX_FIELD,
+ _run_element_with_text("this is a hyperlink"),
+ self._END_COMPLEX_FIELD,
+ ])
+ paragraph = _read_and_get_document_xml_element(element)
+
+ assert_that(paragraph, is_paragraph(children=is_sequence(
+ is_empty_run,
+ self._is_empty_hyperlinked_run,
+ self._is_hyperlinked_run(
+ anchor="InternalLink",
+ children=is_sequence(
+ is_text("this is a hyperlink"),
+ ),
+ ),
+ is_empty_run,
+ )))
+
+ @istest
def runs_after_a_complex_field_for_hyperlinks_are_not_read_as_hyperlinks(self):
element = xml_element("w:p", {}, [
self._BEGIN_COMPLEX_FIELD,