aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2015-06-17 15:05:19 +0100
committerMichael Williamson <mike@zwobble.org>2015-06-17 15:05:19 +0100
commit147fabb2b2e00f7790304b7a1e1f40cdb564cb4f (patch)
treefcd72fdbdf3681acd1dff8dd6237143a80914cfa
parent35d3dd58400d5310fe05ebe8e070c96c756edac0 (diff)
Support underlines in style map
-rw-r--r--mammoth/conversion.py55
-rw-r--r--mammoth/document_matchers.py4
-rw-r--r--mammoth/html_generation.py18
-rw-r--r--mammoth/style_reader/document_matcher_reader.py6
-rw-r--r--tests/conversion_tests.py24
-rw-r--r--tests/html_generation_tests.py14
-rw-r--r--tests/mammoth_tests.py7
-rw-r--r--tests/style_reader_tests.py7
8 files changed, 107 insertions, 28 deletions
diff --git a/mammoth/conversion.py b/mammoth/conversion.py
index 1569849..fc35efe 100644
--- a/mammoth/conversion.py
+++ b/mammoth/conversion.py
@@ -6,7 +6,7 @@ import base64
import random
from . import documents, results, html_paths, images, writers
-from .html_generation import HtmlGenerator, satisfy_html_path
+from .html_generation import HtmlGenerator, satisfy_html_path, append_html_path
def convert_document_element_to_html(element,
@@ -57,7 +57,7 @@ class DocumentConverter(object):
documents.NoteReference: self._convert_note_reference,
documents.Note: self._convert_note,
}
- self._convert_underline = convert_underline
+ self._convert_underline = convert_underline or self._default_convert_underline
def convert_element_to_html(self, element, html_generator):
@@ -97,12 +97,18 @@ class DocumentConverter(object):
run_generator.start("sup")
if run.vertical_alignment == documents.VerticalAlignment.subscript:
run_generator.start("sub")
- if run.is_underline and self._convert_underline is not None:
+ if run.is_underline:
self._convert_underline(run_generator)
self._convert_elements_to_html(run.children, run_generator)
run_generator.end_all()
html_generator.append(run_generator)
-
+
+
+ def _default_convert_underline(self, run_generator):
+ style = self._find_style(None, "underline")
+ if style is not None:
+ append_html_path(run_generator, style.html_path)
+
def _convert_text(self, text, html_generator):
html_generator.text(text.value)
@@ -201,10 +207,9 @@ class DocumentConverter(object):
def _find_html_path(self, element, element_type, default):
- for style in self._style_map:
- document_matcher = style.document_matcher
- if _document_matcher_matches(document_matcher, element, element_type):
- return style.html_path
+ style = self._find_style(element, element_type)
+ if style is not None:
+ return style.html_path
if element.style_id is not None:
self.messages.append(results.warning(
@@ -213,7 +218,12 @@ class DocumentConverter(object):
))
return default
-
+
+ def _find_style(self, element, element_type):
+ for style in self._style_map:
+ document_matcher = style.document_matcher
+ if _document_matcher_matches(document_matcher, element, element_type):
+ return style
def _note_html_id(self, note):
return self._html_id("{0}-{1}".format(note.note_type, note.note_id))
@@ -226,18 +236,21 @@ class DocumentConverter(object):
def _document_matcher_matches(matcher, element, element_type):
- return (
- matcher.element_type == element_type and (
- matcher.style_id is None or
- matcher.style_id == element.style_id
- ) and (
- matcher.style_name is None or
- element.style_name is not None and (matcher.style_name.upper() == element.style_name.upper())
- ) and (
- element_type != "paragraph" or
- matcher.numbering is None or
- matcher.numbering == element.numbering
+ if matcher.element_type == "underline":
+ return element_type == "underline"
+ else:
+ return (
+ matcher.element_type == element_type and (
+ matcher.style_id is None or
+ matcher.style_id == element.style_id
+ ) and (
+ matcher.style_name is None or
+ element.style_name is not None and (matcher.style_name.upper() == element.style_name.upper())
+ ) and (
+ element_type != "paragraph" or
+ matcher.numbering is None or
+ matcher.numbering == element.numbering
+ )
)
- )
_up_arrow = "↑"
diff --git a/mammoth/document_matchers.py b/mammoth/document_matchers.py
index 222ee73..07d5be2 100644
--- a/mammoth/document_matchers.py
+++ b/mammoth/document_matchers.py
@@ -15,3 +15,7 @@ def run(style_id=None, style_name=None):
RunMatcher = collections.namedtuple("RunMatcher", ["style_id", "style_name"])
RunMatcher.element_type = "run"
+
+
+class underline(object):
+ element_type = "underline"
diff --git a/mammoth/html_generation.py b/mammoth/html_generation.py
index c7f4f38..e538f6e 100644
--- a/mammoth/html_generation.py
+++ b/mammoth/html_generation.py
@@ -68,12 +68,22 @@ def satisfy_html_path(generator, path):
generator.end()
for element in path.elements[first_unsatisfied_index:]:
- attributes = {}
- if element.class_names:
- attributes["class"] = _generate_class_attribute(element)
- generator.start(element.names[0], attributes=attributes)
+ _append_element(generator, element)
+
+
+def append_html_path(generator, path):
+ for element in path.elements:
+ _append_element(generator, element)
+
+
+def _append_element(generator, element):
+ attributes = {}
+ if element.class_names:
+ attributes["class"] = _generate_class_attribute(element)
+ generator.start(element.names[0], attributes=attributes)
+
def _find_first_unsatisfied_index(generator, path):
for index, (generated_element, path_element) in enumerate(zip(generator._stack, path.elements)):
if not _is_element_match(generated_element, path_element):
diff --git a/mammoth/style_reader/document_matcher_reader.py b/mammoth/style_reader/document_matcher_reader.py
index 3dc9892..56c4626 100644
--- a/mammoth/style_reader/document_matcher_reader.py
+++ b/mammoth/style_reader/document_matcher_reader.py
@@ -14,6 +14,8 @@ def read_document_matcher_node(root_node):
return _read_paragraph_node(element_node)
elif element_node.expr_name == "run":
return _read_run_node(element_node)
+ elif element_node.expr_name == "underline":
+ return document_matchers.underline
def _read_paragraph_node(paragraph_node):
@@ -53,7 +55,9 @@ def _read_list_node(list_node):
return None
grammar_text = r"""
-document_matcher = paragraph / run
+document_matcher = paragraph / run / underline
+
+underline = "u"
paragraph = "p" style_id? style_name_specifier? list?
diff --git a/tests/conversion_tests.py b/tests/conversion_tests.py
index 4b5b461..19df77f 100644
--- a/tests/conversion_tests.py
+++ b/tests/conversion_tests.py
@@ -172,7 +172,7 @@ def underline_runs_are_ignored_by_default():
@istest
-def underline_runs_can_be_wrapped_in_tags():
+def underline_runs_can_be_wrapped_in_tags_using_convert_underline_argument():
result = convert_document_element_to_html(
documents.run(children=[documents.text("Hello")], is_underline=True),
convert_underline=mammoth.underline.element("u")
@@ -181,6 +181,28 @@ def underline_runs_can_be_wrapped_in_tags():
@istest
+def underline_runs_can_be_mapped_using_style_mapping():
+ result = convert_document_element_to_html(
+ documents.run(children=[documents.text("Hello")], is_underline=True),
+ style_map=[
+ style_reader.read_style("u => em")
+ ]
+ )
+ assert_equal("<em>Hello</em>", result.value)
+
+
+@istest
+def style_mapping_for_underline_runs_does_not_close_parent_elements():
+ result = convert_document_element_to_html(
+ documents.run(children=[documents.text("Hello")], is_underline=True, is_bold=True),
+ style_map=[
+ style_reader.read_style("u => em")
+ ]
+ )
+ assert_equal("<strong><em>Hello</em></strong>", result.value)
+
+
+@istest
def superscript_runs_are_wrapped_in_sup_tags():
result = convert_document_element_to_html(
documents.run(
diff --git a/tests/html_generation_tests.py b/tests/html_generation_tests.py
index ccb0e7e..2375564 100644
--- a/tests/html_generation_tests.py
+++ b/tests/html_generation_tests.py
@@ -1,6 +1,6 @@
from nose.tools import istest, assert_equal
-from mammoth.html_generation import HtmlGenerator, satisfy_html_path
+from mammoth.html_generation import HtmlGenerator, satisfy_html_path, append_html_path
from mammoth import html_paths, writers
@@ -171,5 +171,17 @@ class SatisfyPathTests(object):
assert_equal('<p class="tip">HelpTip', generator.as_string())
+@istest
+class AppendPathTests(object):
+ @istest
+ def already_opened_elements_are_not_closed(self):
+ generator = _create_html_generator()
+ generator.start("strong")
+ path = html_paths.path([html_paths.element(["em"])])
+ append_html_path(generator, path)
+ generator.text("Hello!")
+ assert_equal('<strong><em>Hello!', generator.as_string())
+
+
def _create_html_generator():
return HtmlGenerator(writers.writer)
diff --git a/tests/mammoth_tests.py b/tests/mammoth_tests.py
index a1b396c..e7e7e64 100644
--- a/tests/mammoth_tests.py
+++ b/tests/mammoth_tests.py
@@ -124,6 +124,13 @@ def underline_can_be_configured_with_convert_underline_option():
@istest
+def underline_can_be_configured_with_style_mapping():
+ with open(test_path("underline.docx"), "rb") as fileobj:
+ result = mammoth.convert_to_html(fileobj=fileobj, style_map="u => em")
+ assert_equal('<p><strong>The </strong><strong><em>Sunset</em></strong><strong> Tree</strong></p>', result.value)
+
+
+@istest
def transform_document_is_applied_to_document_before_conversion():
def transform_document(document):
document.children[0].style_id = "Heading1"
diff --git a/tests/style_reader_tests.py b/tests/style_reader_tests.py
index 464cb4a..501629a 100644
--- a/tests/style_reader_tests.py
+++ b/tests/style_reader_tests.py
@@ -134,6 +134,13 @@ class ReadDocumentMatcherTests(object):
read_document_matcher("r[style-name='Emphasis']")
)
+ @istest
+ def reads_underline(self):
+ assert_equal(
+ document_matchers.underline,
+ read_document_matcher("u")
+ )
+
@istest
def document_matcher_is_mapped_to_html_path_using_fat_arrow():