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/image/helpers.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from struct import Struct
|
|
2
|
+
|
|
3
|
+
from .exceptions import UnexpectedEndOfFileError
|
|
4
|
+
|
|
5
|
+
BIG_ENDIAN = ">"
|
|
6
|
+
LITTLE_ENDIAN = "<"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class StreamReader:
|
|
10
|
+
"""Wraps a file-like object to provide access to structured data from a binary file.
|
|
11
|
+
|
|
12
|
+
Byte-order is configurable. `base_offset` is added to any base value provided to
|
|
13
|
+
calculate actual location for reads.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, stream, byte_order, base_offset=0):
|
|
17
|
+
super(StreamReader, self).__init__()
|
|
18
|
+
self._stream = stream
|
|
19
|
+
self._byte_order = LITTLE_ENDIAN if byte_order == LITTLE_ENDIAN else BIG_ENDIAN
|
|
20
|
+
self._base_offset = base_offset
|
|
21
|
+
|
|
22
|
+
def read(self, count):
|
|
23
|
+
"""Allow pass-through read() call."""
|
|
24
|
+
return self._stream.read(count)
|
|
25
|
+
|
|
26
|
+
def read_byte(self, base, offset=0):
|
|
27
|
+
"""Return the int value of the byte at the file position defined by
|
|
28
|
+
self._base_offset + `base` + `offset`.
|
|
29
|
+
|
|
30
|
+
If `base` is None, the byte is read from the current position in the stream.
|
|
31
|
+
"""
|
|
32
|
+
fmt = "B"
|
|
33
|
+
return self._read_int(fmt, base, offset)
|
|
34
|
+
|
|
35
|
+
def read_long(self, base, offset=0):
|
|
36
|
+
"""Return the int value of the four bytes at the file position defined by
|
|
37
|
+
self._base_offset + `base` + `offset`.
|
|
38
|
+
|
|
39
|
+
If `base` is None, the long is read from the current position in the stream. The
|
|
40
|
+
endian setting of this instance is used to interpret the byte layout of the
|
|
41
|
+
long.
|
|
42
|
+
"""
|
|
43
|
+
fmt = "<L" if self._byte_order is LITTLE_ENDIAN else ">L"
|
|
44
|
+
return self._read_int(fmt, base, offset)
|
|
45
|
+
|
|
46
|
+
def read_short(self, base, offset=0):
|
|
47
|
+
"""Return the int value of the two bytes at the file position determined by
|
|
48
|
+
`base` and `offset`, similarly to ``read_long()`` above."""
|
|
49
|
+
fmt = b"<H" if self._byte_order is LITTLE_ENDIAN else b">H"
|
|
50
|
+
return self._read_int(fmt, base, offset)
|
|
51
|
+
|
|
52
|
+
def read_str(self, char_count, base, offset=0):
|
|
53
|
+
"""Return a string containing the `char_count` bytes at the file position
|
|
54
|
+
determined by self._base_offset + `base` + `offset`."""
|
|
55
|
+
|
|
56
|
+
def str_struct(char_count):
|
|
57
|
+
format_ = "%ds" % char_count
|
|
58
|
+
return Struct(format_)
|
|
59
|
+
|
|
60
|
+
struct = str_struct(char_count)
|
|
61
|
+
chars = self._unpack_item(struct, base, offset)
|
|
62
|
+
unicode_str = chars.decode("UTF-8")
|
|
63
|
+
return unicode_str
|
|
64
|
+
|
|
65
|
+
def seek(self, base, offset=0):
|
|
66
|
+
location = self._base_offset + base + offset
|
|
67
|
+
self._stream.seek(location)
|
|
68
|
+
|
|
69
|
+
def tell(self):
|
|
70
|
+
"""Allow pass-through tell() call."""
|
|
71
|
+
return self._stream.tell()
|
|
72
|
+
|
|
73
|
+
def _read_bytes(self, byte_count, base, offset):
|
|
74
|
+
self.seek(base, offset)
|
|
75
|
+
bytes_ = self._stream.read(byte_count)
|
|
76
|
+
if len(bytes_) < byte_count:
|
|
77
|
+
raise UnexpectedEndOfFileError
|
|
78
|
+
return bytes_
|
|
79
|
+
|
|
80
|
+
def _read_int(self, fmt, base, offset):
|
|
81
|
+
struct = Struct(fmt)
|
|
82
|
+
return self._unpack_item(struct, base, offset)
|
|
83
|
+
|
|
84
|
+
def _unpack_item(self, struct, base, offset):
|
|
85
|
+
bytes_ = self._read_bytes(struct.size, base, offset)
|
|
86
|
+
return struct.unpack(bytes_)[0]
|
docx/image/image.py
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"""Provides objects that can characterize image streams.
|
|
2
|
+
|
|
3
|
+
That characterization is as to content type and size, as a required step in including
|
|
4
|
+
them in a document.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import hashlib
|
|
10
|
+
import io
|
|
11
|
+
import os
|
|
12
|
+
from typing import IO, Tuple
|
|
13
|
+
|
|
14
|
+
from docx.image.exceptions import UnrecognizedImageError
|
|
15
|
+
from docx.shared import Emu, Inches, Length, lazyproperty
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Image:
|
|
19
|
+
"""Graphical image stream such as JPEG, PNG, or GIF with properties and methods
|
|
20
|
+
required by ImagePart."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, blob: bytes, filename: str, image_header: BaseImageHeader):
|
|
23
|
+
super(Image, self).__init__()
|
|
24
|
+
self._blob = blob
|
|
25
|
+
self._filename = filename
|
|
26
|
+
self._image_header = image_header
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def from_blob(cls, blob: bytes) -> Image:
|
|
30
|
+
"""Return a new |Image| subclass instance parsed from the image binary contained
|
|
31
|
+
in `blob`."""
|
|
32
|
+
stream = io.BytesIO(blob)
|
|
33
|
+
return cls._from_stream(stream, blob)
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def from_file(cls, image_descriptor: str | IO[bytes]):
|
|
37
|
+
"""Return a new |Image| subclass instance loaded from the image file identified
|
|
38
|
+
by `image_descriptor`, a path or file-like object."""
|
|
39
|
+
if isinstance(image_descriptor, str):
|
|
40
|
+
path = image_descriptor
|
|
41
|
+
with open(path, "rb") as f:
|
|
42
|
+
blob = f.read()
|
|
43
|
+
stream = io.BytesIO(blob)
|
|
44
|
+
filename = os.path.basename(path)
|
|
45
|
+
else:
|
|
46
|
+
stream = image_descriptor
|
|
47
|
+
stream.seek(0)
|
|
48
|
+
blob = stream.read()
|
|
49
|
+
filename = None
|
|
50
|
+
return cls._from_stream(stream, blob, filename)
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def blob(self):
|
|
54
|
+
"""The bytes of the image 'file'."""
|
|
55
|
+
return self._blob
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def content_type(self) -> str:
|
|
59
|
+
"""MIME content type for this image, e.g. ``'image/jpeg'`` for a JPEG image."""
|
|
60
|
+
return self._image_header.content_type
|
|
61
|
+
|
|
62
|
+
@lazyproperty
|
|
63
|
+
def ext(self):
|
|
64
|
+
"""The file extension for the image.
|
|
65
|
+
|
|
66
|
+
If an actual one is available from a load filename it is used. Otherwise a
|
|
67
|
+
canonical extension is assigned based on the content type. Does not contain the
|
|
68
|
+
leading period, e.g. 'jpg', not '.jpg'.
|
|
69
|
+
"""
|
|
70
|
+
return os.path.splitext(self._filename)[1][1:]
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def filename(self):
|
|
74
|
+
"""Original image file name, if loaded from disk, or a generic filename if
|
|
75
|
+
loaded from an anonymous stream."""
|
|
76
|
+
return self._filename
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def px_width(self) -> int:
|
|
80
|
+
"""The horizontal pixel dimension of the image."""
|
|
81
|
+
return self._image_header.px_width
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def px_height(self) -> int:
|
|
85
|
+
"""The vertical pixel dimension of the image."""
|
|
86
|
+
return self._image_header.px_height
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def horz_dpi(self) -> int:
|
|
90
|
+
"""Integer dots per inch for the width of this image.
|
|
91
|
+
|
|
92
|
+
Defaults to 72 when not present in the file, as is often the case.
|
|
93
|
+
"""
|
|
94
|
+
return self._image_header.horz_dpi
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def vert_dpi(self) -> int:
|
|
98
|
+
"""Integer dots per inch for the height of this image.
|
|
99
|
+
|
|
100
|
+
Defaults to 72 when not present in the file, as is often the case.
|
|
101
|
+
"""
|
|
102
|
+
return self._image_header.vert_dpi
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def width(self) -> Inches:
|
|
106
|
+
"""A |Length| value representing the native width of the image, calculated from
|
|
107
|
+
the values of `px_width` and `horz_dpi`."""
|
|
108
|
+
return Inches(self.px_width / self.horz_dpi)
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def height(self) -> Inches:
|
|
112
|
+
"""A |Length| value representing the native height of the image, calculated from
|
|
113
|
+
the values of `px_height` and `vert_dpi`."""
|
|
114
|
+
return Inches(self.px_height / self.vert_dpi)
|
|
115
|
+
|
|
116
|
+
def scaled_dimensions(
|
|
117
|
+
self, width: int | Length | None = None, height: int | Length | None = None
|
|
118
|
+
) -> Tuple[Length, Length]:
|
|
119
|
+
"""(cx, cy) pair representing scaled dimensions of this image.
|
|
120
|
+
|
|
121
|
+
The native dimensions of the image are scaled by applying the following rules to
|
|
122
|
+
the `width` and `height` arguments.
|
|
123
|
+
|
|
124
|
+
* If both `width` and `height` are specified, the return value is (`width`,
|
|
125
|
+
`height`); no scaling is performed.
|
|
126
|
+
* If only one is specified, it is used to compute a scaling factor that is then
|
|
127
|
+
applied to the unspecified dimension, preserving the aspect ratio of the image.
|
|
128
|
+
* If both `width` and `height` are |None|, the native dimensions are returned.
|
|
129
|
+
|
|
130
|
+
The native dimensions are calculated using the dots-per-inch (dpi) value
|
|
131
|
+
embedded in the image, defaulting to 72 dpi if no value is specified, as is
|
|
132
|
+
often the case. The returned values are both |Length| objects.
|
|
133
|
+
"""
|
|
134
|
+
if width is None and height is None:
|
|
135
|
+
return self.width, self.height
|
|
136
|
+
|
|
137
|
+
if width is None:
|
|
138
|
+
assert height is not None
|
|
139
|
+
scaling_factor = float(height) / float(self.height)
|
|
140
|
+
width = round(self.width * scaling_factor)
|
|
141
|
+
|
|
142
|
+
if height is None:
|
|
143
|
+
scaling_factor = float(width) / float(self.width)
|
|
144
|
+
height = round(self.height * scaling_factor)
|
|
145
|
+
|
|
146
|
+
return Emu(width), Emu(height)
|
|
147
|
+
|
|
148
|
+
@lazyproperty
|
|
149
|
+
def sha1(self):
|
|
150
|
+
"""SHA1 hash digest of the image blob."""
|
|
151
|
+
return hashlib.sha1(self._blob).hexdigest()
|
|
152
|
+
|
|
153
|
+
@classmethod
|
|
154
|
+
def _from_stream(
|
|
155
|
+
cls,
|
|
156
|
+
stream: IO[bytes],
|
|
157
|
+
blob: bytes,
|
|
158
|
+
filename: str | None = None,
|
|
159
|
+
) -> Image:
|
|
160
|
+
"""Return an instance of the |Image| subclass corresponding to the format of the
|
|
161
|
+
image in `stream`."""
|
|
162
|
+
image_header = _ImageHeaderFactory(stream)
|
|
163
|
+
if filename is None:
|
|
164
|
+
filename = "image.%s" % image_header.default_ext
|
|
165
|
+
return cls(blob, filename, image_header)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _ImageHeaderFactory(stream: IO[bytes]):
|
|
169
|
+
"""A |BaseImageHeader| subclass instance that can parse headers of image in `stream`."""
|
|
170
|
+
from docx.image import SIGNATURES
|
|
171
|
+
|
|
172
|
+
def read_32(stream: IO[bytes]):
|
|
173
|
+
stream.seek(0)
|
|
174
|
+
return stream.read(32)
|
|
175
|
+
|
|
176
|
+
header = read_32(stream)
|
|
177
|
+
for cls, offset, signature_bytes in SIGNATURES:
|
|
178
|
+
end = offset + len(signature_bytes)
|
|
179
|
+
found_bytes = header[offset:end]
|
|
180
|
+
if found_bytes == signature_bytes:
|
|
181
|
+
return cls.from_stream(stream)
|
|
182
|
+
raise UnrecognizedImageError
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class BaseImageHeader:
|
|
186
|
+
"""Base class for image header subclasses like |Jpeg| and |Tiff|."""
|
|
187
|
+
|
|
188
|
+
def __init__(self, px_width: int, px_height: int, horz_dpi: int, vert_dpi: int):
|
|
189
|
+
self._px_width = px_width
|
|
190
|
+
self._px_height = px_height
|
|
191
|
+
self._horz_dpi = horz_dpi
|
|
192
|
+
self._vert_dpi = vert_dpi
|
|
193
|
+
|
|
194
|
+
@property
|
|
195
|
+
def content_type(self) -> str:
|
|
196
|
+
"""Abstract property definition, must be implemented by all subclasses."""
|
|
197
|
+
msg = "content_type property must be implemented by all subclasses of BaseImageHeader"
|
|
198
|
+
raise NotImplementedError(msg)
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def default_ext(self) -> str:
|
|
202
|
+
"""Default filename extension for images of this type.
|
|
203
|
+
|
|
204
|
+
An abstract property definition, must be implemented by all subclasses.
|
|
205
|
+
"""
|
|
206
|
+
raise NotImplementedError(
|
|
207
|
+
"default_ext property must be implemented by all subclasses of BaseImageHeader"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
@property
|
|
211
|
+
def px_width(self):
|
|
212
|
+
"""The horizontal pixel dimension of the image."""
|
|
213
|
+
return self._px_width
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def px_height(self):
|
|
217
|
+
"""The vertical pixel dimension of the image."""
|
|
218
|
+
return self._px_height
|
|
219
|
+
|
|
220
|
+
@property
|
|
221
|
+
def horz_dpi(self):
|
|
222
|
+
"""Integer dots per inch for the width of this image.
|
|
223
|
+
|
|
224
|
+
Defaults to 72 when not present in the file, as is often the case.
|
|
225
|
+
"""
|
|
226
|
+
return self._horz_dpi
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def vert_dpi(self):
|
|
230
|
+
"""Integer dots per inch for the height of this image.
|
|
231
|
+
|
|
232
|
+
Defaults to 72 when not present in the file, as is often the case.
|
|
233
|
+
"""
|
|
234
|
+
return self._vert_dpi
|