From 81fa73cc773b2d139a4abf3411830d028ae339c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Zoric=CC=8Ca=CC=81k?= <1ondrej.zoricak@gmail.com> Date: Sun, 18 Nov 2018 14:57:20 +0100 Subject: [PATCH 01/13] test travis notification --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index fd69637be..650a8890c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,3 +8,9 @@ python: install: pip install -r requirements.txt # command to run tests, e.g. python setup.py test script: py.test +notifications: + email: + recipients: + - ondrej.zoricak.2@student.tuke.sk + on_success: always + on_failure: always \ No newline at end of file From 3cfb2d6f14492bfe55e61b2c5612f41df68674eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Zoric=CC=8Ca=CC=81k?= <1ondrej.zoricak@gmail.com> Date: Sun, 18 Nov 2018 15:19:46 +0100 Subject: [PATCH 02/13] remove travis notifications --- .travis.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 650a8890c..bdc4e9a39 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,10 +7,4 @@ python: # command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors install: pip install -r requirements.txt # command to run tests, e.g. python setup.py test -script: py.test -notifications: - email: - recipients: - - ondrej.zoricak.2@student.tuke.sk - on_success: always - on_failure: always \ No newline at end of file +script: py.test \ No newline at end of file From 0bc90ec7c58989707b52523a873f7786d5977ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Zoric=CC=8Ca=CC=81k?= <1ondrej.zoricak@gmail.com> Date: Sun, 18 Nov 2018 17:07:10 +0100 Subject: [PATCH 03/13] add acceptance tests for header and footer --- features/sct-access-hdr-ftr.feature | 13 +++++++++ features/steps/section.py | 26 ++++++++++++++++++ features/steps/settings.py | 16 ++++++++++- .../test_files/a_footer_of_all_types.docx | Bin 0 -> 17273 bytes .../test_files/a_header_of_all_types.docx | Bin 0 -> 17264 bytes .../have_a_even_and_odd_header_settings.docx | Bin 0 -> 17144 bytes .../have_no_even_and_odd_header_settings.docx | Bin 0 -> 17139 bytes features/sts-settings-props.feature | 14 ++++++++++ 8 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 features/sct-access-hdr-ftr.feature create mode 100644 features/steps/test_files/a_footer_of_all_types.docx create mode 100644 features/steps/test_files/a_header_of_all_types.docx create mode 100644 features/steps/test_files/have_a_even_and_odd_header_settings.docx create mode 100644 features/steps/test_files/have_no_even_and_odd_header_settings.docx create mode 100644 features/sts-settings-props.feature diff --git a/features/sct-access-hdr-ftr.feature b/features/sct-access-hdr-ftr.feature new file mode 100644 index 000000000..814428caa --- /dev/null +++ b/features/sct-access-hdr-ftr.feature @@ -0,0 +1,13 @@ +# Created by Ondrej at 18/11/2018 +Feature: Access all types of headers and footers (default, first, even) + In order to discover and modify section header access + As a developer using python-docx + I need a way to get headers of sections + + Scenario: Access header of all types + Given a section with all header types + Then header, first_page_header, even_odd_header is present in document.section + + Scenario: Access footer of all types + Given a section with all footer types + Then footer, first_page_footer, even_odd_footer is present in document.section \ No newline at end of file diff --git a/features/steps/section.py b/features/steps/section.py index 496e0f17f..d0a16308a 100644 --- a/features/steps/section.py +++ b/features/steps/section.py @@ -59,6 +59,17 @@ def given_a_section_having_known_orientation(context, orientation): context.section = document.sections[section_idx] +@given('a section with all header types') +def given_a_header_of_type(context): + document = Document(test_docx('a_header_of_all_types')) + context.section = document.sections[0] + + +@given('a section with all footer types') +def given_a_header_of_type(context): + document = Document(test_docx('a_footer_of_all_types')) + context.section = document.sections[0] + # when ===================================================== @when('I set the {margin_side} margin to {inches} inches') @@ -182,3 +193,18 @@ def then_the_reported_section_start_type_is_type(context, start_type): 'ODD_PAGE': WD_SECTION.ODD_PAGE, }[start_type] assert context.section.start_type == expected_start_type + + +@then('header, first_page_header, even_odd_header is present in document.section') +def then_header_of_type_is_present_in_document_section(context): + assert context.section.header.is_linked_to_previous is False + assert context.section.first_page_header.is_linked_to_previous is False + assert context.section.even_odd_header.is_linked_to_previous is False + + +@then('footer, first_page_footer, even_odd_footer is present in document.section') +def then_header_of_type_is_present_in_document_section(context): + assert context.section.footer.is_linked_to_previous is False + assert context.section.first_page_footer.is_linked_to_previous is False + assert context.section.even_odd_footer.is_linked_to_previous is False + diff --git a/features/steps/settings.py b/features/steps/settings.py index b818487b5..e75d0603c 100644 --- a/features/steps/settings.py +++ b/features/steps/settings.py @@ -13,7 +13,7 @@ from docx import Document from docx.settings import Settings -from helpers import test_docx +from helpers import test_docx, tri_state_vals # given ==================================================== @@ -28,9 +28,23 @@ def given_a_document_having_no_settings_part(context): context.document = Document(test_docx('set-no-settings-part')) +@given('a settings having a even and odd headers settings') +def given_a_even_and_odd_headers_settings(context): + context.document = Document(test_docx('have_a_even_and_odd_header_settings')) + +@given('a settings having no even and odd headers settings') +def given_no_even_and_odd_headers_settings(context): + context.document = Document(test_docx('have_no_even_and_odd_header_settings')) + + # then ===================================================== @then('document.settings is a Settings object') def then_document_settings_is_a_Settings_object(context): document = context.document assert type(document.settings) is Settings + +@then('document.settings.even_and_odd_headers is {value}') +def then_document_settings_even_and_odd_header_is_value(context, value): + document, expected_value = context.document, tri_state_vals[value] + assert document.settings.odd_and_even_pages_header_footer is expected_value \ No newline at end of file diff --git a/features/steps/test_files/a_footer_of_all_types.docx b/features/steps/test_files/a_footer_of_all_types.docx new file mode 100644 index 0000000000000000000000000000000000000000..d33f8121068aea7d226d66d0322ad910b826ed9a GIT binary patch literal 17273 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 literal 0 HcmV?d00001 diff --git a/features/steps/test_files/a_header_of_all_types.docx b/features/steps/test_files/a_header_of_all_types.docx new file mode 100644 index 0000000000000000000000000000000000000000..e92d8c447f722a44e0c26635e32aed9fe1c14eae GIT binary patch literal 17264 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 even and odd headers settings + Then document.settings.even_and_odd_headers is + + Examples: Even and odd headers settings values + | a-or-no | value | + | a | True | + | no | False | \ No newline at end of file From 2a72112a1c7ae6d88cb3110198a9a098d95e3616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Zoric=CC=8Ca=CC=81k?= <1ondrej.zoricak@gmail.com> Date: Sat, 24 Nov 2018 11:26:49 +0100 Subject: [PATCH 04/13] get setting different first page header footer --- features/sct-section-props.feature | 74 +++++++++++++++++------------- features/steps/section.py | 14 ++++++ 2 files changed, 56 insertions(+), 32 deletions(-) diff --git a/features/sct-section-props.feature b/features/sct-section-props.feature index 412d93f02..5c5356d45 100644 --- a/features/sct-section-props.feature +++ b/features/sct-section-props.feature @@ -6,7 +6,7 @@ Feature: Access and change section properties Scenario Outline: Get section start type Given a section having start type - Then the reported section start type is + Then the reported section start type is Examples: Section start types | start-type | @@ -19,8 +19,8 @@ Feature: Access and change section properties Scenario Outline: Set section start type Given a section having start type - When I set the section start type to - Then the reported section start type is + When I set the section start type to + Then the reported section start type is Examples: Section start types | initial-start-type | new-start-type | reported-start-type | @@ -31,21 +31,21 @@ Feature: Access and change section properties Scenario: Get section page size Given a section having known page dimension - Then the reported page width is 8.5 inches - And the reported page height is 11 inches + Then the reported page width is 8.5 inches + And the reported page height is 11 inches Scenario: Set section page size Given a section having known page dimension - When I set the section page width to 11 inches - And I set the section page height to 8.5 inches - Then the reported page width is 11 inches - And the reported page height is 8.5 inches + When I set the section page width to 11 inches + And I set the section page height to 8.5 inches + Then the reported page width is 11 inches + And the reported page height is 8.5 inches Scenario Outline: Get section orientation Given a section known to have orientation - Then the reported page orientation is + Then the reported page orientation is Examples: Section page orientations | orientation | reported-orientation | @@ -55,38 +55,48 @@ Feature: Access and change section properties Scenario Outline: Set section orientation Given a section known to have orientation - When I set the section orientation to - Then the reported page orientation is + When I set the section orientation to + Then the reported page orientation is Examples: Section page orientations - | initial-orientation | new-orientation | reported-orientation | - | portrait | WD_ORIENT.LANDSCAPE | WD_ORIENT.LANDSCAPE | - | landscape | WD_ORIENT.PORTRAIT | WD_ORIENT.PORTRAIT | - | landscape | None | WD_ORIENT.PORTRAIT | + | initial-orientation | new-orientation | reported-orientation | + | portrait | WD_ORIENT.LANDSCAPE | WD_ORIENT.LANDSCAPE | + | landscape | WD_ORIENT.PORTRAIT | WD_ORIENT.PORTRAIT | + | landscape | None | WD_ORIENT.PORTRAIT | Scenario: Get section page margins Given a section having known page margins - Then the reported left margin is 1.0 inches - And the reported right margin is 1.25 inches - And the reported top margin is 1.5 inches - And the reported bottom margin is 1.75 inches - And the reported gutter margin is 0.25 inches - And the reported header margin is 0.5 inches - And the reported footer margin is 0.75 inches + Then the reported left margin is 1.0 inches + And the reported right margin is 1.25 inches + And the reported top margin is 1.5 inches + And the reported bottom margin is 1.75 inches + And the reported gutter margin is 0.25 inches + And the reported header margin is 0.5 inches + And the reported footer margin is 0.75 inches Scenario Outline: Set section page margins Given a section having known page margins - When I set the margin to inches - Then the reported margin is inches + When I set the margin to inches + Then the reported margin is inches Examples: Section margin settings | margin-type | length | - | left | 1.0 | - | right | 1.25 | - | top | 0.75 | - | bottom | 1.5 | - | header | 0.25 | - | footer | 0.5 | - | gutter | 0.25 | + | left | 1.0 | + | right | 1.25 | + | top | 0.75 | + | bottom | 1.5 | + | header | 0.25 | + | footer | 0.5 | + | gutter | 0.25 | + + + Scenario Outline: Get settings different first page header footer + Given a section having different first page header footer setting + Then the section.different_first_page_header_footer is + + Examples: Section margin settings + | a-or-no | value | + | a | True | + | no | False | \ No newline at end of file diff --git a/features/steps/section.py b/features/steps/section.py index d0a16308a..650e72e83 100644 --- a/features/steps/section.py +++ b/features/steps/section.py @@ -58,6 +58,17 @@ def given_a_section_having_known_orientation(context, orientation): document = Document(test_docx('sct-section-props')) context.section = document.sections[section_idx] +@given('a section having a different first page header footer setting') +def given_a_section_having_a_different_first_page_header_footer_setting(context): + document = Document(test_docx('section_having_a_different_first_page_header_footer')) + context.section = document.sections[0] + + +@given('a section having no different first page header footer setting') +def given_a_section_having_no_different_first_page_header_footer_setting(context): + document = Document(test_docx('section_having_no_different_first_page_header_footer')) + context.section = document.sections[0] + @given('a section with all header types') def given_a_header_of_type(context): @@ -194,6 +205,9 @@ def then_the_reported_section_start_type_is_type(context, start_type): }[start_type] assert context.section.start_type == expected_start_type +@then('the section.different_first_page_header_footer is {value}') +def then_section_different_first_page_header_footer_is_value(context, value): + assert context.section.different_first_page_header_footer is value @then('header, first_page_header, even_odd_header is present in document.section') def then_header_of_type_is_present_in_document_section(context): From 690f54aa312859232b4a7143951fa1d465ca13fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Zoric=CC=8Ca=CC=81k?= <1ondrej.zoricak@gmail.com> Date: Sat, 24 Nov 2018 16:33:13 +0100 Subject: [PATCH 05/13] get access to header and footer --- docx/__init__.py | 5 +- docx/document.py | 4 +- docx/enum/header_footer.py | 42 ++++++++++ docx/header_footer.py | 70 ++++++++++++++++ docx/oxml/__init__.py | 6 +- docx/oxml/base_complex_types.py | 15 ++++ docx/oxml/section.py | 53 +++++++++++- docx/parts/document.py | 8 ++ docx/parts/header_footer.py | 22 +++++ docx/section.py | 46 ++++++++-- tests/test_document.py | 4 +- tests/test_section.py | 144 ++++++++++++++++++++++++++------ 12 files changed, 381 insertions(+), 38 deletions(-) create mode 100644 docx/enum/header_footer.py create mode 100644 docx/header_footer.py create mode 100644 docx/oxml/base_complex_types.py create mode 100644 docx/parts/header_footer.py diff --git a/docx/__init__.py b/docx/__init__.py index 7083abe56..dd978714e 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -16,6 +16,7 @@ from docx.parts.numbering import NumberingPart from docx.parts.settings import SettingsPart from docx.parts.styles import StylesPart +from docx.parts.header_footer import HeaderFooterPart def part_class_selector(content_type, reltype): @@ -30,8 +31,10 @@ def part_class_selector(content_type, reltype): PartFactory.part_type_for[CT.WML_NUMBERING] = NumberingPart PartFactory.part_type_for[CT.WML_SETTINGS] = SettingsPart PartFactory.part_type_for[CT.WML_STYLES] = StylesPart +PartFactory.part_type_for[CT.WML_HEADER] = HeaderFooterPart +PartFactory.part_type_for[CT.WML_FOOTER] = HeaderFooterPart del ( CT, CorePropertiesPart, DocumentPart, NumberingPart, PartFactory, - StylesPart, part_class_selector + StylesPart, part_class_selector, HeaderFooterPart ) diff --git a/docx/document.py b/docx/document.py index ba94a7990..c0275a900 100644 --- a/docx/document.py +++ b/docx/document.py @@ -87,7 +87,7 @@ def add_section(self, start_type=WD_SECTION.NEW_PAGE): """ new_sectPr = self._element.body.add_section_break() new_sectPr.start_type = start_type - return Section(new_sectPr) + return Section(new_sectPr, self._part) def add_table(self, rows, cols, style=None): """ @@ -147,7 +147,7 @@ def sections(self): A |Sections| object providing access to each section in this document. """ - return Sections(self._element) + return Sections(self._element, self._part) @property def settings(self): diff --git a/docx/enum/header_footer.py b/docx/enum/header_footer.py new file mode 100644 index 000000000..7ba61fce4 --- /dev/null +++ b/docx/enum/header_footer.py @@ -0,0 +1,42 @@ +# encoding: utf-8 + +""" +Enumerations related to the main document in WordprocessingML files +""" + +from __future__ import absolute_import, print_function, unicode_literals + +from .base import alias, XmlEnumeration, XmlMappedEnumMember + + +@alias('WD_HEADER_FOOTER') +class WD_HEADER_FOOTER(XmlEnumeration): + """ + alias: **WD_HEADER_FOOTER_INDEX** + + Specified header or footer in a document or section. + + Example:: + + from docx.enum.header_footer import WD_HEADER_FOOTER + + header = document.sections[-1].header + first_page_header = document.sections[-1].first_page_header + even_odd_header = document.sections[-1].even_odd_header + """ + + __ms_name__ = 'WdHeaderFooterIndex' + + __url__ = 'https://docs.microsoft.com/en-us/office/vba/api/word.wdheaderfooterindex' + + __members__ = ( + XmlMappedEnumMember( + 'PRIMARY', 1, 'default', 'Header or footer on all pages other than the first page of a document or section.' + ), + XmlMappedEnumMember( + 'FIRST_PAGE', 2, 'first', 'First header or footer in a document or section.' + ), + XmlMappedEnumMember( + 'EVEN_PAGE', 3, 'even', 'Headers or footers on even-numbered pages.' + ), + ) diff --git a/docx/header_footer.py b/docx/header_footer.py new file mode 100644 index 000000000..e7f09e9f3 --- /dev/null +++ b/docx/header_footer.py @@ -0,0 +1,70 @@ +# encoding: utf-8 + +""" +|Header| and |Footer| objects +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from .blkcntnr import BlockItemContainer +from .shared import Parented, lazyproperty + + +class _HeaderFooter(Parented): + + __slots__ = '_type' + + def __init__(self, parent, type): + super(_HeaderFooter, self).__init__(parent) + self._type = type + + @lazyproperty + def body(self): + """ + + :return: body of this part + """ + rel = self._parent.sectPr.get_header_reference_of_type(self._type) + if rel is None: + return None + part = self.part.get_related_part(rel.rId) + return _HeaderFooterBody(part.element, self) + + @property + def is_linked_to_previous(self): + """ + + :return: True when is linked otherwise false + """ + return True if self._parent.sectPr.get_header_reference_of_type(self._type) is None else False + + +class Header(_HeaderFooter): + + def __init__(self, parent, type): + super(Header, self).__init__(parent, type) + + +class Footer(_HeaderFooter): + + def __init__(self, parent, type): + super(Footer, self).__init__(parent, type) + + +class _HeaderFooterBody(BlockItemContainer): + """ + Proxy for ````, ```` element in this header and footer, having primarily a + container role. + """ + def __init__(self, elm, parent): + super(_HeaderFooterBody, self).__init__(elm, parent) + + def clear_content(self): + """ + Return this |_HeaderFooterBody| instance after clearing it of all content. + Section properties for the main document story, if present, are + preserved. + """ + return self._element.clear_content() diff --git a/docx/oxml/__init__.py b/docx/oxml/__init__.py index 2731302e2..2ed8ae251 100644 --- a/docx/oxml/__init__.py +++ b/docx/oxml/__init__.py @@ -86,11 +86,15 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): register_element_cls('w:numbering', CT_Numbering) register_element_cls('w:startOverride', CT_DecimalNumber) -from .section import CT_PageMar, CT_PageSz, CT_SectPr, CT_SectType +from .section import CT_PageMar, CT_PageSz, CT_SectPr, CT_SectType, CT_HdrFtrRef, CT_HdrFtr register_element_cls('w:pgMar', CT_PageMar) register_element_cls('w:pgSz', CT_PageSz) register_element_cls('w:sectPr', CT_SectPr) register_element_cls('w:type', CT_SectType) +register_element_cls('w:headerReference', CT_HdrFtrRef) +register_element_cls('w:footerReference', CT_HdrFtrRef) +register_element_cls('w:hdr', CT_HdrFtr) +register_element_cls('w:ftr', CT_HdrFtr) from .shape import ( CT_Blip, CT_BlipFillProperties, CT_GraphicalObject, diff --git a/docx/oxml/base_complex_types.py b/docx/oxml/base_complex_types.py new file mode 100644 index 000000000..45b71150f --- /dev/null +++ b/docx/oxml/base_complex_types.py @@ -0,0 +1,15 @@ +# encoding: utf-8 + +""" +Simple complex types classes represent base elements +""" + +from .xmlchemy import BaseOxmlElement, RequiredAttribute +from .simpletypes import ST_RelationshipId + + +class CT_Rel(BaseOxmlElement): + """ + ```` element, defining the rId . + """ + rId = RequiredAttribute('r:id', ST_RelationshipId) diff --git a/docx/oxml/section.py b/docx/oxml/section.py index cf76b67ed..42336d8b4 100644 --- a/docx/oxml/section.py +++ b/docx/oxml/section.py @@ -9,8 +9,35 @@ from copy import deepcopy from ..enum.section import WD_ORIENTATION, WD_SECTION_START +from ..enum.header_footer import WD_HEADER_FOOTER from .simpletypes import ST_SignedTwipsMeasure, ST_TwipsMeasure -from .xmlchemy import BaseOxmlElement, OptionalAttribute, ZeroOrOne +from .xmlchemy import BaseOxmlElement, OptionalAttribute, ZeroOrOne, ZeroOrMore +from .base_complex_types import CT_Rel + + +class CT_HdrFtr(BaseOxmlElement): + """ + ```` and ````, the container element for the header and footer part. + """ + + p = ZeroOrMore('w:p', successors=()) + tbl = ZeroOrMore('w:tbl', successors=('w:sectPr',)) + + def clear_content(self): + """ + Remove all content child elements from this element. + """ + + content_elms = self[:] + for content_elm in content_elms: + self.remove(content_elm) + + +class CT_HdrFtrRef(CT_Rel): + """ + ```` and ````, elements, defining section header and footer reference. + """ + type = OptionalAttribute('w:type', WD_HEADER_FOOTER) class CT_PageMar(BaseOxmlElement): @@ -57,6 +84,8 @@ class CT_SectPr(BaseOxmlElement): pgMar = ZeroOrOne('w:pgMar', successors=( __child_sequence__[__child_sequence__.index('w:pgMar')+1:] )) + headerReference = ZeroOrMore('w:headerReference') + footerReference = ZeroOrMore('w:footerReference') @property def bottom_margin(self): @@ -256,6 +285,28 @@ def top_margin(self, value): pgMar = self.get_or_add_pgMar() pgMar.top = value + def get_footer_reference_of_type(self, type): + """ + + :param type: of footer refence + :return: "w:footerReference" object if exist, None otherwise + """ + for ref in self.footerReference_lst: + if ref.type == type: + return ref + return None + + def get_header_reference_of_type(self, type): + """ + + :param type: of header refence + :return: "w:headerReference" object if exist, None otherwise + """ + for ref in self.headerReference_lst: + if ref.type == type: + return ref + return None + class CT_SectType(BaseOxmlElement): """ diff --git a/docx/parts/document.py b/docx/parts/document.py index 01266b3bd..9dbaff70e 100644 --- a/docx/parts/document.py +++ b/docx/parts/document.py @@ -57,6 +57,14 @@ def get_or_add_image(self, image_descriptor): rId = self.relate_to(image_part, RT.IMAGE) return rId, image_part.image + def get_related_part(self, rId): + """ + + :param rId: of relationship + :return: relationship of rId + """ + return self.rels.related_parts[rId] + def get_style(self, style_id, style_type): """ Return the style in this document matching *style_id*. Returns the diff --git a/docx/parts/header_footer.py b/docx/parts/header_footer.py new file mode 100644 index 000000000..b0c8148bb --- /dev/null +++ b/docx/parts/header_footer.py @@ -0,0 +1,22 @@ +# encoding: utf-8 + +""" +|HeaderFooterPart| and closely related objects +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from ..opc.part import XmlPart + + +class HeaderFooterPart(XmlPart): + """ + Main header and footer part of a WordprocessingML (WML) package, aka a .docx file. + Acts as broker to other parts such as image, core properties, and style + parts. It also acts as a convenient delegate when a mid-document object + needs a service involving a remote ancestor. The `Parented.part` property + inherited by many content objects provides access to this part object for + that purpose. + """ \ No newline at end of file diff --git a/docx/section.py b/docx/section.py index 16221243b..d795eb7fa 100644 --- a/docx/section.py +++ b/docx/section.py @@ -7,6 +7,8 @@ from __future__ import absolute_import, print_function, unicode_literals from collections import Sequence +from .header_footer import Header, Footer +from .enum.header_footer import WD_HEADER_FOOTER class Sections(Sequence): @@ -14,20 +16,21 @@ class Sections(Sequence): Sequence of |Section| objects corresponding to the sections in the document. Supports ``len()``, iteration, and indexed access. """ - def __init__(self, document_elm): + def __init__(self, document_elm, part): super(Sections, self).__init__() self._document_elm = document_elm + self._part = part def __getitem__(self, key): if isinstance(key, slice): sectPr_lst = self._document_elm.sectPr_lst[key] - return [Section(sectPr) for sectPr in sectPr_lst] + return [Section(sectPr, self._part) for sectPr in sectPr_lst] sectPr = self._document_elm.sectPr_lst[key] - return Section(sectPr) + return Section(sectPr, self._part) def __iter__(self): for sectPr in self._document_elm.sectPr_lst: - yield Section(sectPr) + yield Section(sectPr, self._part) def __len__(self): return len(self._document_elm.sectPr_lst) @@ -37,9 +40,10 @@ class Section(object): """ Document section, providing access to section and page setup settings. """ - def __init__(self, sectPr): + def __init__(self, sectPr, part): super(Section, self).__init__() self._sectPr = sectPr + self._part = part @property def bottom_margin(self): @@ -53,6 +57,26 @@ def bottom_margin(self): def bottom_margin(self, value): self._sectPr.bottom_margin = value + @property + def even_odd_header(self): + return Header(self, WD_HEADER_FOOTER.EVEN_PAGE) + + @property + def even_odd_footer(self): + return Footer(self, WD_HEADER_FOOTER.EVEN_PAGE) + + @property + def first_page_footer(self): + return Footer(self, WD_HEADER_FOOTER.FIRST_PAGE) + + @property + def first_page_header(self): + return Header(self, WD_HEADER_FOOTER.FIRST_PAGE) + + @property + def footer(self): + return Footer(self, WD_HEADER_FOOTER.PRIMARY) + @property def footer_distance(self): """ @@ -80,6 +104,10 @@ def gutter(self): def gutter(self, value): self._sectPr.gutter = value + @property + def header(self): + return Header(self, WD_HEADER_FOOTER.PRIMARY) + @property def header_distance(self): """ @@ -146,6 +174,10 @@ def page_width(self): def page_width(self, value): self._sectPr.page_width = value + @property + def part(self): + return self._part + @property def right_margin(self): """ @@ -172,6 +204,10 @@ def start_type(self): def start_type(self, value): self._sectPr.start_type = value + @property + def sectPr(self): + return self._sectPr + @property def top_margin(self): """ diff --git a/tests/test_document.py b/tests/test_document.py index c1cb060ec..8fffe4d6b 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -73,7 +73,7 @@ def it_can_add_a_section(self, add_section_fixture): assert document.element.xml == expected_xml sectPr = document.element.xpath('w:body/w:sectPr')[0] - Section_.assert_called_once_with(sectPr) + Section_.assert_called_once_with(sectPr, document._part) assert section is section_ def it_can_add_a_table(self, add_table_fixture): @@ -105,7 +105,7 @@ def it_provides_access_to_its_paragraphs(self, paragraphs_fixture): def it_provides_access_to_its_sections(self, sections_fixture): document, Sections_, sections_ = sections_fixture sections = document.sections - Sections_.assert_called_once_with(document._element) + Sections_.assert_called_once_with(document._element, document._part) assert sections is sections_ def it_provides_access_to_its_settings(self, settings_fixture): diff --git a/tests/test_section.py b/tests/test_section.py index a497aa727..a89c257f4 100644 --- a/tests/test_section.py +++ b/tests/test_section.py @@ -10,7 +10,14 @@ from docx.enum.section import WD_ORIENT, WD_SECTION from docx.section import Section, Sections +from docx.parts.document import DocumentPart from docx.shared import Inches +from .unitutil.mock import ( + instance_mock +) +from docx.opc.packuri import PackURI +from docx.opc.constants import RELATIONSHIP_TYPE as RT +from docx.parts.header_footer import HeaderFooterPart from .unitutil.cxml import element, xml @@ -38,18 +45,18 @@ def it_can_access_its_Section_instances_by_index(self, index_fixture): # fixtures ------------------------------------------------------- @pytest.fixture - def index_fixture(self, document_elm): - sections = Sections(document_elm) + def index_fixture(self, document_elm, document_part_): + sections = Sections(document_elm, document_part_) return sections, [0, 1] @pytest.fixture - def iter_fixture(self, document_elm): - sections = Sections(document_elm) + def iter_fixture(self, document_elm, document_part_): + sections = Sections(document_elm, document_part_) return sections, 2 @pytest.fixture - def len_fixture(self, document_elm): - sections = Sections(document_elm) + def len_fixture(self, document_elm, document_part_): + sections = Sections(document_elm, document_part_) return sections, 2 # fixture components --------------------------------------------- @@ -58,6 +65,10 @@ def len_fixture(self, document_elm): def document_elm(self): return element('w:document/w:body/(w:p/w:pPr/w:sectPr, w:sectPr)') + @pytest.fixture + def document_part_(self, request): + return instance_mock(request, DocumentPart) + class DescribeSection(object): @@ -109,6 +120,21 @@ def it_can_change_its_page_margins(self, margins_set_fixture): setattr(section, margin_prop_name, new_value) assert section._sectPr.xml == expected_xml + def it_knows_its_default_header(self, section_with_default_header_fixture): + assert section_with_default_header_fixture.header.is_linked_to_previous is False + assert section_with_default_header_fixture.first_page_header.is_linked_to_previous is True + assert section_with_default_header_fixture.even_odd_header.is_linked_to_previous is True + + def it_knows_its_first_header(self, section_with_first_header_fixture): + assert section_with_first_header_fixture.header.is_linked_to_previous is True + assert section_with_first_header_fixture.first_page_header.is_linked_to_previous is False + assert section_with_first_header_fixture.even_odd_header.is_linked_to_previous is True + + def it_knows_its_even_header(self, section_with_even_header_fixture): + assert section_with_even_header_fixture.header.is_linked_to_previous is True + assert section_with_even_header_fixture.first_page_header.is_linked_to_previous is True + assert section_with_even_header_fixture.even_odd_header.is_linked_to_previous is False + # fixtures ------------------------------------------------------- @pytest.fixture(params=[ @@ -122,9 +148,9 @@ def it_can_change_its_page_margins(self, margins_set_fixture): ('w:sectPr/w:pgMar', 'left_margin', None), ('w:sectPr', 'top_margin', None), ]) - def margins_get_fixture(self, request): + def margins_get_fixture(self, request, document_part_): sectPr_cxml, margin_prop_name, expected_value = request.param - section = Section(element(sectPr_cxml)) + section = Section(element(sectPr_cxml), document_part_) return section, margin_prop_name, expected_value @pytest.fixture(params=[ @@ -146,9 +172,9 @@ def margins_get_fixture(self, request): ('w:sectPr/w:pgMar{w:top=-360}', 'top_margin', Inches(0.6), 'w:sectPr/w:pgMar{w:top=864}'), ]) - def margins_set_fixture(self, request): + def margins_set_fixture(self, request, document_part_): sectPr_cxml, property_name, new_value, expected_cxml = request.param - section = Section(element(sectPr_cxml)) + section = Section(element(sectPr_cxml), document_part_) expected_xml = xml(expected_cxml) return section, property_name, new_value, expected_xml @@ -158,9 +184,9 @@ def margins_set_fixture(self, request): ('w:sectPr/w:pgSz', WD_ORIENT.PORTRAIT), ('w:sectPr', WD_ORIENT.PORTRAIT), ]) - def orientation_get_fixture(self, request): + def orientation_get_fixture(self, request, document_part_): sectPr_cxml, expected_orientation = request.param - section = Section(element(sectPr_cxml)) + section = Section(element(sectPr_cxml), document_part_) return section, expected_orientation @pytest.fixture(params=[ @@ -168,9 +194,9 @@ def orientation_get_fixture(self, request): (WD_ORIENT.PORTRAIT, 'w:sectPr/w:pgSz'), (None, 'w:sectPr/w:pgSz'), ]) - def orientation_set_fixture(self, request): + def orientation_set_fixture(self, request, document_part_): new_orientation, expected_cxml = request.param - section = Section(element('w:sectPr')) + section = Section(element('w:sectPr'), document_part_) expected_xml = xml(expected_cxml) return section, new_orientation, expected_xml @@ -179,18 +205,18 @@ def orientation_set_fixture(self, request): ('w:sectPr/w:pgSz', None), ('w:sectPr', None), ]) - def page_height_get_fixture(self, request): + def page_height_get_fixture(self, request, document_part_): sectPr_cxml, expected_page_height = request.param - section = Section(element(sectPr_cxml)) + section = Section(element(sectPr_cxml), document_part_) return section, expected_page_height @pytest.fixture(params=[ (None, 'w:sectPr/w:pgSz'), (Inches(2), 'w:sectPr/w:pgSz{w:h=2880}'), ]) - def page_height_set_fixture(self, request): + def page_height_set_fixture(self, request, document_part_): new_page_height, expected_cxml = request.param - section = Section(element('w:sectPr')) + section = Section(element('w:sectPr'), document_part_) expected_xml = xml(expected_cxml) return section, new_page_height, expected_xml @@ -199,18 +225,18 @@ def page_height_set_fixture(self, request): ('w:sectPr/w:pgSz', None), ('w:sectPr', None), ]) - def page_width_get_fixture(self, request): + def page_width_get_fixture(self, request, document_part_): sectPr_cxml, expected_page_width = request.param - section = Section(element(sectPr_cxml)) + section = Section(element(sectPr_cxml), document_part_) return section, expected_page_width @pytest.fixture(params=[ (None, 'w:sectPr/w:pgSz'), (Inches(4), 'w:sectPr/w:pgSz{w:w=5760}'), ]) - def page_width_set_fixture(self, request): + def page_width_set_fixture(self, request, document_part_): new_page_width, expected_cxml = request.param - section = Section(element('w:sectPr')) + section = Section(element('w:sectPr'), document_part_) expected_xml = xml(expected_cxml) return section, new_page_width, expected_xml @@ -223,9 +249,9 @@ def page_width_set_fixture(self, request): ('w:sectPr/w:type{w:val=evenPage}', WD_SECTION.EVEN_PAGE), ('w:sectPr/w:type{w:val=nextColumn}', WD_SECTION.NEW_COLUMN), ]) - def start_type_get_fixture(self, request): + def start_type_get_fixture(self, request, document_part_): sectPr_cxml, expected_start_type = request.param - section = Section(element(sectPr_cxml)) + section = Section(element(sectPr_cxml), document_part_) return section, expected_start_type @pytest.fixture(params=[ @@ -242,8 +268,74 @@ def start_type_get_fixture(self, request): ('w:sectPr/w:type', WD_SECTION.NEW_COLUMN, 'w:sectPr/w:type{w:val=nextColumn}'), ]) - def start_type_set_fixture(self, request): + def start_type_set_fixture(self, request, document_part_): initial_cxml, new_start_type, expected_cxml = request.param - section = Section(element(initial_cxml)) + section = Section(element(initial_cxml), document_part_) expected_xml = xml(expected_cxml) return section, new_start_type, expected_xml + + @pytest.fixture(params=[ + 'w:sectPr/w:headerReference{w:type=default,r:id=rId1}/r:id' + ]) + def section_with_default_header_fixture(self, request, document_part_, + header_rel_): + header_reltype, header_part, rId = header_rel_ + sectPr_cxml = request.param + document_part_.load_rel(header_reltype, header_part, rId) + section = Section(element(sectPr_cxml), document_part_) + return section + + @pytest.fixture(params=[ + 'w:sectPr/w:headerReference{w:type=first,r:id=rId1}/r:id' + ]) + def section_with_first_header_fixture(self, request, document_part_, + header_rel_): + header_reltype, header_part, rId = header_rel_ + sectPr_cxml = request.param + document_part_.load_rel(header_reltype, header_part, rId) + section = Section(element(sectPr_cxml), document_part_) + return section + + @pytest.fixture(params=[ + 'w:sectPr/w:headerReference{w:type=even,r:id=rId1}/r:id' + ]) + def section_with_even_header_fixture(self, request, document_part_, + header_rel_): + header_reltype, header_part, rId = header_rel_ + sectPr_cxml = request.param + document_part_.load_rel(header_reltype, header_part, rId) + section = Section(element(sectPr_cxml), document_part_) + return section + + @pytest.fixture + def header_rel_(self, header_rId_, header_reltype_, header_part_): + return header_reltype_, header_part_, header_rId_ + + @pytest.fixture + def header_rId_(self): + return 'rId1' + + @pytest.fixture + def document_part_(self, document_partname_): + return DocumentPart(document_partname_, None, None, None) + + @pytest.fixture + def header_part_(self): + return HeaderFooterPart(None, None, None, None) + + @pytest.fixture + def header_reltype_(self): + return RT.HEADER + + @pytest.fixture + def footer_reltype_(self): + return RT.FOOTER + + @pytest.fixture + def document_partname_(self, _baseURI): + return PackURI(_baseURI) + + @pytest.fixture + def _baseURI(self): + return '/baseURI' + From 58e0f239c90633be74ede482cac871f5dfe7ee27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Zoric=CC=8Ca=CC=81k?= <1ondrej.zoricak@gmail.com> Date: Sat, 24 Nov 2018 17:59:30 +0100 Subject: [PATCH 06/13] add different_first_page_header_footer to section --- docx/oxml/__init__.py | 1 + docx/oxml/section.py | 18 ++++++++++++++++ docx/section.py | 8 +++++++ features/steps/section.py | 2 +- ..._a_different_first_page_header_footer.docx | Bin 0 -> 17162 bytes ...no_different_first_page_header_footer.docx | Bin 0 -> 17138 bytes tests/test_section.py | 20 ++++++++++++++++++ 7 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 features/steps/test_files/section_having_a_different_first_page_header_footer.docx create mode 100644 features/steps/test_files/section_having_no_different_first_page_header_footer.docx diff --git a/docx/oxml/__init__.py b/docx/oxml/__init__.py index 2ed8ae251..21516684a 100644 --- a/docx/oxml/__init__.py +++ b/docx/oxml/__init__.py @@ -95,6 +95,7 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): register_element_cls('w:footerReference', CT_HdrFtrRef) register_element_cls('w:hdr', CT_HdrFtr) register_element_cls('w:ftr', CT_HdrFtr) +register_element_cls('w:titlePg', CT_OnOff) from .shape import ( CT_Blip, CT_BlipFillProperties, CT_GraphicalObject, diff --git a/docx/oxml/section.py b/docx/oxml/section.py index 42336d8b4..9358cf525 100644 --- a/docx/oxml/section.py +++ b/docx/oxml/section.py @@ -86,6 +86,7 @@ class CT_SectPr(BaseOxmlElement): )) headerReference = ZeroOrMore('w:headerReference') footerReference = ZeroOrMore('w:footerReference') + titlePg = ZeroOrOne('w:titlePg') @property def bottom_margin(self): @@ -268,6 +269,23 @@ def start_type(self, value): type = self.get_or_add_type() type.val = value + @property + def titlePg_val(self): + """ + The value of `evenAndOddHeaders/@val` or |None| if not present. + """ + titlePg = self.titlePg + if titlePg is None: + return False + return titlePg.val + + @titlePg_val.setter + def titlePg_val(self, value): + if value in [None, False]: + self._remove_titlePg() + else: + self.get_or_add_titlePg().val = value + @property def top_margin(self): """ diff --git a/docx/section.py b/docx/section.py index d795eb7fa..493a8911f 100644 --- a/docx/section.py +++ b/docx/section.py @@ -57,6 +57,14 @@ def bottom_margin(self): def bottom_margin(self, value): self._sectPr.bottom_margin = value + @property + def different_first_page_header_footer(self): + return self._sectPr.titlePg_val + + @different_first_page_header_footer.setter + def different_first_page_header_footer(self, value): + self._sectPr.titlePg_val = value + @property def even_odd_header(self): return Header(self, WD_HEADER_FOOTER.EVEN_PAGE) diff --git a/features/steps/section.py b/features/steps/section.py index 650e72e83..6941f4b39 100644 --- a/features/steps/section.py +++ b/features/steps/section.py @@ -207,7 +207,7 @@ def then_the_reported_section_start_type_is_type(context, start_type): @then('the section.different_first_page_header_footer is {value}') def then_section_different_first_page_header_footer_is_value(context, value): - assert context.section.different_first_page_header_footer is value + assert context.section.different_first_page_header_footer is (value == 'True') @then('header, first_page_header, even_odd_header is present in document.section') def then_header_of_type_is_present_in_document_section(context): diff --git a/features/steps/test_files/section_having_a_different_first_page_header_footer.docx b/features/steps/test_files/section_having_a_different_first_page_header_footer.docx new file mode 100644 index 0000000000000000000000000000000000000000..ee5c6a6822639a328bd752bc3aebe75e514c5b0b GIT binary patch literal 17162 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
p+!p=)2i~C#HY-)hupa&VbI;@ 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 literal 0 HcmV?d00001 diff --git a/tests/test_section.py b/tests/test_section.py index a89c257f4..920a3428a 100644 --- a/tests/test_section.py +++ b/tests/test_section.py @@ -135,6 +135,12 @@ def it_knows_its_even_header(self, section_with_even_header_fixture): assert section_with_even_header_fixture.first_page_header.is_linked_to_previous is True assert section_with_even_header_fixture.even_odd_header.is_linked_to_previous is False + def it_have_different_first_page_header_footer(self, section_with_different_first_page_header_footer_fixture): + assert section_with_different_first_page_header_footer_fixture.different_first_page_header_footer is True + + def it_have_no_different_first_page_header_footer(self, section_without_different_first_page_header_footer_fixture): + assert section_without_different_first_page_header_footer_fixture.different_first_page_header_footer is False + # fixtures ------------------------------------------------------- @pytest.fixture(params=[ @@ -307,6 +313,20 @@ def section_with_even_header_fixture(self, request, document_part_, section = Section(element(sectPr_cxml), document_part_) return section + @pytest.fixture(params=[ + 'w:sectPr/w:titlePg' + ]) + def section_with_different_first_page_header_footer_fixture(self, request, document_part_): + sectPr_cxml= request.param + return Section(element(sectPr_cxml), document_part_) + + @pytest.fixture(params=[ + 'w:sectPr' + ]) + def section_without_different_first_page_header_footer_fixture(self, request, document_part_): + sectPr_cxml = request.param + return Section(element(sectPr_cxml), document_part_) + @pytest.fixture def header_rel_(self, header_rId_, header_reltype_, header_part_): return header_reltype_, header_part_, header_rId_ From ec7608074120f5e8100bd7639d1379bab7718e8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Zoric=CC=8Ca=CC=81k?= <1ondrej.zoricak@gmail.com> Date: Sun, 25 Nov 2018 19:09:16 +0100 Subject: [PATCH 07/13] test: it_can_change_different_first_page_header_footer_option --- tests/test_section.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_section.py b/tests/test_section.py index 920a3428a..e4b7f40c0 100644 --- a/tests/test_section.py +++ b/tests/test_section.py @@ -141,6 +141,15 @@ def it_have_different_first_page_header_footer(self, section_with_different_firs def it_have_no_different_first_page_header_footer(self, section_without_different_first_page_header_footer_fixture): assert section_without_different_first_page_header_footer_fixture.different_first_page_header_footer is False + def it_can_change_different_first_page_header_footer_option(self, section_can_change_different_first_page_header_footer_fixture): + section, expected_xml = section_can_change_different_first_page_header_footer_fixture + if section.different_first_page_header_footer is False: + section.different_first_page_header_footer = True + assert section._sectPr.xml == expected_xml + else: + section.different_first_page_header_footer = False + assert section._sectPr.xml == expected_xml + # fixtures ------------------------------------------------------- @pytest.fixture(params=[ @@ -327,6 +336,15 @@ def section_without_different_first_page_header_footer_fixture(self, request, do sectPr_cxml = request.param return Section(element(sectPr_cxml), document_part_) + @pytest.fixture(params=[ + ('w:sectPr/w:titlePg', 'w:sectPr'), + ('w:sectPr', 'w:sectPr/w:titlePg') + ]) + def section_can_change_different_first_page_header_footer_fixture(self, request, document_part_): + sectPr_cxml, expected_cxml = request.param + return Section(element(sectPr_cxml), document_part_), xml(expected_cxml) + + @pytest.fixture def header_rel_(self, header_rId_, header_reltype_, header_part_): return header_reltype_, header_part_, header_rId_ From f3c39e226f0120f2e88b9d7eff1e4921034c42f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Zoric=CC=8Ca=CC=81k?= <1ondrej.zoricak@gmail.com> Date: Sun, 25 Nov 2018 19:36:32 +0100 Subject: [PATCH 08/13] add setting odd_and_even_pages_header_footer --- docx/oxml/__init__.py | 5 +++ docx/oxml/settings.py | 38 +++++++++++++++++++++++ docx/settings.py | 12 ++++++++ features/steps/section.py | 4 +-- features/steps/settings.py | 4 +-- features/sts-settings-props.feature | 2 +- tests/test_settings.py | 48 +++++++++++++++++++++++++++++ 7 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 docx/oxml/settings.py create mode 100644 tests/test_settings.py diff --git a/docx/oxml/__init__.py b/docx/oxml/__init__.py index 21516684a..83d004886 100644 --- a/docx/oxml/__init__.py +++ b/docx/oxml/__init__.py @@ -97,6 +97,11 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None): register_element_cls('w:ftr', CT_HdrFtr) register_element_cls('w:titlePg', CT_OnOff) + +from .settings import CT_Settings +register_element_cls('w:settings', CT_Settings) +register_element_cls('w:evenAndOddHeaders', CT_OnOff) + from .shape import ( CT_Blip, CT_BlipFillProperties, CT_GraphicalObject, CT_GraphicalObjectData, CT_Inline, CT_NonVisualDrawingProps, CT_Picture, diff --git a/docx/oxml/settings.py b/docx/oxml/settings.py new file mode 100644 index 000000000..d6bb34e6d --- /dev/null +++ b/docx/oxml/settings.py @@ -0,0 +1,38 @@ +# encoding: utf-8 + +""" +Custom element classes related to the styles part +""" + +from .xmlchemy import ( + BaseOxmlElement, ZeroOrOne +) + + +class CT_Settings(BaseOxmlElement): + """ + `w:settings` element, defining behavior defaults for settings + and containing `w:evenAndOddHeaders` child elements that define even and odd headers + """ + _tag_seq = ( + 'w:evenAndOddHeaders' + ) + + evenAndOddHeaders = ZeroOrOne('w:evenAndOddHeaders') + + @property + def evenAndOddHeaders_val(self): + """ + The value of `evenAndOddHeaders/@val` or |None| if not present. + """ + evenAndOddHeaders = self.evenAndOddHeaders + if evenAndOddHeaders is None: + return False + return evenAndOddHeaders.val + + @evenAndOddHeaders_val.setter + def evenAndOddHeaders_val(self, value): + if value in [None, False]: + self._remove_evenAndOddHeaders() + else: + self.get_or_add_evenAndOddHeaders().val = value diff --git a/docx/settings.py b/docx/settings.py index 737146697..3ee83d1fa 100644 --- a/docx/settings.py +++ b/docx/settings.py @@ -18,3 +18,15 @@ class Settings(ElementProxy): """ __slots__ = () + + @property + def odd_and_even_pages_header_footer(self): + """ + |Length| object representing the bottom margin for all pages in this + section in English Metric Units. + """ + return self._element.evenAndOddHeaders_val + + @odd_and_even_pages_header_footer.setter + def odd_and_even_pages_header_footer(self, value): + self._element.evenAndOddHeaders_val = value \ No newline at end of file diff --git a/features/steps/section.py b/features/steps/section.py index 6941f4b39..5c31dda4a 100644 --- a/features/steps/section.py +++ b/features/steps/section.py @@ -13,7 +13,7 @@ from docx.section import Section from docx.shared import Inches -from helpers import test_docx +from helpers import test_docx, tri_state_vals # given ==================================================== @@ -207,7 +207,7 @@ def then_the_reported_section_start_type_is_type(context, start_type): @then('the section.different_first_page_header_footer is {value}') def then_section_different_first_page_header_footer_is_value(context, value): - assert context.section.different_first_page_header_footer is (value == 'True') + assert context.section.different_first_page_header_footer is tri_state_vals[value] @then('header, first_page_header, even_odd_header is present in document.section') def then_header_of_type_is_present_in_document_section(context): diff --git a/features/steps/settings.py b/features/steps/settings.py index e75d0603c..e8d1b519c 100644 --- a/features/steps/settings.py +++ b/features/steps/settings.py @@ -44,7 +44,7 @@ def then_document_settings_is_a_Settings_object(context): document = context.document assert type(document.settings) is Settings -@then('document.settings.even_and_odd_headers is {value}') +@then('document.settings.odd_and_even_pages_header_footer is {value}') def then_document_settings_even_and_odd_header_is_value(context, value): document, expected_value = context.document, tri_state_vals[value] - assert document.settings.odd_and_even_pages_header_footer is expected_value \ No newline at end of file + assert document.settings.odd_and_even_pages_header_footer is expected_value diff --git a/features/sts-settings-props.feature b/features/sts-settings-props.feature index d0d10e232..b21e0a513 100644 --- a/features/sts-settings-props.feature +++ b/features/sts-settings-props.feature @@ -6,7 +6,7 @@ Feature: Access and change settings properties Scenario Outline: Get even and odd headers settings Given a settings having even and odd headers settings - Then document.settings.even_and_odd_headers is + Then document.settings.odd_and_even_pages_header_footer is Examples: Even and odd headers settings values | a-or-no | value | diff --git a/tests/test_settings.py b/tests/test_settings.py new file mode 100644 index 000000000..0c4c5a3ef --- /dev/null +++ b/tests/test_settings.py @@ -0,0 +1,48 @@ +# encoding: utf-8 + +""" +Test suite for the docx.settings module +""" + +from __future__ import absolute_import, print_function, unicode_literals + +import pytest + +from docx.settings import Settings + +from .unitutil.cxml import element, xml + + +class DescribeSettings(object): + + def it_can_remove_odd_and_even_pages_header_footer(self, remove_even_and_odd_headers_fixture): + settings, expected_xml = remove_even_and_odd_headers_fixture + settings.odd_and_even_pages_header_footer = False + assert settings._element.xml == expected_xml + + def it_can_add_odd_and_even_pages_header_footer(self, add_even_and_odd_headers_fixture): + settings, expected_xml = add_even_and_odd_headers_fixture + settings.odd_and_even_pages_header_footer = True + assert settings._element.xml == expected_xml + + # fixtures ------------------------------------------------------- + + @pytest.fixture(params=[ + ('w:settings/w:evenAndOddHeaders{w:val=1}', + 'w:settings'), + ]) + def remove_even_and_odd_headers_fixture(self, request): + initial_cxml, expected_cxml = request.param + settings = Settings(element(initial_cxml)) + expected_xml = xml(expected_cxml) + return settings, expected_xml + + @pytest.fixture(params=[ + ('w:settings', + 'w:settings/w:evenAndOddHeaders'), + ]) + def add_even_and_odd_headers_fixture(self, request): + initial_cxml, expected_cxml = request.param + settings = Settings(element(initial_cxml)) + expected_xml = xml(expected_cxml) + return settings, expected_xml \ No newline at end of file From f2a52ebaf35c9d8b896f1961e54681a2281e74fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Zoric=CC=8Ca=CC=81k?= <1ondrej.zoricak@gmail.com> Date: Sun, 25 Nov 2018 22:48:40 +0100 Subject: [PATCH 09/13] add tests for settings --- tests/oxml/test_settings.py | 48 +++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 tests/oxml/test_settings.py diff --git a/tests/oxml/test_settings.py b/tests/oxml/test_settings.py new file mode 100644 index 000000000..d470b500d --- /dev/null +++ b/tests/oxml/test_settings.py @@ -0,0 +1,48 @@ +# encoding: utf-8 + +""" +Test suite for the docx.oxml.styles module. +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +import pytest + +from ..unitutil.cxml import element, xml + + +class DescribeCT_Settings(object): + + def it_can_add_evenAndOddHeaders_val(self, add_evenAndOddHeaders_val_fixture): + settings, expected_xml = add_evenAndOddHeaders_val_fixture + settings.evenAndOddHeaders_val = True + assert settings.xml == expected_xml + + def it_can_remove_evenAndOddHeaders_val(self, remove_evenAndOddHeaders_val_fixture): + settings, expected_xml = remove_evenAndOddHeaders_val_fixture + settings.evenAndOddHeaders_val = False + assert settings.xml == expected_xml + + # fixtures ------------------------------------------------------- + + @pytest.fixture(params=[ + ('w:settings', + 'w:settings/w:evenAndOddHeaders'), + ]) + def add_evenAndOddHeaders_val_fixture(self, request): + settings_cxml, expected_cxml = request.param + settings = element(settings_cxml) + expected_xml = xml(expected_cxml) + return settings, expected_xml + + @pytest.fixture(params=[ + ('w:settings/w:evenAndOddHeaders{w:val=1}', + 'w:settings'), + ]) + def remove_evenAndOddHeaders_val_fixture(self, request): + settings_cxml, expected_cxml = request.param + settings = element(settings_cxml) + expected_xml = xml(expected_cxml) + return settings, expected_xml \ No newline at end of file From ec229c50c1f69c6672d76def3d72aae3f9816ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Zoric=CC=8Ca=CC=81k?= <1ondrej.zoricak@gmail.com> Date: Mon, 26 Nov 2018 21:59:22 +0100 Subject: [PATCH 10/13] add acceptable tests for header and footer --- features/ftr-access-props.feature | 14 +++++ features/ftr-link-unlink.feature | 15 +++++ features/hdr-access-props.feature | 14 +++++ features/hdr-link-unlink.feature | 15 +++++ features/steps/footer.py | 53 ++++++++++++++++++ features/steps/header.py | 52 +++++++++++++++++ .../test_files/doc-word-linked_footer.docx | Bin 0 -> 18750 bytes .../test_files/doc-word-linked_header.docx | Bin 0 -> 18556 bytes .../test_files/doc-word-no-linked_footer.docx | Bin 0 -> 19480 bytes .../test_files/doc-word-no-linked_header.docx | Bin 0 -> 15934 bytes 10 files changed, 163 insertions(+) create mode 100644 features/ftr-access-props.feature create mode 100644 features/ftr-link-unlink.feature create mode 100644 features/hdr-access-props.feature create mode 100644 features/hdr-link-unlink.feature create mode 100644 features/steps/footer.py create mode 100644 features/steps/header.py create mode 100644 features/steps/test_files/doc-word-linked_footer.docx create mode 100644 features/steps/test_files/doc-word-linked_header.docx create mode 100644 features/steps/test_files/doc-word-no-linked_footer.docx create mode 100644 features/steps/test_files/doc-word-no-linked_header.docx diff --git a/features/ftr-access-props.feature b/features/ftr-access-props.feature new file mode 100644 index 000000000..7edf25427 --- /dev/null +++ b/features/ftr-access-props.feature @@ -0,0 +1,14 @@ +# Created by Ondrej at 18/11/2018 +Feature: Access and change footer properties + In order to discover and modify document footer behaviors + As a developer using python-docx + I need a way to get and set the properties of a footer + + Scenario Outline: Get is linked to previous property + Given a footer having is_linked_to_previous property + Then document.sections[-1].footer.is_linked_to_previous is + + Examples: Even and odd footers settings values + | a-or-no | value | + | a | True | + | no | False | \ No newline at end of file diff --git a/features/ftr-link-unlink.feature b/features/ftr-link-unlink.feature new file mode 100644 index 000000000..8ef41ec97 --- /dev/null +++ b/features/ftr-link-unlink.feature @@ -0,0 +1,15 @@ +Feature: Link footer of section + In order link footer to section + As a developer using python-docx + I need a way to link footer in one step + + + Scenario Outline: Link and unlink footer to section + Given a document section having footer + When I set footer to + Then a section footer is_linked to previous is + + Examples: Footer values + | value | + | True | + | False | diff --git a/features/hdr-access-props.feature b/features/hdr-access-props.feature new file mode 100644 index 000000000..7e19b4a8e --- /dev/null +++ b/features/hdr-access-props.feature @@ -0,0 +1,14 @@ +# Created by Ondrej at 18/11/2018 +Feature: Access and change header properties + In order to discover and modify document header behaviors + As a developer using python-docx + I need a way to get and set the properties of a header + + Scenario Outline: Get is linked to previous property + Given a header having is_linked_to_previous property + Then document.sections[-1].header.is_linked_to_previous is + + Examples: Even and odd headers settings values + | a-or-no | value | + | a | True | + | no | False | \ No newline at end of file diff --git a/features/hdr-link-unlink.feature b/features/hdr-link-unlink.feature new file mode 100644 index 000000000..da69425ef --- /dev/null +++ b/features/hdr-link-unlink.feature @@ -0,0 +1,15 @@ +Feature: Link header of section + In order link header to section + As a developer using python-docx + I need a way to link header in one step + + + Scenario Outline: Link and unlink header to section + Given a document section having header + When I set header to + Then a section header is_linked to previous is + + Examples: Header values + | value | + | True | + | False | diff --git a/features/steps/footer.py b/features/steps/footer.py new file mode 100644 index 000000000..3d836d95c --- /dev/null +++ b/features/steps/footer.py @@ -0,0 +1,53 @@ +# encoding: utf-8 + +""" +Step implementations for document settings-related features +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from behave import given, then, when + +from docx import Document +from docx.settings import Settings + +from helpers import test_docx, tri_state_vals + + +# given ==================================================== + +@given('a footer having a is_linked_to_previous property') +def given_a_document_having_a_footer_linked_to_previous(context): + context.document = Document(test_docx('doc-word-linked_footer')) + + +@given('a footer having no is_linked_to_previous property') +def given_a_document_having_no_footer_linked_to_previous(context): + context.document = Document(test_docx('doc-word-no-linked_footer')) + + +@given('a document section having footer') +def given_a_document_section_having_footer(context): + context.document = Document(test_docx('a_footer_of_all_types')) + +# when ===================================================== + + +@when('I set footer to {value}') +def when_i_link_footer_to_previous_section(context, value): + document, value = context.document, tri_state_vals[value] + document.sections[-1].footer.is_linked_to_previous = value + +# then ===================================================== + +@then('document.sections[-1].footer.is_linked_to_previous is {value}') +def then_document_settings_even_and_odd_footer_is_value(context, value): + document, expected_value = context.document, tri_state_vals[value] + assert document.sections[-1].footer.is_linked_to_previous is expected_value + +@then('a section footer is_linked to previous is {value}') +def then_section_is_linked_to_previous_with_value(context, value): + document, expected_value = context.document, tri_state_vals[value] + assert document.sections[-1].footer.is_linked_to_previous is expected_value \ No newline at end of file diff --git a/features/steps/header.py b/features/steps/header.py new file mode 100644 index 000000000..178fbc299 --- /dev/null +++ b/features/steps/header.py @@ -0,0 +1,52 @@ +# encoding: utf-8 + +""" +Step implementations for document settings-related features +""" + +from __future__ import ( + absolute_import, division, print_function, unicode_literals +) + +from behave import given, then, when + +from docx import Document +from docx.settings import Settings + +from helpers import test_docx, tri_state_vals + + +# given ==================================================== + +@given('a header having a is_linked_to_previous property') +def given_a_document_having_a_header_linked_to_previous(context): + context.document = Document(test_docx('doc-word-linked_header')) + + +@given('a header having no is_linked_to_previous property') +def given_a_document_having_no_header_linked_to_previous(context): + context.document = Document(test_docx('doc-word-no-linked_header')) + + +@given('a document section having header') +def given_a_document_section_having_header(context): + context.document = Document(test_docx('a_header_of_all_types')) + +# when ===================================================== + +@when('I set header to {value}') +def when_i_link_header_to_previous_section(context, value): + document, value = context.document, tri_state_vals[value] + document.sections[-1].header.is_linked_to_previous = value + +# then ===================================================== + +@then('document.sections[-1].header.is_linked_to_previous is {value}') +def then_document_settings_even_and_odd_header_is_value(context, value): + document, expected_value = context.document, tri_state_vals[value] + assert document.sections[-1].header.is_linked_to_previous is expected_value + +@then('a section header is_linked to previous is {value}') +def then_section_is_linked_to_previous_with_value(context, value): + document, expected_value = context.document, tri_state_vals[value] + assert document.sections[-1].header.is_linked_to_previous is expected_value diff --git a/features/steps/test_files/doc-word-linked_footer.docx b/features/steps/test_files/doc-word-linked_footer.docx new file mode 100644 index 0000000000000000000000000000000000000000..2c6babf0572cbf6c7c9de2e60824cdc8d2567ca2 GIT binary patch literal 18750 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 KX9@rK_5T56X={K0 literal 0 HcmV?d00001 diff --git a/features/steps/test_files/doc-word-linked_header.docx b/features/steps/test_files/doc-word-linked_header.docx new file mode 100644 index 0000000000000000000000000000000000000000..3eb2fb32a85bfda000def947ab3a8a41d5532d02 GIT binary patch literal 18556 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 R0027hrwd4DxkbLe`+oyFhU5SM literal 0 HcmV?d00001 diff --git a/features/steps/test_files/doc-word-no-linked_header.docx b/features/steps/test_files/doc-word-no-linked_header.docx new file mode 100644 index 0000000000000000000000000000000000000000..6b3d61aab36a17dcb2edf653f0b266df565e9372 GIT binary patch literal 15934 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 A%m4rY literal 0 HcmV?d00001 From 6c2470604a63a25ce4202adaef43eb92befe0df0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Zoric=CC=8Ca=CC=81k?= <1ondrej.zoricak@gmail.com> Date: Wed, 28 Nov 2018 14:16:09 +0100 Subject: [PATCH 11/13] update feature test_files --- .../test_files/doc-word-no-linked_header.docx | Bin 15934 -> 19305 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/features/steps/test_files/doc-word-no-linked_header.docx b/features/steps/test_files/doc-word-no-linked_header.docx index 6b3d61aab36a17dcb2edf653f0b266df565e9372..f8c6480c3eb68c1bcbbfa39b69da1a4c3e50ad3a 100644 GIT binary patch delta 8331 zcmeHsXH-;8x^)9h&d`z(B!lFfGmU^C89{OsBJB>4W~iajDo@gce-&A7pZP;5%@s<(NwprFni7m1lan_o?W)wNnb#c?C z@BN(pjQ-nv?Ebd=wb4Gqru(yD@&{O-?pdl$JE1$$hVY)%U1~lYykJpFj{*2y#CC~o z%6=r_P2(*o9luv7#mi6}#l{6K=($tcOcgu?+Y{$$5bw2>zrSR@d%!QJEGlIj!ee=k2 zTxG!0Ebo39{>JC=|vQ?GZ$3ss9{vT7JbGZi%`T`5P|Q$Vxe64ajbZSXZ5-h(x96qgYOWk6aZPP zr`80-x`+>^3a%-gEv}z3(ZkULl4WrM$;=TPiEXdOYWmd)hF^wST!3xme(yeDmKQU- zj1B_n5r9CXsEG1&^R(r&b+hqtv2*q2^>=ZeO6qZ4WQ0Tje!x!ux*cUpmrKVC|Q)MW&a~uG~>EF$>LAz76M%A z3THYINZr9lN#c!@Vxky-&x} zV*o!74JP@Vb6JLGn!m`oC6IA1%4WJT)k-pnQ7u`ps{F?EAzqOv-{^3 z%5tYs`b_Y?h(H;qjyo$3jVx>3cUwdV)6Q#b(VcF3cfiU>8u0shA?45IY+LP@zZXrC zK{M1dOdLAG^{}LTR#>Jy*;y#)yB<{fi4hfav+7Fv?ADpt-LVCIjP3=C4s z6}1;H9r6)`-UYymDkQbaZ+B$UA2^|LovN6&m%d0NdazT{EAl{h(ok&JgmWUWeIa+o z30DK4=r#MG>;I1J(Idl+wRgP))if#`#q9}wJDnGW>4`0{)n6%(@VGj@3F3$}amZ;D z)K=P59|qQ##66(i?7)#TEYYJlr}7K=s`YFAM6NH;r&0&&e71S|&rs-GH9K%Yg+dB0 z2t@M#L*cQVm93qp@J%G-AvNPB_(>wR6VDi!X20NaGrYpN>oPvQRccbR+ASWTe4U$Q9p$TH>enTIZZc6jD6Ej;CD}eU48yfPXDWaifA9@10vm4lXi@VfsO>Vs zTJ{j4(SlE`3b&QFUaC0I$yDd&M?e^lnz8Q-S<^{v3_Qg*K$o5v$h$Ib@I&pWXo*b_DDw+9oz|O>deE&n3+d$56;6L-ck`Taq^eal$6ym~t(P+P{EPv|ks= zM)%!VyN_1TTkW@1rq{z~ri^&Xpqt*BJ=?jBBaId(a&xo~SBvVmz(JWQc2HqD+|YJ6 z+Clv? zA-w!#EtbxIH)98UX~|-THGfKAVPBFkamMVoHdhkyvI(Y0oTU7`Y_@FuwFSeIy>RP3~fvW}j z8YO;GC_q;Cxzj4&$vvMMk;_RG3Pz!(yih zKU(!UV~hBDj(wO8zUim+=eMCD%?6Re460|U?lB&DwHhX|%zdBoM#KT#f;YAwn?G3L zvH4A?FU%KD+o=6I|8gJX9JT6Z>JDJp*FNTdQG84qEAPYFX)Kyt-lpqtzqc|XN6)*Q ztMCv}+*F-mdT=<%;hcHG7hn1oyDOoLsRN1I8E(kCx-v2E$xX<%kL7v65tR$+3krW3 zSkrSITd>KR$WtD7DR~X(n!W+aM}B0B;uEWI9jG;eUEYGeDv7 z{i51V%9G^E{;lps^~lb`0J?t{JPLxsqYcG;AI+#+g3aNJpw1QI-_Sw8324y-t4awt z&@l;u0Sih5lHfM*FTg@NGGj1(gw_zNRC1_>Vy~n07k{u9IY$5WY343$_w1BK#ohLY zN2xAE6UAOCKJ6p2DxZN(w!0AV2l~(#hTJL+D}(zh`vpEJOEXwmhP<4FnmY51YW%*{ zmhrWPOW&OeyyWsttkZ$jH-e^4Y4hT+mNP^}s^F?+p_?-OOC`eCjEEsY4RsQuX7>B| z)-qB)u%E7-bikKlS&rLip*V~0;!>wynap(?5FZHRf!_z_or(|3g+78Ye+?e?q;vei zUfK>1TxL1Pd{3yypYr7H%g8d{IM#}7Y$3GrI~kunzFbl#&*yvvNZ!w=ildJtGCGV; zzhFg5+E^n$K+{6vQ@Gsh%1_#bukhD%rC(yFNec@XHr5cLF*u%w0YSyiacU_!uE7|O zG?$22*DI{sj695602)OCG4~qMqw9vdXk%@TtyXy2)9psr^T*{3d1gyxlVjT%avQxH zEewHXzw*?g`c3e0a>{5Zl{guTrumWjqdm4}((l?JJCg5G+A>x~p+#$hSgyEi9OSiT zwsLxqi2AZ!=L+S`2ak}5upKd>4-1i{kwySTvqr3P-eaYzzh@jw+~s^D1%xK)ZD1CW%1_cUEA{O1801sU^;YW>G)3-G#->b~L+_D#|^q-08ofNRCixRxl zfLZ|q^y0ns-0*IOq90()aPX**jAEx72$4}k&Z&PIDPGccd z`N|K)Y#R>~=Jun=2I)Z81dXYM=Ygmyno79V0ic1(Lm|G#2e|kWppDi{&gqm z=(@ZZuC?0U;`;SXw^vsH(tl|DR^4Wm*%j7JNdU9Pn~OmaF+s!tOvk74LprvlzWG^t zmZ6!NC6W3$r*Hl;E0~m(7X^25pC+?JH3buMSjYH~1d(@koGt@e{kYs2eoV(Ty$|2p z!a^(Jq}dA9Yp2?Njt(RZ(FOb+r0y$Fpzu=M#imP9Yqmyi#=+lI{KOswzWw%^)8`v#-x z@a18yr5619z<&la97BH2ippCDk)|VLT8I)kSRwK#kwXvtG&ki;#rZ4#b|jq5Z1^cv zAwnxBZBAVX7g319($7>vingbaK(M7I9k43l2LxH3RbeUBUQlJ>lxTqk)Lhx5t+UCH z58@+oto5m=={Hr`&(#CH%jINg@V|9jhoP{f$iL<^P+=v}hQg9ZaM2Bx5XKne=3dr! z_w3CI!!nj{uoR4AhyRQDnhJn3Bv`OxJ&PkRBw>${CA*rc(qUr6pL~TKzQSD#DBL8T zFX)?Ldl{V5{6s@wpm`5wGwzb?7*-K9gbXYcJGSc)%f*`>J_)WKtvTvh7kY}Zk?gaY zXt_Tp^W~EFsfptc!-7D9*cp+2nK!Zaf7Nt#jE;G%h@4?JroDD=x|p=;K?7a{Dt{H*Qu(|5&Iybr^LyKq>~ zgz?%J2cI~$4HvVxh}mozJU|!0JhSK9`$&^(}BZ1;1795-`6|zj=a@^9-hOe=7KQ|zt1!d zaZNKqs(uXOW$EU4fpJPNAB`m{x&MTzDdO!Z8<>_Z7q5qo3tQ4v^azUe{G4oAiL!e5nv=R7lmKj2zmwk}Rp{K$V(lcpmt$&ESFBA`^W@71$oI8d3x3(7 zgT7=%ITak?gQkx!BBf_TLh6#Tfx_?6rBy@6HOZ6%qO-fE>hyi#Rzi?ZBk!sCEn7&# z^2`Zdm2z03)8(4PC(Skql#lN?xC{r<<5ES%Lr!K)SF|!YwCSsi0hX zdO6ZBap=uc{#OLcen63XH2nSi#7t*ODJ&u0Nh1%khosa_aa(~Uup;N4%jdggzj+B! zON-5)Y1FinO3}^JBwrw17;99# zYdr#W8YBwYt=TmSjsh4WHv002aogva)PmstpN1Cv@W@DHxHhSlR?Ssfu?bnt|I1DwsMWSV_K`AMb5R z%@`^jsaw?mfeV6s7)XZvcg0vT>iQxsYx);!)gM?)XGvF(Tu~d~jpT(r8qNOvxF0lA6|LTQ)NN)1uY_;&l^)K>@K4-0wg^s^`wEt^6R@sH2p- zf3Tw?E{n3Gl!QbKT}hZ;Fks{}Ki0*e3Cr2^Y!YTVN-z0&(5@PLHF3zR1&aGp9-leUe4uOJiFDxJ z68uS*My{0>(Oj3$vj8}58s6gE5H&v0trHFo$4opac3R~$-Sw|nTwAIkgV_(Mj64W{ z!bf^NUy)@Yv52K;!{?@+OVy+>m$G}Y@wx}`zN_7a@_2Zx`dLGIqSgn!OtszD6N83L z@Whs|aEl?miUPUD^)H{^s~+a5v|hoQc(*erirdx2%yAh5*i@qNG&+R>iIkT7wiy`9 zrM3_x_}Fx>dXm(|7d1nMlyo@IhN`fE(P8$S-Dmib6XI)O{fzj@V7@n%ROZ!bg5o&d zT{66_sb|`U&^bij`aglre;bhSQyxyDTFBE z$_y7-?9@_O-1;)YuBt43Oy}gHt8S&`IZNtG<9Zts%DG%&fcUUFu^pRmr9k97iaM(-he(XXlxZJ z?8}%Pv(Qyye@0BqfLh-lOGiWJ0sSkJ7f~glBejkJ0$F(4IeYQ(-u!E8{A2Hb^b%21 z{_VoUM#xF>(*CpO^|$AsB1DuVi_$+E0e?MjLb+tW9kRc(1^&zHZ>qM43dvg}#>D^7 zTL_UX$;+9Ea@l@Q@rMKzyeX^v^AvxS>`+snFOnlVC7BS8QZ$?#6#p>Wza03xesl9~ z^gJp=xD?Z$SvjB7ErNBLKZo17+9F7$sQ*l|wo!de=n+^_EPoaXZx-H*I*7kpqYwfi z#fwhLglLswXZf2b!vDiC{A+XcIsP2wg_xD0MVv^}pyzTUATlfnH&io|7ZD@PPj^F$ z^QbWfD6^O9ud%3BK0iW5nhV7!H0VnLh*9Y~C{Ce~UzY)aPD7ss1etP@TyC>WFG3DT6>ZK3?8#E)QLt e`5e9NTz;3lv^6lXZu(K8eqT|c6{~V{_x}KPC0h9m$fDaI- zr>l|)1OO^2YT5ZfxEJX|dR=e^jU&{F+M2lQEeqa=?7F4e2rZV_s#@X1NZY#77pFl& zx)44Ww8nst(Mrp@O%m(qh7(rz5GaM+q4v+Cw zxXZVO8x7Mr9clmk0E@lNG~}7{X13xsKPjPD*=n6fFT|I_9qMEB+|8(1O^yKSy(jnX z$}c)ZtFWu?JL-X>L`ia5G+zxXcF==uOxcr z;LO3-^*6RFaMx}LOrsC@a(TE)Bd6qJ$_loZ7i?g+I8f({u~Bm#^f9mvf!9$VIP8F4 zqwU4VpdDJH`$=ydcuL0#Z>WVDD>zQ4-ViY1W$9kUMe|T`?y9U4<8dU4ub-ZD=R8zo zawq2UC6@rDM^|su_MSMQ)3e9p*9Fd6yGsM`ThalzE^VL zK=>4Omil87UJ~kvSMkhp7tZP@tLLW>1>!2xwDm8%6|sXliSEC*QluPMrYA{RZcfQ$ zF0&x7?yh;r*8i%{HBGaiPWHH&Iq=g&W$wt~&Bifd_Bodx6sIr@Wa=_j7MId=^5b)! zI$H}<+axvnW@oC0e_(Ck!wkX=%vMQ#imzct3TJIl(L1(lC{Ks^hd#Di9xzRrY+Rgw z+wl@B(KNNP-12_%m+q^xRLEhy=99LaC8dQ{u^dpI-<{q^({9Nq^uDJc75d>7_I}GhZ%~S)sWWU}k z?fCNjROWv3T;1;N(RN`nnv-v1;9TYx@b`J*awdO?Zh!!QavA`D@gfrLdHG-@FvWG_9z zy*(wFJlh^Q@_MhqLR06Q2BBxTr5OqHRl0^mQesn$qP`0b#V}dHKBZDdsi$Hh3RTP4 z>B8V5=V(F7NzP9Ox?rlHQ35Pf2+5U5n zCJAO`5_>lWp2ORS9*I z3^G3|S-0!GhUBrRpQLFywQ7(Tsm-Jl|HZjoJM$~@l=^Asvu9uZgg2_QEK4okq`XEQ zG=EfG)&)PxkCGQ5sU8kJjjRmt$4&f@@iv`~IlldL{0e`wYxk(H5+z&wk}KoS?-i`N zyNWWx3dJkvndp*k`SPSmw~Z#R0x`4YLX#4fJ`;tB2+`)T(9(V=Og3>|zN3`?rq5@o zyr2ElOnf>rox@aX{->9aof5QN6}&YzM#oB7iDCC&>$~ehfYBPV)TPDq?zo)nk`~5Z z%BdH=*-s8yc|V;=Kb|S64{-N9DseZPU`%lfs1Ou-mw4H>OJQ4O#$7$IchlI3vKgwU z#rQ0<@#LoXAu=ZSG_Hy+_)OV}AVFAt3_KnUZ|=gZFMXd5@)Y(K;4*Jy>bRwMX=pi! z%62|!XGiGKwBGld^Eb-Rc9Bw*@fKayGDX`)m=KBb(lTtLu*973so7)_I8O(9 z_za9iB>45V{D6-aX}9tqv;EQWc13z}v7TX9MqoNZgOn@zZjBZU-}1H?L$BRANp!}2 z*nYS<3ZY%z4+-V3kz?wX?#T8|ij1>FBtqii3_LLbH9TC~+M9MBxBzLklBDy70p!>% z_@?M+tsO_VDmup}q|#d|jU109nX&e<7p`-%E(p*2xL>`6HIA+$J50gFyy|!Tm>K(U zaaCQbd#o@iLg~mojk5oZ|EfyCbt;@E@f!#Wz=Ln=1&*e9vfq{NAU(i{~Tp{I0Rd7H<_ays^R#(V1=X66suFSx@*6n9i#+G|m2lrriy0j2dru_y950-{(Dgmcjt@@LDZcU_iDl z)d*qT9QXe{quHK?X3YyG6$YO9V1rXmUv?I(?ulBnx!U2$4JmJ z0z6#UH@U7qB#-$kV^C@fs5%ROFRdEuE7v_e9zkW_7iLVToW-?DnSv^|GmN8r9qr}! zNL~exBMqIzkAG>cwe+bAg_qcTz>b|26pu=g`v_|#doGQyX*NYNP*giblS%Dl zaRV)w`xXNRar3~(XKkGf5{ncfpb7yMoz)7eBa5=f#S_g1Wt5J{sD{%G7h4qlm0v$g zYvCa))jEhcr!cfR@)4aj-zBZth%JZc_yTDQ9nStUBZi<9#4jJN1NTgvwSJChx_C{} ziQGAVRhq33`GX|p%M6gvOe!y`^0wwZ zCNIFG^S@x)K5aX`)DdQCK93dt4JM&~z=Xd56AOz9U7!KEO(3X;vUG)2H`;8wG%Pdv zYrb8RV>bIrnhM+$dLklngbj$)PdOG;?RXWKt)LK+b?L@nIo({U*xmVbo}PEV!E`0& zauk80&1%I5jI@5#@YuJtEcMY)Wm|>K`P9a_bF6c&b@H6z+@#k%&htz|K)WclRL9?V5$s>Gq!rt}JdbrVKoZ zn;-t=n{!azo`V07wd{$7Ppv95l1rfqj$XWblz487031D9+tZ8=SR3BtNV=}0RQ@qUAQ}h>*@L&}Nj5H3SN%Wz8y`P|o zZyM8qCMx>uk1%2nv)>_z3c9Rjy337$A1(cwn2naxRAiGExr@r22^F}l7_WS6hxR~F zbQw%EeDL8Th;F+I-RqD6dxsj}cT{GC)La;cfQ2;Y^~>Hp!)&-xh=4ZnemBFu--e8L zV;{A1RGDt<)f#IpyEKfRWacaF`iMqj1J*|We5 zE-q?izHajA<#mFF3X5#M!KRyt^Rufe>t>KV?D_Yql*;)qp|FoS@xLq<*F+Nqu`=%ft=s;1UyN}v_{$MQ*>~6iq_GSLDv%#6 zmoP7?acji&y1w6QMDPluqxehB;8rHiyl}hca--6!~otM1= ztRCSoi9H!2)CGPX_l==VtK>30n$GB$6f8W)jx~}S=d@N1aArDAaBYG|}xt-%Q1t2qMtFW>Jqkvo`lt%)r2T{BYXA<7Oq`cw2! zU%5h$jK@F=cS&V-zH#;4s1wDO%5v(3mn;*z6(@Qh@ivE67g^la-DmR!3nk7k#>-Mw zS91&B!7-9QP|8bMKP3x$8W1P69L!P(qdLdF>E*@=cELIP+p@#38Yl>VH9ewR$w@X^)D&932rmp(RxkI7%z zVS`#<8n~{vb@;6TV(H%;+9NAuEh3CVaOU67rsH{ol}ja5vS0|m%FK)|^WO2&k}uS6 z_78Q&)y`0ZZEpR6=S0U!^Zxh{;|FR?lZd=MXbD4xK(US9AmytrjTu=ek%N)HHDv|v zQf`%2VTd3m)P@aS`#h49Rj{ys;;B^8xwyjSarft|jEau8oSMQJo6IiOV@l5wy=)Bs zuIK8jB*;N{AuWb_&|il%coQ@O-UrQ8KOr^?B)gOCxO=busj@H-r~d}d(6^|20@hyC zY_g-Y>_xrXg^j5v^ft{d9fuPQT*b<8pNGnR2*y%9T$>bce@~ z<~Oq$i3_heO40Pey!+E`7n{NPg1j-=wziHRif-KusGAMdppw|t8Z7>x(@#BClqS~^ z7T5AnI8Bmm){Hg;=bHHr-r=O3k7h}9A?qhqLqa-<2XSk>mjfiRh(>oZ(ozFeGHR|w z3MqwJ%0{0{kl8_*y3}SFZ$<^Sp!@3>`l-7LWzpiFkCHa$PL^BRT}4b+St(MT!%9VJ zBjgFCL`~0cdsNVutBQM4O|K2+f+boe^)9i?vqi2kw8_?ZiHE>Aciz`jH?Y3ldg92< zywsddej4v@{1z;4{a3W_d3Pk~otNisOI|gb4K?W_wgX;Ph5Gg3d-JKhATNvAnV`xx z@XpsuS5t)z<-x16I>>mK)~ZNi;gQp5{TcrDU5ll~xB>beg0l1EkJ6D>zbkF3p^&%u ztP8rihkvfFh8tF)4u>;DE?1zyOQ=f{MQjoNHzSpg&`@380n}e%3mw>?NQvkl3%JSL zA_b1&Nygye+^><23v!|EV&v&=v+#^X)ypCh&Q=dIdN|tfGbG3;?THupxrdLYiV#6@ zwqKMk$8yuHiv2j4reQBpX${|Y@c$z6Xz?|Bd%OkD40C2Dg`%lQvxQ+h{39r6jJmn~ zZ*p9(qh<-LKUT;9u>(Qkfd8*qgfG)D1QPK-HMqI|G$jB4=*6%7kzH~C053uTy<(vK zpLYJ6eE<2x9Up>(;kh(9LE*Icdz$>vzuCwIPw$`t0GR&hT|D)H@!lvH^zQ=mLZSIW z;Wv^0BXW#*DU<*{OOunng83hY@8W2OarD76YO&(SQJf%QHvCtVF!Vo5i9Z$q09WWm zVIcthoi&y(=1nh}FYNz*4`|}WpP)tYX=qN+As@a54ZnyxPWlspf7bK$3v_q%#RE|+ z7h%W=0>Sa#Xc+YGAiOXIB>C?>pGo1{wFIF52a}7nF1G$Jsg%YmYG1;$Yk~1gTAZM~ zYxol_A^fod81XM9z|qC%qM2df1j~NV=$-vabIg{SOPA BL013( From 6a3d7d403baee93cc5451949d51468e78959c7ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Zoric=CC=8Ca=CC=81k?= <1ondrej.zoricak@gmail.com> Date: Wed, 28 Nov 2018 19:31:52 +0100 Subject: [PATCH 12/13] add tests for footers --- tests/test_section.py | 68 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/tests/test_section.py b/tests/test_section.py index e4b7f40c0..3551708fc 100644 --- a/tests/test_section.py +++ b/tests/test_section.py @@ -17,7 +17,7 @@ ) from docx.opc.packuri import PackURI from docx.opc.constants import RELATIONSHIP_TYPE as RT -from docx.parts.header_footer import HeaderFooterPart +from docx.parts.header_footer import HeaderPart, FooterPart from .unitutil.cxml import element, xml @@ -120,6 +120,21 @@ def it_can_change_its_page_margins(self, margins_set_fixture): setattr(section, margin_prop_name, new_value) assert section._sectPr.xml == expected_xml + def it_knows_its_default_footer(self, section_with_default_footer_fixture): + assert section_with_default_footer_fixture.footer.is_linked_to_previous is False + assert section_with_default_footer_fixture.first_page_footer.is_linked_to_previous is True + assert section_with_default_footer_fixture.even_odd_footer.is_linked_to_previous is True + + def it_knows_its_first_footer(self, section_with_first_footer_fixture): + assert section_with_first_footer_fixture.footer.is_linked_to_previous is True + assert section_with_first_footer_fixture.first_page_footer.is_linked_to_previous is False + assert section_with_first_footer_fixture.even_odd_footer.is_linked_to_previous is True + + def it_knows_its_even_footer(self, section_with_even_footer_fixture): + assert section_with_even_footer_fixture.footer.is_linked_to_previous is True + assert section_with_even_footer_fixture.first_page_footer.is_linked_to_previous is True + assert section_with_even_footer_fixture.even_odd_footer.is_linked_to_previous is False + def it_knows_its_default_header(self, section_with_default_header_fixture): assert section_with_default_header_fixture.header.is_linked_to_previous is False assert section_with_default_header_fixture.first_page_header.is_linked_to_previous is True @@ -289,6 +304,17 @@ def start_type_set_fixture(self, request, document_part_): expected_xml = xml(expected_cxml) return section, new_start_type, expected_xml + @pytest.fixture(params=[ + 'w:sectPr/w:footerReference{w:type=default,r:id=rId1}/r:id' + ]) + def section_with_default_footer_fixture(self, request, document_part_, + footer_rel_): + footer_reltype, footer_part, rId = footer_rel_ + sectPr_cxml = request.param + document_part_.load_rel(footer_reltype, footer_part, rId) + section = Section(element(sectPr_cxml), document_part_) + return section + @pytest.fixture(params=[ 'w:sectPr/w:headerReference{w:type=default,r:id=rId1}/r:id' ]) @@ -300,6 +326,17 @@ def section_with_default_header_fixture(self, request, document_part_, section = Section(element(sectPr_cxml), document_part_) return section + @pytest.fixture(params=[ + 'w:sectPr/w:footerReference{w:type=first,r:id=rId1}/r:id' + ]) + def section_with_first_footer_fixture(self, request, document_part_, + footer_rel_): + footer_reltype, footer_part, rId = footer_rel_ + sectPr_cxml = request.param + document_part_.load_rel(footer_reltype, footer_part, rId) + section = Section(element(sectPr_cxml), document_part_) + return section + @pytest.fixture(params=[ 'w:sectPr/w:headerReference{w:type=first,r:id=rId1}/r:id' ]) @@ -311,6 +348,17 @@ def section_with_first_header_fixture(self, request, document_part_, section = Section(element(sectPr_cxml), document_part_) return section + @pytest.fixture(params=[ + 'w:sectPr/w:footerReference{w:type=even,r:id=rId1}/r:id' + ]) + def section_with_even_footer_fixture(self, request, document_part_, + footer_rel_): + footer_reltype, footer_part, rId = footer_rel_ + sectPr_cxml = request.param + document_part_.load_rel(footer_reltype, footer_part, rId) + section = Section(element(sectPr_cxml), document_part_) + return section + @pytest.fixture(params=[ 'w:sectPr/w:headerReference{w:type=even,r:id=rId1}/r:id' ]) @@ -349,17 +397,33 @@ def section_can_change_different_first_page_header_footer_fixture(self, request, def header_rel_(self, header_rId_, header_reltype_, header_part_): return header_reltype_, header_part_, header_rId_ + @pytest.fixture + def footer_rel_(self, footer_rId_, footer_reltype_, footer_part_): + return footer_reltype_, footer_part_, footer_rId_ + @pytest.fixture def header_rId_(self): return 'rId1' + @pytest.fixture + def footer_rId_(self): + return 'rId2' + @pytest.fixture def document_part_(self, document_partname_): return DocumentPart(document_partname_, None, None, None) @pytest.fixture def header_part_(self): - return HeaderFooterPart(None, None, None, None) + return HeaderPart(None, None, None, None) + + @pytest.fixture + def footer_part_(self): + return FooterPart(None, None, None, None) + + @pytest.fixture + def footer_reltype_(self): + return RT.FOOTER @pytest.fixture def header_reltype_(self): From e519a07e531a57fced2f0f6c0f556d7dae097b16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Zoric=CC=8Ca=CC=81k?= <1ondrej.zoricak@gmail.com> Date: Fri, 30 Nov 2018 10:41:06 +0100 Subject: [PATCH 13/13] add feature to header, footer like is_linked_to_previous --- docx/__init__.py | 8 +- docx/header_footer.py | 143 +++++++++++++++++------ docx/opc/part.py | 8 ++ docx/opc/rel.py | 15 +++ docx/oxml/section.py | 44 +++++++ docx/parts/document.py | 37 ++++++ docx/parts/header_footer.py | 49 +++++++- docx/section.py | 12 +- tests/test_header_footer.py | 223 ++++++++++++++++++++++++++++++++++++ tests/test_section.py | 72 +----------- 10 files changed, 497 insertions(+), 114 deletions(-) create mode 100644 tests/test_header_footer.py diff --git a/docx/__init__.py b/docx/__init__.py index dd978714e..926c05934 100644 --- a/docx/__init__.py +++ b/docx/__init__.py @@ -16,7 +16,7 @@ from docx.parts.numbering import NumberingPart from docx.parts.settings import SettingsPart from docx.parts.styles import StylesPart -from docx.parts.header_footer import HeaderFooterPart +from docx.parts.header_footer import HeaderPart, FooterPart def part_class_selector(content_type, reltype): @@ -31,10 +31,10 @@ def part_class_selector(content_type, reltype): PartFactory.part_type_for[CT.WML_NUMBERING] = NumberingPart PartFactory.part_type_for[CT.WML_SETTINGS] = SettingsPart PartFactory.part_type_for[CT.WML_STYLES] = StylesPart -PartFactory.part_type_for[CT.WML_HEADER] = HeaderFooterPart -PartFactory.part_type_for[CT.WML_FOOTER] = HeaderFooterPart +PartFactory.part_type_for[CT.WML_HEADER] = HeaderPart +PartFactory.part_type_for[CT.WML_FOOTER] = FooterPart del ( CT, CorePropertiesPart, DocumentPart, NumberingPart, PartFactory, - StylesPart, part_class_selector, HeaderFooterPart + StylesPart, part_class_selector ) diff --git a/docx/header_footer.py b/docx/header_footer.py index e7f09e9f3..c70299b89 100644 --- a/docx/header_footer.py +++ b/docx/header_footer.py @@ -9,62 +9,137 @@ ) from .blkcntnr import BlockItemContainer -from .shared import Parented, lazyproperty +from .shared import ElementProxy, Emu -class _HeaderFooter(Parented): +class _HeaderFooter(ElementProxy): - __slots__ = '_type' + __slots__ = ('_type', '__body') - def __init__(self, parent, type): - super(_HeaderFooter, self).__init__(parent) - self._type = type + def __init__(self, element, parent, hf_type): + super(_HeaderFooter, self).__init__(element, parent) + self._type = hf_type + self.__body = None - @lazyproperty - def body(self): + def add_paragraph(self, text='', style=None): """ + Return a paragraph, populated + with *text* and having paragraph style *style*. *text* can contain + tab (``\\t``) characters, which are converted to the appropriate XML + form for a tab. *text* can also include newline (``\\n``) or carriage + return (``\\r``) characters, each of which is converted to a line + break. + """ + return self._body.add_paragraph(text, style) - :return: body of this part + @property + def paragraphs(self): + """ + A list of |Paragraph| instances corresponding to the paragraphs in + this object. Note that paragraphs within revision + marks such as ```` or ```` do not appear in this list. """ - rel = self._parent.sectPr.get_header_reference_of_type(self._type) - if rel is None: - return None - part = self.part.get_related_part(rel.rId) - return _HeaderFooterBody(part.element, self) + return self._body.paragraphs + + def add_table(self, rows, cols, style=None): + """ + Add a table having row and column counts of *rows* and *cols* + respectively and table style of *style*. *style* may be a paragraph + style object or a paragraph style name. If *style* is |None|, the + table inherits the default table style of the document. + """ + table = self._body.add_table(rows, cols, self._block_width) + table.style = style + return table @property - def is_linked_to_previous(self): + def tables(self): + """ + A list of |Table| instances corresponding to the tables in + this object. Note that only tables appearing at the + top level of the document appear in this list; a table nested inside + a table cell does not appear. A table within revision marks such as + ```` or ```` will also not appear in the list. """ - :return: True when is linked otherwise false + return self._body.tables + + @property + def _block_width(self): """ - return True if self._parent.sectPr.get_header_reference_of_type(self._type) is None else False + Return a |Length| object specifying the width of available "writing" + space between the margins of the last section of this document. + """ + section = self._parent + return Emu( + section.page_width - section.left_margin - section.right_margin + ) + + @property + def _body(self): + if self.__body is None: + ref = self._parent.sectPr.get_header_reference_of_type(self._type) + if ref is None: + return None + part = self.part.get_related_part(ref.rId) + if part is not None: + self.__body = _HeaderFooterBody(part._element, self) + + return self.__body class Header(_HeaderFooter): - def __init__(self, parent, type): - super(Header, self).__init__(parent, type) + @property + def is_linked_to_previous(self): + """ + :return: True when is linked to previous header otherwise false + """ + if self._parent is None: + return True + return True if self._parent.sectPr.get_header_reference_of_type(self._type) is None else False -class Footer(_HeaderFooter): + @is_linked_to_previous.setter + def is_linked_to_previous(self, value): + """ + - if previous value is True and value is False then need to create new header + - if previous value is False and value is True then need to remove reference + """ + if self.is_linked_to_previous is True and value is False: + rId = self.part.add_header_part() + self._parent.sectPr.add_header_reference_of_type(rId, self._type) + self.add_paragraph('') + elif self.is_linked_to_previous is False and value is True: + self._parent.sectPr.remove_header_reference(self._type) + self.__body = None - def __init__(self, parent, type): - super(Footer, self).__init__(parent, type) +class Footer(_HeaderFooter): -class _HeaderFooterBody(BlockItemContainer): - """ - Proxy for ````, ```` element in this header and footer, having primarily a - container role. - """ - def __init__(self, elm, parent): - super(_HeaderFooterBody, self).__init__(elm, parent) + @property + def is_linked_to_previous(self): + """ + + :return: True when is linked otherwise false + """ + if self._parent is None: + return True + return True if self._parent.sectPr.get_footer_reference_of_type(self._type) is None else False - def clear_content(self): + @is_linked_to_previous.setter + def is_linked_to_previous(self, value): """ - Return this |_HeaderFooterBody| instance after clearing it of all content. - Section properties for the main document story, if present, are - preserved. + - if previous value is True and value is False then need to create new footer + - if previous value is False and value is True then need to remove reference """ - return self._element.clear_content() + if self.is_linked_to_previous is True and value is False: + rId = self.part.add_footer_part(self._type) + self._parent.sectPr.add_footer_reference_of_type(rId, self._type) + elif self.is_linked_to_previous is False and value is True: + self._parent.sectPr.remove_footer_reference(self._type) + self.__body = None + + +class _HeaderFooterBody(BlockItemContainer): + pass diff --git a/docx/opc/part.py b/docx/opc/part.py index 928d3c183..b7c9acbc3 100644 --- a/docx/opc/part.py +++ b/docx/opc/part.py @@ -120,6 +120,14 @@ def part_related_by(self, reltype): """ return self.rels.part_with_reltype(reltype) + def parts_by_type(self, reltype): + """ + Return parts to which this part has a relationship of *reltype*. + Raises |KeyError| if no such relationship is found and |ValueError| + Provides ability to resolve implicitly related part, such as Slide -> SlideLayout. + """ + return self.rels.parts_with_reltype(reltype) + def relate_to(self, target, reltype, is_external=False): """ Return rId key of relationship of *reltype* to *target*, from an diff --git a/docx/opc/rel.py b/docx/opc/rel.py index 7dba2af8e..de7b5ae62 100644 --- a/docx/opc/rel.py +++ b/docx/opc/rel.py @@ -63,6 +63,14 @@ def part_with_reltype(self, reltype): rel = self._get_rel_of_type(reltype) return rel.target_part + def parts_with_reltype(self, reltype): + """ + Return target parts of rel with matching *reltype*, raising |KeyError| + if not found + """ + rels = self._get_rels_of_type(reltype) + return [rel.target_part for rel in rels] + @property def related_parts(self): """ @@ -119,6 +127,13 @@ def _get_rel_of_type(self, reltype): raise ValueError(tmpl % reltype) return matching[0] + def _get_rels_of_type(self, reltype): + matching = [rel for rel in self.values() if rel.reltype == reltype] + if len(matching) == 0: + tmpl = "no relationship of type '%s' in collection" + raise KeyError(tmpl % reltype) + return matching + @property def _next_rId(self): """ diff --git a/docx/oxml/section.py b/docx/oxml/section.py index 9358cf525..cd2510aae 100644 --- a/docx/oxml/section.py +++ b/docx/oxml/section.py @@ -325,6 +325,50 @@ def get_header_reference_of_type(self, type): return ref return None + def add_header_reference_of_type(self, rId, type): + """ + Create new header reference with rId and type + :param type: of header reference + :param rId: relation id to related header file + :return: + """ + headerRef = self._add_headerReference() + headerRef.rId = rId + headerRef.type = type + + def remove_header_reference(self, type): + """ + Remove header reference from this section by type + :param type: of header reference + :param rId: relation id to related header file + :return: + """ + ref = self.get_header_reference_of_type(type) + if ref is not None: + self.remove(ref) + + def add_footer_reference_of_type(self, rId, type): + """ + Create new footer reference with rId and type + :param type: of footer reference + :param rId: relation id to related footer file + :return: + """ + footerRef = self._add_footerReference() + footerRef.rId = rId + footerRef.type = type + + def remove_footer_reference(self, type): + """ + Remove footer reference from this section by type + :param type: of footer reference + :param rId: relation id to related footer file + :return: + """ + ref = self.get_footer_reference_of_type(type) + if ref is not None: + self.remove(ref) + class CT_SectType(BaseOxmlElement): """ diff --git a/docx/parts/document.py b/docx/parts/document.py index 9dbaff70e..b58a2716f 100644 --- a/docx/parts/document.py +++ b/docx/parts/document.py @@ -17,6 +17,7 @@ from ..shared import lazyproperty from .settings import SettingsPart from .styles import StylesPart +from .header_footer import HeaderPart, FooterPart class DocumentPart(XmlPart): @@ -28,6 +29,16 @@ class DocumentPart(XmlPart): inherited by many content objects provides access to this part object for that purpose. """ + + def add_relationship(self, part, relationship_type): + """ + Add relation to this document part + :param part: + :param relationship_type: + :return: rId + """ + return self.relate_to(part, relationship_type) + @property def core_properties(self): """ @@ -179,3 +190,29 @@ def _styles_part(self): styles_part = StylesPart.default(self.package) self.relate_to(styles_part, RT.STYLES) return styles_part + + def add_header_part(self): + """ + Creates an empty header part. + """ + headers_parts = self.parts_by_type(RT.HEADER) + last_header_number = 0 + for part in headers_parts: + header_num = int(part.partname.split('.')[-2][len('/word/header'):]) + if header_num > last_header_number: + last_header_number = header_num + return self.add_relationship(HeaderPart.new(headers_parts[-1].part.blob, self.package, last_header_number + 1), + RT.HEADER) + + def add_footer_part(self): + """ + Creates an empty footer part. + """ + footers_parts = self.parts_by_type(RT.FOOTER) + last_footer_number = 0 + for part in footers_parts: + footer_num = int(part.partname.split('.')[-2][len('/word/footer'):]) + if footer_num > last_footer_number: + last_footer_number = footer_num + return self.add_relationship(FooterPart.new(footers_parts[-1].part.blob, self.package, last_footer_number + 1), + RT.FOOTER) diff --git a/docx/parts/header_footer.py b/docx/parts/header_footer.py index b0c8148bb..7f28763f4 100644 --- a/docx/parts/header_footer.py +++ b/docx/parts/header_footer.py @@ -8,10 +8,42 @@ absolute_import, division, print_function, unicode_literals ) +from ..opc.constants import CONTENT_TYPE as CT +from ..opc.packuri import PackURI +from ..oxml import parse_xml + from ..opc.part import XmlPart -class HeaderFooterPart(XmlPart): +class _HeaderFooterPart(XmlPart): + pass + + +class HeaderPart(_HeaderFooterPart): + """ + Main header and footer part of a WordprocessingML (WML) package, aka a .docx file. + Acts as broker to other parts such as image, core properties, and style + parts. It also acts as a convenient delegate when a mid-document object + needs a service involving a remote ancestor. The `Parented.part` property + inherited by many content objects provides access to this part object for + that purpose. + """ + + @classmethod + def new(cls, blob, package, header_number): + """ + Return a newly created header part, containing a default + `w:hdr` element tree. + """ + partname = PackURI('/word/header%s.xml' % str(header_number)) + content_type = CT.WML_HEADER + element = parse_xml(blob) + header_part = cls(partname, content_type, element, package) + header_part.element.clear_content() + return header_part + + +class FooterPart(_HeaderFooterPart): """ Main header and footer part of a WordprocessingML (WML) package, aka a .docx file. Acts as broker to other parts such as image, core properties, and style @@ -19,4 +51,17 @@ class HeaderFooterPart(XmlPart): needs a service involving a remote ancestor. The `Parented.part` property inherited by many content objects provides access to this part object for that purpose. - """ \ No newline at end of file + """ + + @classmethod + def new(cls, blob, package, footer_number): + """ + Return a newly created footer part, containing a default + `w:ftr` element tree. + """ + partname = PackURI('/word/footer%s.xml' % str(footer_number)) + content_type = CT.WML_FOOTER + element = parse_xml(blob) + footer_part = cls(partname, content_type, element, package) + footer_part.element.clear_content() + return footer_part diff --git a/docx/section.py b/docx/section.py index 493a8911f..cc545ff70 100644 --- a/docx/section.py +++ b/docx/section.py @@ -67,23 +67,23 @@ def different_first_page_header_footer(self, value): @property def even_odd_header(self): - return Header(self, WD_HEADER_FOOTER.EVEN_PAGE) + return Header(self._sectPr, self, WD_HEADER_FOOTER.EVEN_PAGE) @property def even_odd_footer(self): - return Footer(self, WD_HEADER_FOOTER.EVEN_PAGE) + return Footer(self._sectPr, self, WD_HEADER_FOOTER.EVEN_PAGE) @property def first_page_footer(self): - return Footer(self, WD_HEADER_FOOTER.FIRST_PAGE) + return Footer(self._sectPr, self, WD_HEADER_FOOTER.FIRST_PAGE) @property def first_page_header(self): - return Header(self, WD_HEADER_FOOTER.FIRST_PAGE) + return Header(self._sectPr, self, WD_HEADER_FOOTER.FIRST_PAGE) @property def footer(self): - return Footer(self, WD_HEADER_FOOTER.PRIMARY) + return Footer(self._sectPr, self, WD_HEADER_FOOTER.PRIMARY) @property def footer_distance(self): @@ -114,7 +114,7 @@ def gutter(self, value): @property def header(self): - return Header(self, WD_HEADER_FOOTER.PRIMARY) + return Header(self._sectPr, self, WD_HEADER_FOOTER.PRIMARY) @property def header_distance(self): diff --git a/tests/test_header_footer.py b/tests/test_header_footer.py new file mode 100644 index 000000000..d552dd0bc --- /dev/null +++ b/tests/test_header_footer.py @@ -0,0 +1,223 @@ +# encoding: utf-8 + +""" +Test suite for the docx.settings module +""" + +from __future__ import absolute_import, print_function, unicode_literals + +import pytest +from .unitutil.mock import ( + instance_mock, method_mock +) + +from docx.section import Section + +from docx.opc.packuri import PackURI +from docx.opc.constants import RELATIONSHIP_TYPE as RT +from docx.parts.document import DocumentPart +from .unitutil.cxml import element, xml +from docx.parts.header_footer import HeaderPart, FooterPart + + +class DescribeSettings(object): + + def it_knows_its_default_header(self, section_with_default_header_fixture): + assert section_with_default_header_fixture.header.is_linked_to_previous is False + assert section_with_default_header_fixture.first_page_header.is_linked_to_previous is True + assert section_with_default_header_fixture.even_odd_header.is_linked_to_previous is True + + def it_knows_its_first_header(self, section_with_first_header_fixture): + assert section_with_first_header_fixture.header.is_linked_to_previous is True + assert section_with_first_header_fixture.first_page_header.is_linked_to_previous is False + assert section_with_first_header_fixture.even_odd_header.is_linked_to_previous is True + + def it_knows_its_even_header(self, section_with_even_header_fixture): + assert section_with_even_header_fixture.header.is_linked_to_previous is True + assert section_with_even_header_fixture.first_page_header.is_linked_to_previous is True + assert section_with_even_header_fixture.even_odd_header.is_linked_to_previous is False + + def it_can_change_header_set_is_linked(self, section_with_default_header_can_change_is_linked_fixture): + section, value, expected_xml = section_with_default_header_can_change_is_linked_fixture + section.header.is_linked_to_previous = value + assert section._sectPr.xml == expected_xml + + def it_knows_its_default_footer(self, section_with_default_footer_fixture): + assert section_with_default_footer_fixture.footer.is_linked_to_previous is False + assert section_with_default_footer_fixture.first_page_footer.is_linked_to_previous is True + assert section_with_default_footer_fixture.even_odd_footer.is_linked_to_previous is True + + def it_knows_its_first_footer(self, section_with_first_footer_fixture): + assert section_with_first_footer_fixture.footer.is_linked_to_previous is True + assert section_with_first_footer_fixture.first_page_footer.is_linked_to_previous is False + assert section_with_first_footer_fixture.even_odd_footer.is_linked_to_previous is True + + def it_knows_its_even_footer(self, section_with_even_footer_fixture): + assert section_with_even_footer_fixture.footer.is_linked_to_previous is True + assert section_with_even_footer_fixture.first_page_footer.is_linked_to_previous is True + assert section_with_even_footer_fixture.even_odd_footer.is_linked_to_previous is False + + def it_can_change__footer_set_is_linked(self, section_with_default_footer_can_change_is_linked_fixture): + section, value, expected_xml = section_with_default_footer_can_change_is_linked_fixture + section.footer.is_linked_to_previous = value + assert section._sectPr.xml == expected_xml + + # fixtures ------------------------------------------------------- + + @pytest.fixture(params=[ + ('w:sectPr/(w:footerReference{w:type=default,r:id=rId1}, r:id)', True, + 'w:sectPr/r:id'), + ('w:sectPr/r:id', False, + 'w:sectPr/(r:id, w:footerReference{w:type=default,r:id=rId1})') + ]) + def section_with_default_footer_can_change_is_linked_fixture(self, request, document_part_, + footer_rel_): + footer_reltype, footer_part, rId = footer_rel_ + sectPr_cxml, value, expected_xml = request.param + document_part_.load_rel(footer_reltype, footer_part, rId) + section = Section(element(sectPr_cxml), document_part_) + return section, value, xml(expected_xml) + + @pytest.fixture(params=[ + 'w:sectPr/w:footerReference{w:type=default,r:id=rId1}/r:id' + ]) + def section_with_default_footer_fixture(self, request, document_part_, + footer_rel_): + footer_reltype, footer_part, rId = footer_rel_ + sectPr_cxml = request.param + document_part_.load_rel(footer_reltype, footer_part, rId) + section = Section(element(sectPr_cxml), document_part_) + return section + + @pytest.fixture(params=[ + 'w:sectPr/w:footerReference{w:type=first,r:id=rId1}/r:id' + ]) + def section_with_first_footer_fixture(self, request, document_part_, + footer_rel_): + footer_reltype, footer_part, rId = footer_rel_ + sectPr_cxml = request.param + document_part_.load_rel(footer_reltype, footer_part, rId) + section = Section(element(sectPr_cxml), document_part_) + return section + + @pytest.fixture(params=[ + 'w:sectPr/w:footerReference{w:type=even,r:id=rId1}/r:id' + ]) + def section_with_even_footer_fixture(self, request, document_part_, + footer_rel_): + footer_reltype, footer_part, rId = footer_rel_ + sectPr_cxml = request.param + document_part_.load_rel(footer_reltype, footer_part, rId) + section = Section(element(sectPr_cxml), document_part_) + return section + + @pytest.fixture(params=[ + ('w:sectPr/(w:headerReference{w:type=default,r:id=rId1}, r:id)', True, + 'w:sectPr/r:id'), + ('w:sectPr/r:id', False, + 'w:sectPr/(r:id, w:headerReference{w:type=default,r:id=rId1})') + ]) + def section_with_default_header_can_change_is_linked_fixture(self, request, document_part_, + header_rel_): + header_reltype, header_part, rId = header_rel_ + sectPr_cxml, value, expected_xml = request.param + document_part_.load_rel(header_reltype, header_part, rId) + section = Section(element(sectPr_cxml), document_part_) + return section, value, xml(expected_xml) + + @pytest.fixture(params=[ + 'w:sectPr/w:headerReference{w:type=default,r:id=rId1}/r:id' + ]) + def section_with_default_header_fixture(self, request, document_part_, + header_rel_): + header_reltype, header_part, rId = header_rel_ + sectPr_cxml = request.param + document_part_.load_rel(header_reltype, header_part, rId) + section = Section(element(sectPr_cxml), document_part_) + return section + + @pytest.fixture(params=[ + 'w:sectPr/w:headerReference{w:type=first,r:id=rId1}/r:id' + ]) + def section_with_first_header_fixture(self, request, document_part_, + header_rel_): + header_reltype, header_part, rId = header_rel_ + sectPr_cxml = request.param + document_part_.load_rel(header_reltype, header_part, rId) + section = Section(element(sectPr_cxml), document_part_) + return section + + + @pytest.fixture(params=[ + 'w:sectPr/w:headerReference{w:type=even,r:id=rId1}/r:id' + ]) + def section_with_even_header_fixture(self, request, document_part_, + header_rel_): + header_reltype, header_part, rId = header_rel_ + sectPr_cxml = request.param + document_part_.load_rel(header_reltype, header_part, rId) + section = Section(element(sectPr_cxml), document_part_) + return section + + @pytest.fixture + def header_rel_(self, header_rId_, header_reltype_, header_part_): + return header_reltype_, header_part_, header_rId_ + + + @pytest.fixture + def footer_rel_(self, footer_rId_, footer_reltype_, footer_part_): + return footer_reltype_, footer_part_, footer_rId_ + + @pytest.fixture + def footer_rId_(self): + return 'rId1' + + @pytest.fixture + def header_rId_(self): + return 'rId1' + + @pytest.fixture + def document_part_(self, request, document_partname_, _add_header_part_, _add_footer_part_): + document_part = instance_mock(request, DocumentPart) + document_part.add_header_part = _add_header_part_ + document_part.add_footer_part = _add_footer_part_ + return document_part + + @pytest.fixture + def _add_header_part_(self, request, header_rId_): + return method_mock( + request, DocumentPart, 'add_header_part', + return_value=header_rId_ + ) + + @pytest.fixture + def _add_footer_part_(self, request, footer_rId_): + return method_mock( + request, DocumentPart, 'add_footer_part', + return_value=footer_rId_ + ) + + + @pytest.fixture + def header_part_(self, request): + return instance_mock(request, HeaderPart) + + @pytest.fixture + def footer_part_(self, request): + return instance_mock(request, FooterPart) + + @pytest.fixture + def header_reltype_(self): + return RT.HEADER + + @pytest.fixture + def footer_reltype_(self): + return RT.FOOTER + + @pytest.fixture + def document_partname_(self, _baseURI): + return PackURI(_baseURI) + + @pytest.fixture + def _baseURI(self): + return '/baseURI' diff --git a/tests/test_section.py b/tests/test_section.py index 3551708fc..170f428db 100644 --- a/tests/test_section.py +++ b/tests/test_section.py @@ -135,21 +135,6 @@ def it_knows_its_even_footer(self, section_with_even_footer_fixture): assert section_with_even_footer_fixture.first_page_footer.is_linked_to_previous is True assert section_with_even_footer_fixture.even_odd_footer.is_linked_to_previous is False - def it_knows_its_default_header(self, section_with_default_header_fixture): - assert section_with_default_header_fixture.header.is_linked_to_previous is False - assert section_with_default_header_fixture.first_page_header.is_linked_to_previous is True - assert section_with_default_header_fixture.even_odd_header.is_linked_to_previous is True - - def it_knows_its_first_header(self, section_with_first_header_fixture): - assert section_with_first_header_fixture.header.is_linked_to_previous is True - assert section_with_first_header_fixture.first_page_header.is_linked_to_previous is False - assert section_with_first_header_fixture.even_odd_header.is_linked_to_previous is True - - def it_knows_its_even_header(self, section_with_even_header_fixture): - assert section_with_even_header_fixture.header.is_linked_to_previous is True - assert section_with_even_header_fixture.first_page_header.is_linked_to_previous is True - assert section_with_even_header_fixture.even_odd_header.is_linked_to_previous is False - def it_have_different_first_page_header_footer(self, section_with_different_first_page_header_footer_fixture): assert section_with_different_first_page_header_footer_fixture.different_first_page_header_footer is True @@ -315,17 +300,6 @@ def section_with_default_footer_fixture(self, request, document_part_, section = Section(element(sectPr_cxml), document_part_) return section - @pytest.fixture(params=[ - 'w:sectPr/w:headerReference{w:type=default,r:id=rId1}/r:id' - ]) - def section_with_default_header_fixture(self, request, document_part_, - header_rel_): - header_reltype, header_part, rId = header_rel_ - sectPr_cxml = request.param - document_part_.load_rel(header_reltype, header_part, rId) - section = Section(element(sectPr_cxml), document_part_) - return section - @pytest.fixture(params=[ 'w:sectPr/w:footerReference{w:type=first,r:id=rId1}/r:id' ]) @@ -337,17 +311,6 @@ def section_with_first_footer_fixture(self, request, document_part_, section = Section(element(sectPr_cxml), document_part_) return section - @pytest.fixture(params=[ - 'w:sectPr/w:headerReference{w:type=first,r:id=rId1}/r:id' - ]) - def section_with_first_header_fixture(self, request, document_part_, - header_rel_): - header_reltype, header_part, rId = header_rel_ - sectPr_cxml = request.param - document_part_.load_rel(header_reltype, header_part, rId) - section = Section(element(sectPr_cxml), document_part_) - return section - @pytest.fixture(params=[ 'w:sectPr/w:footerReference{w:type=even,r:id=rId1}/r:id' ]) @@ -359,17 +322,6 @@ def section_with_even_footer_fixture(self, request, document_part_, section = Section(element(sectPr_cxml), document_part_) return section - @pytest.fixture(params=[ - 'w:sectPr/w:headerReference{w:type=even,r:id=rId1}/r:id' - ]) - def section_with_even_header_fixture(self, request, document_part_, - header_rel_): - header_reltype, header_part, rId = header_rel_ - sectPr_cxml = request.param - document_part_.load_rel(header_reltype, header_part, rId) - section = Section(element(sectPr_cxml), document_part_) - return section - @pytest.fixture(params=[ 'w:sectPr/w:titlePg' ]) @@ -393,42 +345,26 @@ def section_can_change_different_first_page_header_footer_fixture(self, request, return Section(element(sectPr_cxml), document_part_), xml(expected_cxml) - @pytest.fixture - def header_rel_(self, header_rId_, header_reltype_, header_part_): - return header_reltype_, header_part_, header_rId_ - @pytest.fixture def footer_rel_(self, footer_rId_, footer_reltype_, footer_part_): return footer_reltype_, footer_part_, footer_rId_ - @pytest.fixture - def header_rId_(self): - return 'rId1' - @pytest.fixture def footer_rId_(self): return 'rId2' @pytest.fixture - def document_part_(self, document_partname_): - return DocumentPart(document_partname_, None, None, None) - - @pytest.fixture - def header_part_(self): - return HeaderPart(None, None, None, None) + def document_part_(self, request, document_partname_): + return instance_mock(request, DocumentPart) @pytest.fixture - def footer_part_(self): - return FooterPart(None, None, None, None) + def footer_part_(self, request): + return instance_mock(request, FooterPart) @pytest.fixture def footer_reltype_(self): return RT.FOOTER - @pytest.fixture - def header_reltype_(self): - return RT.HEADER - @pytest.fixture def footer_reltype_(self): return RT.FOOTER