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/shape.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Objects related to shapes.
|
|
2
|
+
|
|
3
|
+
A shape is a visual object that appears on the drawing layer of a document.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from docx.enum.shape import WD_INLINE_SHAPE
|
|
11
|
+
from docx.oxml.ns import nsmap
|
|
12
|
+
from docx.shared import Parented
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from docx.oxml.document import CT_Body
|
|
16
|
+
from docx.oxml.shape import CT_Inline
|
|
17
|
+
from docx.parts.story import StoryPart
|
|
18
|
+
from docx.shared import Length
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class InlineShapes(Parented):
|
|
22
|
+
"""Sequence of |InlineShape| instances, supporting len(), iteration, and indexed access."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, body_elm: CT_Body, parent: StoryPart):
|
|
25
|
+
super(InlineShapes, self).__init__(parent)
|
|
26
|
+
self._body = body_elm
|
|
27
|
+
|
|
28
|
+
def __getitem__(self, idx: int):
|
|
29
|
+
"""Provide indexed access, e.g. 'inline_shapes[idx]'."""
|
|
30
|
+
try:
|
|
31
|
+
inline = self._inline_lst[idx]
|
|
32
|
+
except IndexError:
|
|
33
|
+
msg = "inline shape index [%d] out of range" % idx
|
|
34
|
+
raise IndexError(msg)
|
|
35
|
+
|
|
36
|
+
return InlineShape(inline)
|
|
37
|
+
|
|
38
|
+
def __iter__(self):
|
|
39
|
+
return (InlineShape(inline) for inline in self._inline_lst)
|
|
40
|
+
|
|
41
|
+
def __len__(self):
|
|
42
|
+
return len(self._inline_lst)
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def _inline_lst(self):
|
|
46
|
+
body = self._body
|
|
47
|
+
xpath = "//w:p/w:r/w:drawing/wp:inline"
|
|
48
|
+
return body.xpath(xpath)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class InlineShape:
|
|
52
|
+
"""Proxy for an ``<wp:inline>`` element, representing the container for an inline
|
|
53
|
+
graphical object."""
|
|
54
|
+
|
|
55
|
+
def __init__(self, inline: CT_Inline):
|
|
56
|
+
super(InlineShape, self).__init__()
|
|
57
|
+
self._inline = inline
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def height(self) -> Length:
|
|
61
|
+
"""Read/write.
|
|
62
|
+
|
|
63
|
+
The display height of this inline shape as an |Emu| instance.
|
|
64
|
+
"""
|
|
65
|
+
return self._inline.extent.cy
|
|
66
|
+
|
|
67
|
+
@height.setter
|
|
68
|
+
def height(self, cy: Length):
|
|
69
|
+
self._inline.extent.cy = cy
|
|
70
|
+
self._inline.graphic.graphicData.pic.spPr.cy = cy
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def type(self):
|
|
74
|
+
"""The type of this inline shape as a member of
|
|
75
|
+
``docx.enum.shape.WD_INLINE_SHAPE``, e.g. ``LINKED_PICTURE``.
|
|
76
|
+
|
|
77
|
+
Read-only.
|
|
78
|
+
"""
|
|
79
|
+
graphicData = self._inline.graphic.graphicData
|
|
80
|
+
uri = graphicData.uri
|
|
81
|
+
if uri == nsmap["pic"]:
|
|
82
|
+
blip = graphicData.pic.blipFill.blip
|
|
83
|
+
if blip.link is not None:
|
|
84
|
+
return WD_INLINE_SHAPE.LINKED_PICTURE
|
|
85
|
+
return WD_INLINE_SHAPE.PICTURE
|
|
86
|
+
if uri == nsmap["c"]:
|
|
87
|
+
return WD_INLINE_SHAPE.CHART
|
|
88
|
+
if uri == nsmap["dgm"]:
|
|
89
|
+
return WD_INLINE_SHAPE.SMART_ART
|
|
90
|
+
return WD_INLINE_SHAPE.NOT_IMPLEMENTED
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def width(self):
|
|
94
|
+
"""Read/write.
|
|
95
|
+
|
|
96
|
+
The display width of this inline shape as an |Emu| instance.
|
|
97
|
+
"""
|
|
98
|
+
return self._inline.extent.cx
|
|
99
|
+
|
|
100
|
+
@width.setter
|
|
101
|
+
def width(self, cx: Length):
|
|
102
|
+
self._inline.extent.cx = cx
|
|
103
|
+
self._inline.graphic.graphicData.pic.spPr.cx = cx
|
docx/shared.py
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
"""Objects shared by docx modules."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import functools
|
|
6
|
+
from typing import (
|
|
7
|
+
TYPE_CHECKING,
|
|
8
|
+
Any,
|
|
9
|
+
Callable,
|
|
10
|
+
Generic,
|
|
11
|
+
Iterator,
|
|
12
|
+
List,
|
|
13
|
+
Tuple,
|
|
14
|
+
TypeVar,
|
|
15
|
+
cast,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
import docx.types as t
|
|
20
|
+
from docx.opc.part import XmlPart
|
|
21
|
+
from docx.oxml.xmlchemy import BaseOxmlElement
|
|
22
|
+
from docx.parts.story import StoryPart
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Length(int):
|
|
26
|
+
"""Base class for length constructor classes Inches, Cm, Mm, Px, and Emu.
|
|
27
|
+
|
|
28
|
+
Behaves as an int count of English Metric Units, 914,400 to the inch, 36,000 to the
|
|
29
|
+
mm. Provides convenience unit conversion methods in the form of read-only
|
|
30
|
+
properties. Immutable.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
_EMUS_PER_INCH = 914400
|
|
34
|
+
_EMUS_PER_CM = 360000
|
|
35
|
+
_EMUS_PER_MM = 36000
|
|
36
|
+
_EMUS_PER_PT = 12700
|
|
37
|
+
_EMUS_PER_TWIP = 635
|
|
38
|
+
|
|
39
|
+
def __new__(cls, emu: int):
|
|
40
|
+
return int.__new__(cls, emu)
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def cm(self):
|
|
44
|
+
"""The equivalent length expressed in centimeters (float)."""
|
|
45
|
+
return self / float(self._EMUS_PER_CM)
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def emu(self):
|
|
49
|
+
"""The equivalent length expressed in English Metric Units (int)."""
|
|
50
|
+
return self
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def inches(self):
|
|
54
|
+
"""The equivalent length expressed in inches (float)."""
|
|
55
|
+
return self / float(self._EMUS_PER_INCH)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def mm(self):
|
|
59
|
+
"""The equivalent length expressed in millimeters (float)."""
|
|
60
|
+
return self / float(self._EMUS_PER_MM)
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def pt(self):
|
|
64
|
+
"""Floating point length in points."""
|
|
65
|
+
return self / float(self._EMUS_PER_PT)
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def twips(self):
|
|
69
|
+
"""The equivalent length expressed in twips (int)."""
|
|
70
|
+
return int(round(self / float(self._EMUS_PER_TWIP)))
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class Inches(Length):
|
|
74
|
+
"""Convenience constructor for length in inches, e.g. ``width = Inches(0.5)``."""
|
|
75
|
+
|
|
76
|
+
def __new__(cls, inches: float):
|
|
77
|
+
emu = int(inches * Length._EMUS_PER_INCH)
|
|
78
|
+
return Length.__new__(cls, emu)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class Cm(Length):
|
|
82
|
+
"""Convenience constructor for length in centimeters, e.g. ``height = Cm(12)``."""
|
|
83
|
+
|
|
84
|
+
def __new__(cls, cm: float):
|
|
85
|
+
emu = int(cm * Length._EMUS_PER_CM)
|
|
86
|
+
return Length.__new__(cls, emu)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Emu(Length):
|
|
90
|
+
"""Convenience constructor for length in English Metric Units, e.g. ``width =
|
|
91
|
+
Emu(457200)``."""
|
|
92
|
+
|
|
93
|
+
def __new__(cls, emu: int):
|
|
94
|
+
return Length.__new__(cls, int(emu))
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class Mm(Length):
|
|
98
|
+
"""Convenience constructor for length in millimeters, e.g. ``width = Mm(240.5)``."""
|
|
99
|
+
|
|
100
|
+
def __new__(cls, mm: float):
|
|
101
|
+
emu = int(mm * Length._EMUS_PER_MM)
|
|
102
|
+
return Length.__new__(cls, emu)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class Pt(Length):
|
|
106
|
+
"""Convenience value class for specifying a length in points."""
|
|
107
|
+
|
|
108
|
+
def __new__(cls, points: float):
|
|
109
|
+
emu = int(points * Length._EMUS_PER_PT)
|
|
110
|
+
return Length.__new__(cls, emu)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class Twips(Length):
|
|
114
|
+
"""Convenience constructor for length in twips, e.g. ``width = Twips(42)``.
|
|
115
|
+
|
|
116
|
+
A twip is a twentieth of a point, 635 EMU.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
def __new__(cls, twips: float):
|
|
120
|
+
emu = int(twips * Length._EMUS_PER_TWIP)
|
|
121
|
+
return Length.__new__(cls, emu)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class RGBColor(Tuple[int, int, int]):
|
|
125
|
+
"""Immutable value object defining a particular RGB color."""
|
|
126
|
+
|
|
127
|
+
def __new__(cls, r: int, g: int, b: int):
|
|
128
|
+
msg = "RGBColor() takes three integer values 0-255"
|
|
129
|
+
for val in (r, g, b):
|
|
130
|
+
if not isinstance(val, int): # pyright: ignore[reportUnnecessaryIsInstance]
|
|
131
|
+
raise TypeError(msg)
|
|
132
|
+
if val < 0 or val > 255:
|
|
133
|
+
raise ValueError(msg)
|
|
134
|
+
return super(RGBColor, cls).__new__(cls, (r, g, b))
|
|
135
|
+
|
|
136
|
+
def __repr__(self):
|
|
137
|
+
return "RGBColor(0x%02x, 0x%02x, 0x%02x)" % self
|
|
138
|
+
|
|
139
|
+
def __str__(self):
|
|
140
|
+
"""Return a hex string rgb value, like '3C2F80'."""
|
|
141
|
+
return "%02X%02X%02X" % self
|
|
142
|
+
|
|
143
|
+
@classmethod
|
|
144
|
+
def from_string(cls, rgb_hex_str: str) -> RGBColor:
|
|
145
|
+
"""Return a new instance from an RGB color hex string like ``'3C2F80'``."""
|
|
146
|
+
r = int(rgb_hex_str[:2], 16)
|
|
147
|
+
g = int(rgb_hex_str[2:4], 16)
|
|
148
|
+
b = int(rgb_hex_str[4:], 16)
|
|
149
|
+
return cls(r, g, b)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
T = TypeVar("T")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class lazyproperty(Generic[T]):
|
|
156
|
+
"""Decorator like @property, but evaluated only on first access.
|
|
157
|
+
|
|
158
|
+
Like @property, this can only be used to decorate methods having only a `self`
|
|
159
|
+
parameter, and is accessed like an attribute on an instance, i.e. trailing
|
|
160
|
+
parentheses are not used. Unlike @property, the decorated method is only evaluated
|
|
161
|
+
on first access; the resulting value is cached and that same value returned on
|
|
162
|
+
second and later access without re-evaluation of the method.
|
|
163
|
+
|
|
164
|
+
Like @property, this class produces a *data descriptor* object, which is stored in
|
|
165
|
+
the __dict__ of the *class* under the name of the decorated method ('fget'
|
|
166
|
+
nominally). The cached value is stored in the __dict__ of the *instance* under that
|
|
167
|
+
same name.
|
|
168
|
+
|
|
169
|
+
Because it is a data descriptor (as opposed to a *non-data descriptor*), its
|
|
170
|
+
`__get__()` method is executed on each access of the decorated attribute; the
|
|
171
|
+
__dict__ item of the same name is "shadowed" by the descriptor.
|
|
172
|
+
|
|
173
|
+
While this may represent a performance improvement over a property, its greater
|
|
174
|
+
benefit may be its other characteristics. One common use is to construct
|
|
175
|
+
collaborator objects, removing that "real work" from the constructor, while still
|
|
176
|
+
only executing once. It also de-couples client code from any sequencing
|
|
177
|
+
considerations; if it's accessed from more than one location, it's assured it will
|
|
178
|
+
be ready whenever needed.
|
|
179
|
+
|
|
180
|
+
Loosely based on: https://stackoverflow.com/a/6849299/1902513.
|
|
181
|
+
|
|
182
|
+
A lazyproperty is read-only. There is no counterpart to the optional "setter" (or
|
|
183
|
+
deleter) behavior of an @property. This is critically important to maintaining its
|
|
184
|
+
immutability and idempotence guarantees. Attempting to assign to a lazyproperty
|
|
185
|
+
raises AttributeError unconditionally.
|
|
186
|
+
|
|
187
|
+
The parameter names in the methods below correspond to this usage example::
|
|
188
|
+
|
|
189
|
+
class Obj(object)
|
|
190
|
+
|
|
191
|
+
@lazyproperty
|
|
192
|
+
def fget(self):
|
|
193
|
+
return 'some result'
|
|
194
|
+
|
|
195
|
+
obj = Obj()
|
|
196
|
+
|
|
197
|
+
Not suitable for wrapping a function (as opposed to a method) because it is not
|
|
198
|
+
callable."""
|
|
199
|
+
|
|
200
|
+
def __init__(self, fget: Callable[..., T]) -> None:
|
|
201
|
+
"""*fget* is the decorated method (a "getter" function).
|
|
202
|
+
|
|
203
|
+
A lazyproperty is read-only, so there is only an *fget* function (a regular
|
|
204
|
+
@property can also have an fset and fdel function). This name was chosen for
|
|
205
|
+
consistency with Python's `property` class which uses this name for the
|
|
206
|
+
corresponding parameter.
|
|
207
|
+
"""
|
|
208
|
+
# --- maintain a reference to the wrapped getter method
|
|
209
|
+
self._fget = fget
|
|
210
|
+
# --- and store the name of that decorated method
|
|
211
|
+
self._name = fget.__name__
|
|
212
|
+
# --- adopt fget's __name__, __doc__, and other attributes
|
|
213
|
+
functools.update_wrapper(self, fget) # pyright: ignore
|
|
214
|
+
|
|
215
|
+
def __get__(self, obj: Any, type: Any = None) -> T:
|
|
216
|
+
"""Called on each access of 'fget' attribute on class or instance.
|
|
217
|
+
|
|
218
|
+
*self* is this instance of a lazyproperty descriptor "wrapping" the property
|
|
219
|
+
method it decorates (`fget`, nominally).
|
|
220
|
+
|
|
221
|
+
*obj* is the "host" object instance when the attribute is accessed from an
|
|
222
|
+
object instance, e.g. `obj = Obj(); obj.fget`. *obj* is None when accessed on
|
|
223
|
+
the class, e.g. `Obj.fget`.
|
|
224
|
+
|
|
225
|
+
*type* is the class hosting the decorated getter method (`fget`) on both class
|
|
226
|
+
and instance attribute access.
|
|
227
|
+
"""
|
|
228
|
+
# --- when accessed on class, e.g. Obj.fget, just return this descriptor
|
|
229
|
+
# --- instance (patched above to look like fget).
|
|
230
|
+
if obj is None:
|
|
231
|
+
return self # type: ignore
|
|
232
|
+
|
|
233
|
+
# --- when accessed on instance, start by checking instance __dict__ for
|
|
234
|
+
# --- item with key matching the wrapped function's name
|
|
235
|
+
value = obj.__dict__.get(self._name)
|
|
236
|
+
if value is None:
|
|
237
|
+
# --- on first access, the __dict__ item will be absent. Evaluate fget()
|
|
238
|
+
# --- and store that value in the (otherwise unused) host-object
|
|
239
|
+
# --- __dict__ value of same name ('fget' nominally)
|
|
240
|
+
value = self._fget(obj)
|
|
241
|
+
obj.__dict__[self._name] = value
|
|
242
|
+
return cast(T, value)
|
|
243
|
+
|
|
244
|
+
def __set__(self, obj: Any, value: Any) -> None:
|
|
245
|
+
"""Raises unconditionally, to preserve read-only behavior.
|
|
246
|
+
|
|
247
|
+
This decorator is intended to implement immutable (and idempotent) object
|
|
248
|
+
attributes. For that reason, assignment to this property must be explicitly
|
|
249
|
+
prevented.
|
|
250
|
+
|
|
251
|
+
If this __set__ method was not present, this descriptor would become a
|
|
252
|
+
*non-data descriptor*. That would be nice because the cached value would be
|
|
253
|
+
accessed directly once set (__dict__ attrs have precedence over non-data
|
|
254
|
+
descriptors on instance attribute lookup). The problem is, there would be
|
|
255
|
+
nothing to stop assignment to the cached value, which would overwrite the result
|
|
256
|
+
of `fget()` and break both the immutability and idempotence guarantees of this
|
|
257
|
+
decorator.
|
|
258
|
+
|
|
259
|
+
The performance with this __set__() method in place was roughly 0.4 usec per
|
|
260
|
+
access when measured on a 2.8GHz development machine; so quite snappy and
|
|
261
|
+
probably not a rich target for optimization efforts.
|
|
262
|
+
"""
|
|
263
|
+
raise AttributeError("can't set attribute")
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def write_only_property(f: Callable[[Any, Any], None]):
|
|
267
|
+
"""@write_only_property decorator.
|
|
268
|
+
|
|
269
|
+
Creates a property (descriptor attribute) that accepts assignment, but not getattr
|
|
270
|
+
(use in an expression).
|
|
271
|
+
"""
|
|
272
|
+
docstring = f.__doc__
|
|
273
|
+
|
|
274
|
+
return property(fset=f, doc=docstring)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class ElementProxy:
|
|
278
|
+
"""Base class for lxml element proxy classes.
|
|
279
|
+
|
|
280
|
+
An element proxy class is one whose primary responsibilities are fulfilled by
|
|
281
|
+
manipulating the attributes and child elements of an XML element. They are the most
|
|
282
|
+
common type of class in python-docx other than custom element (oxml) classes.
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
def __init__(self, element: BaseOxmlElement, parent: t.ProvidesXmlPart | None = None):
|
|
286
|
+
self._element = element
|
|
287
|
+
self._parent = parent
|
|
288
|
+
|
|
289
|
+
def __eq__(self, other: object):
|
|
290
|
+
"""Return |True| if this proxy object refers to the same oxml element as does
|
|
291
|
+
`other`.
|
|
292
|
+
|
|
293
|
+
ElementProxy objects are value objects and should maintain no mutable local
|
|
294
|
+
state. Equality for proxy objects is defined as referring to the same XML
|
|
295
|
+
element, whether or not they are the same proxy object instance.
|
|
296
|
+
"""
|
|
297
|
+
if not isinstance(other, ElementProxy):
|
|
298
|
+
return False
|
|
299
|
+
return self._element is other._element
|
|
300
|
+
|
|
301
|
+
def __ne__(self, other: object):
|
|
302
|
+
if not isinstance(other, ElementProxy):
|
|
303
|
+
return True
|
|
304
|
+
return self._element is not other._element
|
|
305
|
+
|
|
306
|
+
@property
|
|
307
|
+
def element(self):
|
|
308
|
+
"""The lxml element proxied by this object."""
|
|
309
|
+
return self._element
|
|
310
|
+
|
|
311
|
+
@property
|
|
312
|
+
def part(self) -> XmlPart:
|
|
313
|
+
"""The package part containing this object."""
|
|
314
|
+
if self._parent is None:
|
|
315
|
+
raise ValueError("part is not accessible from this element")
|
|
316
|
+
return self._parent.part
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
class Parented:
|
|
320
|
+
"""Provides common services for document elements that occur below a part but may
|
|
321
|
+
occasionally require an ancestor object to provide a service, such as add or drop a
|
|
322
|
+
relationship.
|
|
323
|
+
|
|
324
|
+
Provides ``self._parent`` attribute to subclasses.
|
|
325
|
+
"""
|
|
326
|
+
|
|
327
|
+
def __init__(self, parent: t.ProvidesXmlPart):
|
|
328
|
+
self._parent = parent
|
|
329
|
+
|
|
330
|
+
@property
|
|
331
|
+
def part(self) -> XmlPart:
|
|
332
|
+
"""The package part containing this object."""
|
|
333
|
+
return self._parent.part
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class StoryChild:
|
|
337
|
+
"""A document element within a story part.
|
|
338
|
+
|
|
339
|
+
Story parts include DocumentPart and Header/FooterPart and can contain block items
|
|
340
|
+
(paragraphs and tables). Items from the block-item subtree occasionally require an
|
|
341
|
+
ancestor object to provide access to part-level or package-level items like styles
|
|
342
|
+
or images or to add or drop a relationship.
|
|
343
|
+
|
|
344
|
+
Provides `self._parent` attribute to subclasses.
|
|
345
|
+
"""
|
|
346
|
+
|
|
347
|
+
def __init__(self, parent: t.ProvidesStoryPart):
|
|
348
|
+
self._parent = parent
|
|
349
|
+
|
|
350
|
+
@property
|
|
351
|
+
def part(self) -> StoryPart:
|
|
352
|
+
"""The package part containing this object."""
|
|
353
|
+
return self._parent.part
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
class TextAccumulator:
|
|
357
|
+
"""Accepts `str` fragments and joins them together, in order, on `.pop().
|
|
358
|
+
|
|
359
|
+
Handy when text in a stream is broken up arbitrarily and you want to join it back
|
|
360
|
+
together within certain bounds. The optional `separator` argument determines how
|
|
361
|
+
the text fragments are punctuated, defaulting to the empty string.
|
|
362
|
+
"""
|
|
363
|
+
|
|
364
|
+
def __init__(self, separator: str = ""):
|
|
365
|
+
self._separator = separator
|
|
366
|
+
self._texts: List[str] = []
|
|
367
|
+
|
|
368
|
+
def push(self, text: str) -> None:
|
|
369
|
+
"""Add a text fragment to the accumulator."""
|
|
370
|
+
self._texts.append(text)
|
|
371
|
+
|
|
372
|
+
def pop(self) -> Iterator[str]:
|
|
373
|
+
"""Generate sero-or-one str from those accumulated.
|
|
374
|
+
|
|
375
|
+
Using `yield from accum.pop()` in a generator setting avoids producing an empty
|
|
376
|
+
string when no text is in the accumulator.
|
|
377
|
+
"""
|
|
378
|
+
if not self._texts:
|
|
379
|
+
return
|
|
380
|
+
text = self._separator.join(self._texts)
|
|
381
|
+
self._texts.clear()
|
|
382
|
+
yield text
|
docx/styles/__init__.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Sub-package module for docx.styles sub-package."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Dict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BabelFish:
|
|
9
|
+
"""Translates special-case style names from UI name (e.g. Heading 1) to
|
|
10
|
+
internal/styles.xml name (e.g. heading 1) and back."""
|
|
11
|
+
|
|
12
|
+
style_aliases = (
|
|
13
|
+
("Caption", "caption"),
|
|
14
|
+
("Footer", "footer"),
|
|
15
|
+
("Header", "header"),
|
|
16
|
+
("Heading 1", "heading 1"),
|
|
17
|
+
("Heading 2", "heading 2"),
|
|
18
|
+
("Heading 3", "heading 3"),
|
|
19
|
+
("Heading 4", "heading 4"),
|
|
20
|
+
("Heading 5", "heading 5"),
|
|
21
|
+
("Heading 6", "heading 6"),
|
|
22
|
+
("Heading 7", "heading 7"),
|
|
23
|
+
("Heading 8", "heading 8"),
|
|
24
|
+
("Heading 9", "heading 9"),
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
internal_style_names: Dict[str, str] = dict(style_aliases)
|
|
28
|
+
ui_style_names = {item[1]: item[0] for item in style_aliases}
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def ui2internal(cls, ui_style_name: str) -> str:
|
|
32
|
+
"""Return the internal style name corresponding to `ui_style_name`, such as
|
|
33
|
+
'heading 1' for 'Heading 1'."""
|
|
34
|
+
return cls.internal_style_names.get(ui_style_name, ui_style_name)
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def internal2ui(cls, internal_style_name: str) -> str:
|
|
38
|
+
"""Return the user interface style name corresponding to `internal_style_name`,
|
|
39
|
+
such as 'Heading 1' for 'heading 1'."""
|
|
40
|
+
return cls.ui_style_names.get(internal_style_name, internal_style_name)
|