From c6e6a2b462d0bc5b7a5ac61d26c907e66cbcc946 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Thu, 23 Nov 2017 19:54:53 +0000 Subject: Parse paragraph indents --- mammoth/documents.py | 28 ++++++++++++++++++++-- mammoth/docx/body_xml.py | 10 ++++++++ tests/docx/body_xml_tests.py | 55 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 90 insertions(+), 3 deletions(-) diff --git a/mammoth/documents.py b/mammoth/documents.py index e4372ab..b00f1bb 100644 --- a/mammoth/documents.py +++ b/mammoth/documents.py @@ -21,6 +21,24 @@ class Paragraph(HasChildren): style_name = cobble.field() numbering = cobble.field() alignment = cobble.field() + indent = cobble.field() + + +@cobble.data +class ParagraphIndent(object): + start = cobble.field() + end = cobble.field() + first_line = cobble.field() + hanging = cobble.field() + + +@cobble.data +class Indent(object): + left = cobble.field() + right = cobble.field() + first_line = cobble.field() + hanging = cobble.field() + @cobble.data class Run(HasChildren): @@ -86,8 +104,14 @@ def document(children, notes=None, comments=None): comments = [] return Document(children, notes, comments=comments) -def paragraph(children, style_id=None, style_name=None, numbering=None, alignment=None): - return Paragraph(children, style_id, style_name, numbering, alignment=alignment) +def paragraph(children, style_id=None, style_name=None, numbering=None, alignment=None, indent=None): + if indent is None: + indent = paragraph_indent() + + return Paragraph(children, style_id, style_name, numbering, alignment=alignment, indent=indent) + +def paragraph_indent(start=None, end=None, first_line=None, hanging=None): + return ParagraphIndent(start=start, end=end, first_line=first_line, hanging=hanging) def run( children, diff --git a/mammoth/docx/body_xml.py b/mammoth/docx/body_xml.py index 01920f0..cfe3493 100644 --- a/mammoth/docx/body_xml.py +++ b/mammoth/docx/body_xml.py @@ -120,6 +120,7 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f properties = element.find_child_or_null("w:pPr") alignment = properties.find_child_or_null("w:jc").attributes.get("w:val") numbering = _read_numbering_properties(properties.find_child_or_null("w:numPr")) + indent = _read_paragraph_indent(properties.find_child_or_null("w:ind")) return _ReadResult.map_results( _read_paragraph_style(properties), @@ -130,6 +131,7 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f style_name=style[1], numbering=numbering, alignment=alignment, + indent=indent, )).append_extra() def _read_paragraph_style(properties): @@ -203,6 +205,14 @@ def _create_reader(numbering, content_types, relationships, styles, docx_file, f else: return numbering.find_level(num_id, level_index) + def _read_paragraph_indent(element): + attributes = element.attributes + return documents.paragraph_indent( + start=attributes.get("w:start") or attributes.get("w:left"), + end=attributes.get("w:end") or attributes.get("w:right"), + first_line=attributes.get("w:firstLine"), + hanging=attributes.get("w:hanging"), + ) def tab(element): return _success(documents.tab()) diff --git a/tests/docx/body_xml_tests.py b/tests/docx/body_xml_tests.py index d1dd071..067037e 100644 --- a/tests/docx/body_xml_tests.py +++ b/tests/docx/body_xml_tests.py @@ -97,7 +97,7 @@ class ParagraphTests(object): paragraph_xml = xml_element("w:p", {}, [properties_xml]) paragraph = _read_and_get_document_xml_element(paragraph_xml) assert_equal("center", paragraph.alignment) - + @istest def paragraph_has_no_numbering_if_it_has_no_numbering_properties(self): element = xml_element("w:p") @@ -144,6 +144,59 @@ class ParagraphTests(object): return xml_element("w:p", {}, [properties_xml]) +@istest +class ParagraphIndentTests(object): + @istest + def when_w_start_is_set_then_start_indent_is_read_from_w_start(self): + paragraph_xml = self._paragraph_with_indent({"w:start": "720", "w:left": "40"}) + paragraph = _read_and_get_document_xml_element(paragraph_xml) + assert_equal("720", paragraph.indent.start) + + @istest + def when_w_start_is_not_set_then_start_indent_is_read_from_w_left(self): + paragraph_xml = self._paragraph_with_indent({"w:left": "720"}) + paragraph = _read_and_get_document_xml_element(paragraph_xml) + assert_equal("720", paragraph.indent.start) + + @istest + def when_w_end_is_set_then_end_indent_is_read_from_w_end(self): + paragraph_xml = self._paragraph_with_indent({"w:end": "720", "w:right": "40"}) + paragraph = _read_and_get_document_xml_element(paragraph_xml) + assert_equal("720", paragraph.indent.end) + + @istest + def when_w_end_is_not_set_then_end_indent_is_read_from_w_right(self): + paragraph_xml = self._paragraph_with_indent({"w:right": "720"}) + paragraph = _read_and_get_document_xml_element(paragraph_xml) + assert_equal("720", paragraph.indent.end) + + @istest + def paragraph_has_indent_first_line_read_from_paragraph_properties_if_present(self): + paragraph_xml = self._paragraph_with_indent({"w:firstLine": "720"}) + paragraph = _read_and_get_document_xml_element(paragraph_xml) + assert_equal("720", paragraph.indent.first_line) + + @istest + def paragraph_has_indent_hanging_read_from_paragraph_properties_if_present(self): + paragraph_xml = self._paragraph_with_indent({"w:hanging": "720"}) + paragraph = _read_and_get_document_xml_element(paragraph_xml) + assert_equal("720", paragraph.indent.hanging) + + @istest + def when_indent_attributes_arent_set_then_indents_are_none(self): + paragraph_xml = self._paragraph_with_indent({}) + paragraph = _read_and_get_document_xml_element(paragraph_xml) + assert_equal(None, paragraph.indent.start) + assert_equal(None, paragraph.indent.end) + assert_equal(None, paragraph.indent.first_line) + assert_equal(None, paragraph.indent.hanging) + + def _paragraph_with_indent(self, attributes): + indent_xml = xml_element("w:ind", attributes) + properties_xml = xml_element("w:pPr", {}, [indent_xml]) + return xml_element("w:p", {}, [properties_xml]) + + @istest class RunTests(object): @istest -- cgit v1.2.3