From bc8c341b37bfac34bbeaccdd5368c2d7edc309ac Mon Sep 17 00:00:00 2001 From: Bruno Herfst Date: Wed, 26 Oct 2016 11:04:09 +1100 Subject: Add support for page and column breaks --- mammoth/conversion.py | 30 +++++++++++++++++++----- mammoth/document_matchers.py | 10 +++++++- mammoth/documents.py | 11 +++++---- mammoth/docx/body_xml.py | 19 ++++++++++----- mammoth/styles/parser/document_matcher_parser.py | 20 ++++++++++++++++ 5 files changed, 73 insertions(+), 17 deletions(-) (limited to 'mammoth') diff --git a/mammoth/conversion.py b/mammoth/conversion.py index 9398c5c..bae503a 100644 --- a/mammoth/conversion.py +++ b/mammoth/conversion.py @@ -132,6 +132,7 @@ class _DocumentConverter(documents.ElementVisitor): else: return html_paths.empty + def visit_text(self, text): return [html.text(text.value)] @@ -176,11 +177,22 @@ class _DocumentConverter(documents.ElementVisitor): return [ html.element("td", attributes, nodes) ] - - - def visit_line_break(self, line_break): - return [html.element("br")] - + + + def visit_break(self, break_): + return self._find_html_path_for_break(break_).wrap(lambda: []) + + + def _find_html_path_for_break(self, break_): + style = self._find_style(break_, "break") + if style is not None: + return style.html_path + elif break_.break_type == "line": + return html_paths.path([html_paths.element("br", fresh=True)]) + else: + return html_paths.empty + + def visit_note_reference(self, note_reference): self._note_references.append(note_reference) note_number = len(self._note_references) @@ -192,6 +204,7 @@ class _DocumentConverter(documents.ElementVisitor): }, [html.text("[{0}]".format(note_number))]) ]) ] + def visit_note(self, note): note_body = self._visit_all(note.body) + [ @@ -300,7 +313,12 @@ class _DocumentConverter(documents.ElementVisitor): def _document_matcher_matches(matcher, element, element_type): if matcher.element_type in ["underline", "strikethrough", "bold", "italic", "comment_reference"]: return matcher.element_type == element_type - else: + elif matcher.element_type == "break": + return ( + matcher.element_type == element_type and + matcher.break_type == element.break_type + ) + else: # matcher.element_type in ["paragraph", "run"]: return ( matcher.element_type == element_type and ( matcher.style_id is None or diff --git a/mammoth/document_matchers.py b/mammoth/document_matchers.py index d1f8e74..faccb50 100644 --- a/mammoth/document_matchers.py +++ b/mammoth/document_matchers.py @@ -39,6 +39,15 @@ class comment_reference(object): element_type = "comment_reference" +BreakMatcher = collections.namedtuple("BreakMatcher", ["break_type"]) +BreakMatcher.element_type = "break" + + +line_break = BreakMatcher("line") +page_break = BreakMatcher("page") +column_break = BreakMatcher("column") + + def equal_to(value): return StringMatcher(_operator_equal_to, value) @@ -50,7 +59,6 @@ def _operator_equal_to(first, second): def starts_with(value): return StringMatcher(_operator_starts_with, value) - def _operator_starts_with(first, second): return second.upper().startswith(first.upper()) diff --git a/mammoth/documents.py b/mammoth/documents.py index 2767f86..327380d 100644 --- a/mammoth/documents.py +++ b/mammoth/documents.py @@ -53,8 +53,13 @@ class TableCell(HasChildren): rowspan = cobble.field() @cobble.data -class LineBreak(Element): - pass +class Break(Element): + break_type = cobble.field() + +line_break = Break("line") +page_break = Break("page") +column_break = Break("column") + @cobble.data class Tab(Element): @@ -123,8 +128,6 @@ def table_cell(children, colspan=None, rowspan=None): return TableCell(children=children, colspan=colspan, rowspan=rowspan) -line_break = LineBreak - def numbering_level(level_index, is_ordered): return _NumberingLevel(str(level_index), bool(is_ordered)) diff --git a/mammoth/docx/body_xml.py b/mammoth/docx/body_xml.py index 22d8440..1fcd567 100644 --- a/mammoth/docx/body_xml.py +++ b/mammoth/docx/body_xml.py @@ -253,7 +253,8 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f return children_result.map(lambda children: documents.hyperlink(anchor=anchor, children=children)) else: return children_result - + + def bookmark_start(element): name = element.attributes.get("w:name") if name == "_GoBack": @@ -262,13 +263,19 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f return _success(documents.bookmark(name)) - def br(element): + def break_(element): break_type = element.attributes.get("w:type") - if break_type: + + if not break_type or break_type == "textWrapping": + return _success(documents.line_break) + elif break_type == "page": + return _success(documents.page_break) + elif break_type == "column": + return _success(documents.column_break) + else: warning = results.warning("Unsupported break type: {0}".format(break_type)) return _empty_result_with_message(warning) - else: - return _success(documents.line_break()) + def inline(element): properties = element.find_child_or_null("wp:docPr").attributes @@ -372,7 +379,7 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f "w:pict": pict, "w:hyperlink": hyperlink, "w:bookmarkStart": bookmark_start, - "w:br": br, + "w:br": break_, "wp:inline": inline, "wp:anchor": inline, "v:imagedata": read_imagedata, diff --git a/mammoth/styles/parser/document_matcher_parser.py b/mammoth/styles/parser/document_matcher_parser.py index 7f117b7..36999d0 100644 --- a/mammoth/styles/parser/document_matcher_parser.py +++ b/mammoth/styles/parser/document_matcher_parser.py @@ -40,6 +40,9 @@ def parse_document_matcher(tokens): elif tokens.try_skip(TokenType.IDENTIFIER, "comment-reference"): return document_matchers.comment_reference + elif tokens.try_skip(TokenType.IDENTIFIER, "br"): + return _parse_break(tokens) + else: raise LineParseError("Unrecognised document element: {0}".format(tokens.next_value(TokenType.IDENTIFIER))) @@ -78,3 +81,20 @@ def _parse_list_type(tokens): return False else: raise LineParseError("Unrecognised list type: {0}".format(list_type)) + + +def _parse_break(tokens): + tokens.skip(TokenType.SYMBOL, "[") + tokens.skip(TokenType.IDENTIFIER, "type") + tokens.skip(TokenType.SYMBOL, "=") + type_name = parse_string(tokens) + tokens.skip(TokenType.SYMBOL, "]"); + + if type_name == "line": + return document_matchers.line_break + elif type_name == "page": + return document_matchers.page_break + elif type_name == "column": + return document_matchers.column_break + else: + raise LineParseError("Unrecognised break type: {0}".format(type_name)) -- cgit v1.2.3