ps-python-docx 1.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- docx/__init__.py +68 -0
- docx/api.py +37 -0
- docx/blkcntnr.py +101 -0
- docx/comments.py +163 -0
- docx/dml/__init__.py +0 -0
- docx/dml/color.py +112 -0
- docx/document.py +275 -0
- docx/drawing/__init__.py +59 -0
- docx/enum/__init__.py +0 -0
- docx/enum/base.py +150 -0
- docx/enum/dml.py +103 -0
- docx/enum/section.py +86 -0
- docx/enum/shape.py +19 -0
- docx/enum/style.py +452 -0
- docx/enum/table.py +136 -0
- docx/enum/text.py +367 -0
- docx/exceptions.py +18 -0
- docx/image/__init__.py +23 -0
- docx/image/bmp.py +43 -0
- docx/image/constants.py +172 -0
- docx/image/exceptions.py +13 -0
- docx/image/gif.py +38 -0
- docx/image/helpers.py +86 -0
- docx/image/image.py +234 -0
- docx/image/jpeg.py +425 -0
- docx/image/png.py +253 -0
- docx/image/tiff.py +289 -0
- docx/opc/__init__.py +0 -0
- docx/opc/constants.py +306 -0
- docx/opc/coreprops.py +142 -0
- docx/opc/exceptions.py +12 -0
- docx/opc/oxml.py +247 -0
- docx/opc/package.py +219 -0
- docx/opc/packuri.py +109 -0
- docx/opc/part.py +247 -0
- docx/opc/parts/__init__.py +0 -0
- docx/opc/parts/coreprops.py +48 -0
- docx/opc/phys_pkg.py +119 -0
- docx/opc/pkgreader.py +254 -0
- docx/opc/pkgwriter.py +115 -0
- docx/opc/rel.py +153 -0
- docx/opc/shared.py +31 -0
- docx/opc/spec.py +24 -0
- docx/oxml/__init__.py +261 -0
- docx/oxml/comments.py +124 -0
- docx/oxml/coreprops.py +298 -0
- docx/oxml/document.py +88 -0
- docx/oxml/drawing.py +11 -0
- docx/oxml/exceptions.py +10 -0
- docx/oxml/ns.py +109 -0
- docx/oxml/numbering.py +109 -0
- docx/oxml/parser.py +62 -0
- docx/oxml/section.py +537 -0
- docx/oxml/settings.py +138 -0
- docx/oxml/shape.py +299 -0
- docx/oxml/shared.py +52 -0
- docx/oxml/simpletypes.py +434 -0
- docx/oxml/styles.py +341 -0
- docx/oxml/table.py +977 -0
- docx/oxml/text/__init__.py +0 -0
- docx/oxml/text/font.py +333 -0
- docx/oxml/text/hyperlink.py +45 -0
- docx/oxml/text/pagebreak.py +278 -0
- docx/oxml/text/paragraph.py +106 -0
- docx/oxml/text/parfmt.py +392 -0
- docx/oxml/text/run.py +307 -0
- docx/oxml/xmlchemy.py +696 -0
- docx/package.py +110 -0
- docx/parts/__init__.py +0 -0
- docx/parts/comments.py +51 -0
- docx/parts/document.py +182 -0
- docx/parts/hdrftr.py +53 -0
- docx/parts/image.py +80 -0
- docx/parts/numbering.py +32 -0
- docx/parts/settings.py +50 -0
- docx/parts/story.py +95 -0
- docx/parts/styles.py +42 -0
- docx/parts/theme.py +53 -0
- docx/py.typed +0 -0
- docx/section.py +479 -0
- docx/settings.py +35 -0
- docx/shape.py +103 -0
- docx/shared.py +382 -0
- docx/styles/__init__.py +40 -0
- docx/styles/latent.py +198 -0
- docx/styles/style.py +264 -0
- docx/styles/styles.py +147 -0
- docx/table.py +537 -0
- docx/templates/default-comments.xml +12 -0
- docx/templates/default-docx-template/[Content_Types].xml +17 -0
- docx/templates/default-docx-template/_rels/.rels +7 -0
- docx/templates/default-docx-template/customXml/_rels/item1.xml.rels +4 -0
- docx/templates/default-docx-template/customXml/item1.xml +2 -0
- docx/templates/default-docx-template/customXml/itemProps1.xml +6 -0
- docx/templates/default-docx-template/docProps/app.xml +36 -0
- docx/templates/default-docx-template/docProps/core.xml +13 -0
- docx/templates/default-docx-template/docProps/thumbnail.jpeg +0 -0
- docx/templates/default-docx-template/word/_rels/document.xml.rels +11 -0
- docx/templates/default-docx-template/word/document.xml +11 -0
- docx/templates/default-docx-template/word/fontTable.xml +61 -0
- docx/templates/default-docx-template/word/numbering.xml +201 -0
- docx/templates/default-docx-template/word/settings.xml +53 -0
- docx/templates/default-docx-template/word/styles.xml +11844 -0
- docx/templates/default-docx-template/word/stylesWithEffects.xml +11800 -0
- docx/templates/default-docx-template/word/theme/theme1.xml +318 -0
- docx/templates/default-docx-template/word/webSettings.xml +5 -0
- docx/templates/default-footer.xml +27 -0
- docx/templates/default-header.xml +27 -0
- docx/templates/default-settings.xml +26 -0
- docx/templates/default-styles.xml +190 -0
- docx/templates/default.docx +0 -0
- docx/text/__init__.py +0 -0
- docx/text/font.py +472 -0
- docx/text/hyperlink.py +121 -0
- docx/text/pagebreak.py +104 -0
- docx/text/paragraph.py +173 -0
- docx/text/parfmt.py +286 -0
- docx/text/run.py +257 -0
- docx/text/tabstops.py +123 -0
- docx/theme.py +67 -0
- docx/types.py +34 -0
- ps_python_docx-1.3.0.dist-info/METADATA +77 -0
- ps_python_docx-1.3.0.dist-info/RECORD +126 -0
- ps_python_docx-1.3.0.dist-info/WHEEL +5 -0
- ps_python_docx-1.3.0.dist-info/licenses/LICENSE +20 -0
- ps_python_docx-1.3.0.dist-info/top_level.txt +1 -0
docx/parts/styles.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Provides StylesPart and related objects."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from docx.opc.constants import CONTENT_TYPE as CT
|
|
9
|
+
from docx.opc.packuri import PackURI
|
|
10
|
+
from docx.opc.part import XmlPart
|
|
11
|
+
from docx.oxml.parser import parse_xml
|
|
12
|
+
from docx.styles.styles import Styles
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from docx.opc.package import OpcPackage
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class StylesPart(XmlPart):
|
|
19
|
+
"""Proxy for the styles.xml part containing style definitions for a document or
|
|
20
|
+
glossary."""
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def default(cls, package: OpcPackage) -> StylesPart:
|
|
24
|
+
"""Return a newly created styles part, containing a default set of elements."""
|
|
25
|
+
partname = PackURI("/word/styles.xml")
|
|
26
|
+
content_type = CT.WML_STYLES
|
|
27
|
+
element = parse_xml(cls._default_styles_xml())
|
|
28
|
+
return cls(partname, content_type, element, package)
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def styles(self):
|
|
32
|
+
"""The |_Styles| instance containing the styles (<w:style> element proxies) for
|
|
33
|
+
this styles part."""
|
|
34
|
+
return Styles(self.element)
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def _default_styles_xml(cls):
|
|
38
|
+
"""Return a bytestream containing XML for a default styles part."""
|
|
39
|
+
path = os.path.join(os.path.split(__file__)[0], "..", "templates", "default-styles.xml")
|
|
40
|
+
with open(path, "rb") as f:
|
|
41
|
+
xml_bytes = f.read()
|
|
42
|
+
return xml_bytes
|
docx/parts/theme.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""DrawingML theme part."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
from zipfile import ZipFile
|
|
8
|
+
|
|
9
|
+
from docx.opc.constants import CONTENT_TYPE as CT
|
|
10
|
+
from docx.opc.oxml import serialize_part_xml
|
|
11
|
+
from docx.opc.part import Part
|
|
12
|
+
from docx.oxml.ns import qn
|
|
13
|
+
from docx.oxml.parser import parse_xml
|
|
14
|
+
from docx.shared import lazyproperty
|
|
15
|
+
from docx.theme import ThemeFonts
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from lxml.etree import _Element # pyright: ignore[reportPrivateUsage]
|
|
19
|
+
|
|
20
|
+
from docx.package import Package
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ThemePart(Part):
|
|
24
|
+
"""Theme XML, parsed on demand to preserve untouched theme parts verbatim."""
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def default(cls, package: Package) -> ThemePart:
|
|
28
|
+
"""Create a complete theme using the bundled default document."""
|
|
29
|
+
path = os.path.join(os.path.dirname(__file__), "..", "templates", "default.docx")
|
|
30
|
+
with ZipFile(path) as archive:
|
|
31
|
+
blob = archive.read("word/theme/theme1.xml")
|
|
32
|
+
return cls(package.next_partname("/word/theme/theme%d.xml"), CT.OFC_THEME, blob, package)
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def blob(self) -> bytes:
|
|
36
|
+
if "_theme" in self.__dict__:
|
|
37
|
+
return serialize_part_xml(self._theme)
|
|
38
|
+
return super().blob
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def fonts(self) -> ThemeFonts:
|
|
42
|
+
"""Font scheme proxy. Raise |ValueError| for an incomplete theme."""
|
|
43
|
+
scheme = self._theme.find(f"{qn('a:themeElements')}/{qn('a:fontScheme')}")
|
|
44
|
+
if scheme is None:
|
|
45
|
+
raise ValueError("theme is missing its font scheme")
|
|
46
|
+
return ThemeFonts(scheme)
|
|
47
|
+
|
|
48
|
+
@lazyproperty
|
|
49
|
+
def _theme(self) -> _Element:
|
|
50
|
+
try:
|
|
51
|
+
return parse_xml(super().blob)
|
|
52
|
+
except SyntaxError as exc:
|
|
53
|
+
raise ValueError("theme XML is malformed") from exc
|
docx/py.typed
ADDED
|
File without changes
|
docx/section.py
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
"""The |Section| object and related proxy classes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Iterator, List, Sequence, overload
|
|
6
|
+
|
|
7
|
+
from docx.blkcntnr import BlockItemContainer
|
|
8
|
+
from docx.enum.section import WD_HEADER_FOOTER
|
|
9
|
+
from docx.oxml.text.paragraph import CT_P
|
|
10
|
+
from docx.parts.hdrftr import FooterPart, HeaderPart
|
|
11
|
+
from docx.shared import lazyproperty
|
|
12
|
+
from docx.table import Table
|
|
13
|
+
from docx.text.paragraph import Paragraph
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from docx.enum.section import WD_ORIENTATION, WD_SECTION_START
|
|
17
|
+
from docx.oxml.document import CT_Document
|
|
18
|
+
from docx.oxml.section import CT_SectPr
|
|
19
|
+
from docx.parts.document import DocumentPart
|
|
20
|
+
from docx.parts.story import StoryPart
|
|
21
|
+
from docx.shared import Length
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Section:
|
|
25
|
+
"""Document section, providing access to section and page setup settings.
|
|
26
|
+
|
|
27
|
+
Also provides access to headers and footers.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, sectPr: CT_SectPr, document_part: DocumentPart):
|
|
31
|
+
super(Section, self).__init__()
|
|
32
|
+
self._sectPr = sectPr
|
|
33
|
+
self._document_part = document_part
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def bottom_margin(self) -> Length | None:
|
|
37
|
+
"""Read/write. Bottom margin for pages in this section, in EMU.
|
|
38
|
+
|
|
39
|
+
`None` when no bottom margin has been specified. Assigning |None| removes any
|
|
40
|
+
bottom-margin setting.
|
|
41
|
+
"""
|
|
42
|
+
return self._sectPr.bottom_margin
|
|
43
|
+
|
|
44
|
+
@bottom_margin.setter
|
|
45
|
+
def bottom_margin(self, value: int | Length | None):
|
|
46
|
+
self._sectPr.bottom_margin = value
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def different_first_page_header_footer(self) -> bool:
|
|
50
|
+
"""True if this section displays a distinct first-page header and footer.
|
|
51
|
+
|
|
52
|
+
Read/write. The definition of the first-page header and footer are accessed
|
|
53
|
+
using :attr:`.first_page_header` and :attr:`.first_page_footer` respectively.
|
|
54
|
+
"""
|
|
55
|
+
return self._sectPr.titlePg_val
|
|
56
|
+
|
|
57
|
+
@different_first_page_header_footer.setter
|
|
58
|
+
def different_first_page_header_footer(self, value: bool):
|
|
59
|
+
self._sectPr.titlePg_val = value
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def even_page_footer(self) -> _Footer:
|
|
63
|
+
"""|_Footer| object defining footer content for even pages.
|
|
64
|
+
|
|
65
|
+
The content of this footer definition is ignored unless the document setting
|
|
66
|
+
:attr:`~.Settings.odd_and_even_pages_header_footer` is set True.
|
|
67
|
+
"""
|
|
68
|
+
return _Footer(self._sectPr, self._document_part, WD_HEADER_FOOTER.EVEN_PAGE)
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def even_page_header(self) -> _Header:
|
|
72
|
+
"""|_Header| object defining header content for even pages.
|
|
73
|
+
|
|
74
|
+
The content of this header definition is ignored unless the document setting
|
|
75
|
+
:attr:`~.Settings.odd_and_even_pages_header_footer` is set True.
|
|
76
|
+
"""
|
|
77
|
+
return _Header(self._sectPr, self._document_part, WD_HEADER_FOOTER.EVEN_PAGE)
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def first_page_footer(self) -> _Footer:
|
|
81
|
+
"""|_Footer| object defining footer content for the first page of this section.
|
|
82
|
+
|
|
83
|
+
The content of this footer definition is ignored unless the property
|
|
84
|
+
:attr:`.different_first_page_header_footer` is set True.
|
|
85
|
+
"""
|
|
86
|
+
return _Footer(self._sectPr, self._document_part, WD_HEADER_FOOTER.FIRST_PAGE)
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def first_page_header(self) -> _Header:
|
|
90
|
+
"""|_Header| object defining header content for the first page of this section.
|
|
91
|
+
|
|
92
|
+
The content of this header definition is ignored unless the property
|
|
93
|
+
:attr:`.different_first_page_header_footer` is set True.
|
|
94
|
+
"""
|
|
95
|
+
return _Header(self._sectPr, self._document_part, WD_HEADER_FOOTER.FIRST_PAGE)
|
|
96
|
+
|
|
97
|
+
@lazyproperty
|
|
98
|
+
def footer(self) -> _Footer:
|
|
99
|
+
"""|_Footer| object representing default page footer for this section.
|
|
100
|
+
|
|
101
|
+
The default footer is used for odd-numbered pages when separate odd/even footers
|
|
102
|
+
are enabled. It is used for both odd and even-numbered pages otherwise.
|
|
103
|
+
"""
|
|
104
|
+
return _Footer(self._sectPr, self._document_part, WD_HEADER_FOOTER.PRIMARY)
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def footer_distance(self) -> Length | None:
|
|
108
|
+
"""Distance from bottom edge of page to bottom edge of the footer.
|
|
109
|
+
|
|
110
|
+
Read/write. |None| if no setting is present in the XML.
|
|
111
|
+
"""
|
|
112
|
+
return self._sectPr.footer
|
|
113
|
+
|
|
114
|
+
@footer_distance.setter
|
|
115
|
+
def footer_distance(self, value: int | Length | None):
|
|
116
|
+
self._sectPr.footer = value
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def gutter(self) -> Length | None:
|
|
120
|
+
"""|Length| object representing page gutter size in English Metric Units.
|
|
121
|
+
|
|
122
|
+
Read/write. The page gutter is extra spacing added to the `inner` margin to
|
|
123
|
+
ensure even margins after page binding. Generally only used in book-bound
|
|
124
|
+
documents with double-sided and facing pages.
|
|
125
|
+
|
|
126
|
+
This setting applies to all pages in this section.
|
|
127
|
+
|
|
128
|
+
"""
|
|
129
|
+
return self._sectPr.gutter
|
|
130
|
+
|
|
131
|
+
@gutter.setter
|
|
132
|
+
def gutter(self, value: int | Length | None):
|
|
133
|
+
self._sectPr.gutter = value
|
|
134
|
+
|
|
135
|
+
@lazyproperty
|
|
136
|
+
def header(self) -> _Header:
|
|
137
|
+
"""|_Header| object representing default page header for this section.
|
|
138
|
+
|
|
139
|
+
The default header is used for odd-numbered pages when separate odd/even headers
|
|
140
|
+
are enabled. It is used for both odd and even-numbered pages otherwise.
|
|
141
|
+
"""
|
|
142
|
+
return _Header(self._sectPr, self._document_part, WD_HEADER_FOOTER.PRIMARY)
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def header_distance(self) -> Length | None:
|
|
146
|
+
"""Distance from top edge of page to top edge of header.
|
|
147
|
+
|
|
148
|
+
Read/write. |None| if no setting is present in the XML. Assigning |None| causes
|
|
149
|
+
default value to be used.
|
|
150
|
+
"""
|
|
151
|
+
return self._sectPr.header
|
|
152
|
+
|
|
153
|
+
@header_distance.setter
|
|
154
|
+
def header_distance(self, value: int | Length | None):
|
|
155
|
+
self._sectPr.header = value
|
|
156
|
+
|
|
157
|
+
def iter_inner_content(self) -> Iterator[Paragraph | Table]:
|
|
158
|
+
"""Generate each Paragraph or Table object in this `section`.
|
|
159
|
+
|
|
160
|
+
Items appear in document order.
|
|
161
|
+
"""
|
|
162
|
+
for element in self._sectPr.iter_inner_content():
|
|
163
|
+
yield (Paragraph(element, self) if isinstance(element, CT_P) else Table(element, self))
|
|
164
|
+
|
|
165
|
+
@property
|
|
166
|
+
def left_margin(self) -> Length | None:
|
|
167
|
+
"""|Length| object representing the left margin for all pages in this section in
|
|
168
|
+
English Metric Units."""
|
|
169
|
+
return self._sectPr.left_margin
|
|
170
|
+
|
|
171
|
+
@left_margin.setter
|
|
172
|
+
def left_margin(self, value: int | Length | None):
|
|
173
|
+
self._sectPr.left_margin = value
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def orientation(self) -> WD_ORIENTATION:
|
|
177
|
+
""":ref:`WdOrientation` member specifying page orientation for this section.
|
|
178
|
+
|
|
179
|
+
One of ``WD_ORIENT.PORTRAIT`` or ``WD_ORIENT.LANDSCAPE``.
|
|
180
|
+
"""
|
|
181
|
+
return self._sectPr.orientation
|
|
182
|
+
|
|
183
|
+
@orientation.setter
|
|
184
|
+
def orientation(self, value: WD_ORIENTATION | None):
|
|
185
|
+
self._sectPr.orientation = value
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def page_height(self) -> Length | None:
|
|
189
|
+
"""Total page height used for this section.
|
|
190
|
+
|
|
191
|
+
This value is inclusive of all edge spacing values such as margins.
|
|
192
|
+
|
|
193
|
+
Page orientation is taken into account, so for example, its expected value
|
|
194
|
+
would be ``Inches(8.5)`` for letter-sized paper when orientation is landscape.
|
|
195
|
+
"""
|
|
196
|
+
return self._sectPr.page_height
|
|
197
|
+
|
|
198
|
+
@page_height.setter
|
|
199
|
+
def page_height(self, value: Length | None):
|
|
200
|
+
self._sectPr.page_height = value
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def page_width(self) -> Length | None:
|
|
204
|
+
"""Total page width used for this section.
|
|
205
|
+
|
|
206
|
+
This value is like "paper size" and includes all edge spacing values such as
|
|
207
|
+
margins.
|
|
208
|
+
|
|
209
|
+
Page orientation is taken into account, so for example, its expected value
|
|
210
|
+
would be ``Inches(11)`` for letter-sized paper when orientation is landscape.
|
|
211
|
+
"""
|
|
212
|
+
return self._sectPr.page_width
|
|
213
|
+
|
|
214
|
+
@page_width.setter
|
|
215
|
+
def page_width(self, value: Length | None):
|
|
216
|
+
self._sectPr.page_width = value
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
def part(self) -> StoryPart:
|
|
220
|
+
return self._document_part
|
|
221
|
+
|
|
222
|
+
@property
|
|
223
|
+
def right_margin(self) -> Length | None:
|
|
224
|
+
"""|Length| object representing the right margin for all pages in this section
|
|
225
|
+
in English Metric Units."""
|
|
226
|
+
return self._sectPr.right_margin
|
|
227
|
+
|
|
228
|
+
@right_margin.setter
|
|
229
|
+
def right_margin(self, value: Length | None):
|
|
230
|
+
self._sectPr.right_margin = value
|
|
231
|
+
|
|
232
|
+
@property
|
|
233
|
+
def start_type(self) -> WD_SECTION_START:
|
|
234
|
+
"""Type of page-break (if any) inserted at the start of this section.
|
|
235
|
+
|
|
236
|
+
For exmple, ``WD_SECTION_START.ODD_PAGE`` if the section should begin on the
|
|
237
|
+
next odd page, possibly inserting two page-breaks instead of one.
|
|
238
|
+
"""
|
|
239
|
+
return self._sectPr.start_type
|
|
240
|
+
|
|
241
|
+
@start_type.setter
|
|
242
|
+
def start_type(self, value: WD_SECTION_START | None):
|
|
243
|
+
self._sectPr.start_type = value
|
|
244
|
+
|
|
245
|
+
@property
|
|
246
|
+
def top_margin(self) -> Length | None:
|
|
247
|
+
"""|Length| object representing the top margin for all pages in this section in
|
|
248
|
+
English Metric Units."""
|
|
249
|
+
return self._sectPr.top_margin
|
|
250
|
+
|
|
251
|
+
@top_margin.setter
|
|
252
|
+
def top_margin(self, value: Length | None):
|
|
253
|
+
self._sectPr.top_margin = value
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class Sections(Sequence[Section]):
|
|
257
|
+
"""Sequence of |Section| objects corresponding to the sections in the document.
|
|
258
|
+
|
|
259
|
+
Supports ``len()``, iteration, and indexed access.
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
def __init__(self, document_elm: CT_Document, document_part: DocumentPart):
|
|
263
|
+
super(Sections, self).__init__()
|
|
264
|
+
self._document_elm = document_elm
|
|
265
|
+
self._document_part = document_part
|
|
266
|
+
|
|
267
|
+
@overload
|
|
268
|
+
def __getitem__(self, key: int) -> Section: ...
|
|
269
|
+
|
|
270
|
+
@overload
|
|
271
|
+
def __getitem__(self, key: slice) -> List[Section]: ...
|
|
272
|
+
|
|
273
|
+
def __getitem__(self, key: int | slice) -> Section | List[Section]:
|
|
274
|
+
if isinstance(key, slice):
|
|
275
|
+
return [
|
|
276
|
+
Section(sectPr, self._document_part)
|
|
277
|
+
for sectPr in self._document_elm.sectPr_lst[key]
|
|
278
|
+
]
|
|
279
|
+
return Section(self._document_elm.sectPr_lst[key], self._document_part)
|
|
280
|
+
|
|
281
|
+
def __iter__(self) -> Iterator[Section]:
|
|
282
|
+
for sectPr in self._document_elm.sectPr_lst:
|
|
283
|
+
yield Section(sectPr, self._document_part)
|
|
284
|
+
|
|
285
|
+
def __len__(self) -> int:
|
|
286
|
+
return len(self._document_elm.sectPr_lst)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class _BaseHeaderFooter(BlockItemContainer):
|
|
290
|
+
"""Base class for header and footer classes."""
|
|
291
|
+
|
|
292
|
+
def __init__(
|
|
293
|
+
self,
|
|
294
|
+
sectPr: CT_SectPr,
|
|
295
|
+
document_part: DocumentPart,
|
|
296
|
+
header_footer_index: WD_HEADER_FOOTER,
|
|
297
|
+
):
|
|
298
|
+
self._sectPr = sectPr
|
|
299
|
+
self._document_part = document_part
|
|
300
|
+
self._hdrftr_index = header_footer_index
|
|
301
|
+
|
|
302
|
+
@property
|
|
303
|
+
def is_linked_to_previous(self) -> bool:
|
|
304
|
+
"""``True`` if this header/footer uses the definition from the prior section.
|
|
305
|
+
|
|
306
|
+
``False`` if this header/footer has an explicit definition.
|
|
307
|
+
|
|
308
|
+
Assigning ``True`` to this property removes the header/footer definition for
|
|
309
|
+
this section, causing it to "inherit" the corresponding definition of the prior
|
|
310
|
+
section. Assigning ``False`` causes a new, empty definition to be added for this
|
|
311
|
+
section, but only if no definition is already present.
|
|
312
|
+
"""
|
|
313
|
+
# ---absence of a header/footer part indicates "linked" behavior---
|
|
314
|
+
return not self._has_definition
|
|
315
|
+
|
|
316
|
+
@is_linked_to_previous.setter
|
|
317
|
+
def is_linked_to_previous(self, value: bool) -> None:
|
|
318
|
+
new_state = bool(value)
|
|
319
|
+
# ---do nothing when value is not being changed---
|
|
320
|
+
if new_state == self.is_linked_to_previous:
|
|
321
|
+
return
|
|
322
|
+
if new_state is True:
|
|
323
|
+
self._drop_definition()
|
|
324
|
+
else:
|
|
325
|
+
self._add_definition()
|
|
326
|
+
|
|
327
|
+
@property
|
|
328
|
+
def part(self) -> HeaderPart | FooterPart:
|
|
329
|
+
"""The |HeaderPart| or |FooterPart| for this header/footer.
|
|
330
|
+
|
|
331
|
+
This overrides `BlockItemContainer.part` and is required to support image
|
|
332
|
+
insertion and perhaps other content like hyperlinks.
|
|
333
|
+
"""
|
|
334
|
+
# ---should not appear in documentation;
|
|
335
|
+
# ---not an interface property, even though public
|
|
336
|
+
return self._get_or_add_definition()
|
|
337
|
+
|
|
338
|
+
def _add_definition(self) -> HeaderPart | FooterPart:
|
|
339
|
+
"""Return newly-added header/footer part."""
|
|
340
|
+
raise NotImplementedError("must be implemented by each subclass")
|
|
341
|
+
|
|
342
|
+
@property
|
|
343
|
+
def _definition(self) -> HeaderPart | FooterPart:
|
|
344
|
+
"""|HeaderPart| or |FooterPart| object containing header/footer content."""
|
|
345
|
+
raise NotImplementedError("must be implemented by each subclass")
|
|
346
|
+
|
|
347
|
+
def _drop_definition(self) -> None:
|
|
348
|
+
"""Remove header/footer part containing the definition of this header/footer."""
|
|
349
|
+
raise NotImplementedError("must be implemented by each subclass")
|
|
350
|
+
|
|
351
|
+
@property
|
|
352
|
+
def _element(self):
|
|
353
|
+
"""`w:hdr` or `w:ftr` element, root of header/footer part."""
|
|
354
|
+
return self._get_or_add_definition().element
|
|
355
|
+
|
|
356
|
+
def _get_or_add_definition(self) -> HeaderPart | FooterPart:
|
|
357
|
+
"""Return HeaderPart or FooterPart object for this section.
|
|
358
|
+
|
|
359
|
+
If this header/footer inherits its content, the part for the prior header/footer
|
|
360
|
+
is returned; this process continue recursively until a definition is found. If
|
|
361
|
+
the definition cannot be inherited (because the header/footer belongs to the
|
|
362
|
+
first section), a new definition is added for that first section and then
|
|
363
|
+
returned.
|
|
364
|
+
"""
|
|
365
|
+
# ---note this method is called recursively to access inherited definitions---
|
|
366
|
+
# ---case-1: definition is not inherited---
|
|
367
|
+
if self._has_definition:
|
|
368
|
+
return self._definition
|
|
369
|
+
# ---case-2: definition is inherited and belongs to second-or-later section---
|
|
370
|
+
prior_headerfooter = self._prior_headerfooter
|
|
371
|
+
if prior_headerfooter:
|
|
372
|
+
return prior_headerfooter._get_or_add_definition()
|
|
373
|
+
# ---case-3: definition is inherited, but belongs to first section---
|
|
374
|
+
return self._add_definition()
|
|
375
|
+
|
|
376
|
+
@property
|
|
377
|
+
def _has_definition(self) -> bool:
|
|
378
|
+
"""True if this header/footer has a related part containing its definition."""
|
|
379
|
+
raise NotImplementedError("must be implemented by each subclass")
|
|
380
|
+
|
|
381
|
+
@property
|
|
382
|
+
def _prior_headerfooter(self) -> _Header | _Footer | None:
|
|
383
|
+
"""|_Header| or |_Footer| proxy on prior sectPr element.
|
|
384
|
+
|
|
385
|
+
Returns None if this is first section.
|
|
386
|
+
"""
|
|
387
|
+
raise NotImplementedError("must be implemented by each subclass")
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
class _Footer(_BaseHeaderFooter):
|
|
391
|
+
"""Page footer, used for all three types (default, even-page, and first-page).
|
|
392
|
+
|
|
393
|
+
Note that, like a document or table cell, a footer must contain a minimum of one
|
|
394
|
+
paragraph and a new or otherwise "empty" footer contains a single empty paragraph.
|
|
395
|
+
This first paragraph can be accessed as `footer.paragraphs[0]` for purposes of
|
|
396
|
+
adding content to it. Using :meth:`add_paragraph()` by itself to add content will
|
|
397
|
+
leave an empty paragraph above the newly added one.
|
|
398
|
+
"""
|
|
399
|
+
|
|
400
|
+
def _add_definition(self) -> FooterPart:
|
|
401
|
+
"""Return newly-added footer part."""
|
|
402
|
+
footer_part, rId = self._document_part.add_footer_part()
|
|
403
|
+
self._sectPr.add_footerReference(self._hdrftr_index, rId)
|
|
404
|
+
return footer_part
|
|
405
|
+
|
|
406
|
+
@property
|
|
407
|
+
def _definition(self):
|
|
408
|
+
"""|FooterPart| object containing content of this footer."""
|
|
409
|
+
footerReference = self._sectPr.get_footerReference(self._hdrftr_index)
|
|
410
|
+
# -- currently this is never called when `._has_definition` evaluates False --
|
|
411
|
+
assert footerReference is not None
|
|
412
|
+
return self._document_part.footer_part(footerReference.rId)
|
|
413
|
+
|
|
414
|
+
def _drop_definition(self):
|
|
415
|
+
"""Remove footer definition (footer part) associated with this section."""
|
|
416
|
+
rId = self._sectPr.remove_footerReference(self._hdrftr_index)
|
|
417
|
+
self._document_part.drop_rel(rId)
|
|
418
|
+
|
|
419
|
+
@property
|
|
420
|
+
def _has_definition(self) -> bool:
|
|
421
|
+
"""True if a footer is defined for this section."""
|
|
422
|
+
footerReference = self._sectPr.get_footerReference(self._hdrftr_index)
|
|
423
|
+
return footerReference is not None
|
|
424
|
+
|
|
425
|
+
@property
|
|
426
|
+
def _prior_headerfooter(self):
|
|
427
|
+
"""|_Footer| proxy on prior sectPr element or None if this is first section."""
|
|
428
|
+
preceding_sectPr = self._sectPr.preceding_sectPr
|
|
429
|
+
return (
|
|
430
|
+
None
|
|
431
|
+
if preceding_sectPr is None
|
|
432
|
+
else _Footer(preceding_sectPr, self._document_part, self._hdrftr_index)
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
class _Header(_BaseHeaderFooter):
|
|
437
|
+
"""Page header, used for all three types (default, even-page, and first-page).
|
|
438
|
+
|
|
439
|
+
Note that, like a document or table cell, a header must contain a minimum of one
|
|
440
|
+
paragraph and a new or otherwise "empty" header contains a single empty paragraph.
|
|
441
|
+
This first paragraph can be accessed as `header.paragraphs[0]` for purposes of
|
|
442
|
+
adding content to it. Using :meth:`add_paragraph()` by itself to add content will
|
|
443
|
+
leave an empty paragraph above the newly added one.
|
|
444
|
+
"""
|
|
445
|
+
|
|
446
|
+
def _add_definition(self):
|
|
447
|
+
"""Return newly-added header part."""
|
|
448
|
+
header_part, rId = self._document_part.add_header_part()
|
|
449
|
+
self._sectPr.add_headerReference(self._hdrftr_index, rId)
|
|
450
|
+
return header_part
|
|
451
|
+
|
|
452
|
+
@property
|
|
453
|
+
def _definition(self):
|
|
454
|
+
"""|HeaderPart| object containing content of this header."""
|
|
455
|
+
headerReference = self._sectPr.get_headerReference(self._hdrftr_index)
|
|
456
|
+
# -- currently this is never called when `._has_definition` evaluates False --
|
|
457
|
+
assert headerReference is not None
|
|
458
|
+
return self._document_part.header_part(headerReference.rId)
|
|
459
|
+
|
|
460
|
+
def _drop_definition(self):
|
|
461
|
+
"""Remove header definition associated with this section."""
|
|
462
|
+
rId = self._sectPr.remove_headerReference(self._hdrftr_index)
|
|
463
|
+
self._document_part.drop_header_part(rId)
|
|
464
|
+
|
|
465
|
+
@property
|
|
466
|
+
def _has_definition(self) -> bool:
|
|
467
|
+
"""True if a header is explicitly defined for this section."""
|
|
468
|
+
headerReference = self._sectPr.get_headerReference(self._hdrftr_index)
|
|
469
|
+
return headerReference is not None
|
|
470
|
+
|
|
471
|
+
@property
|
|
472
|
+
def _prior_headerfooter(self):
|
|
473
|
+
"""|_Header| proxy on prior sectPr element or None if this is first section."""
|
|
474
|
+
preceding_sectPr = self._sectPr.preceding_sectPr
|
|
475
|
+
return (
|
|
476
|
+
None
|
|
477
|
+
if preceding_sectPr is None
|
|
478
|
+
else _Header(preceding_sectPr, self._document_part, self._hdrftr_index)
|
|
479
|
+
)
|
docx/settings.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Settings object, providing access to document-level settings."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, cast
|
|
6
|
+
|
|
7
|
+
from docx.shared import ElementProxy
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
import docx.types as t
|
|
11
|
+
from docx.oxml.settings import CT_Settings
|
|
12
|
+
from docx.oxml.xmlchemy import BaseOxmlElement
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Settings(ElementProxy):
|
|
16
|
+
"""Provides access to document-level settings for a document.
|
|
17
|
+
|
|
18
|
+
Accessed using the :attr:`.Document.settings` property.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, element: BaseOxmlElement, parent: t.ProvidesXmlPart | None = None):
|
|
22
|
+
super().__init__(element, parent)
|
|
23
|
+
self._settings = cast("CT_Settings", element)
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def odd_and_even_pages_header_footer(self) -> bool:
|
|
27
|
+
"""True if this document has distinct odd and even page headers and footers.
|
|
28
|
+
|
|
29
|
+
Read/write.
|
|
30
|
+
"""
|
|
31
|
+
return self._settings.evenAndOddHeaders_val
|
|
32
|
+
|
|
33
|
+
@odd_and_even_pages_header_footer.setter
|
|
34
|
+
def odd_and_even_pages_header_footer(self, value: bool):
|
|
35
|
+
self._settings.evenAndOddHeaders_val = value
|