python-docx-oss 0.1.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.
Files changed (132) hide show
  1. docx/__init__.py +71 -0
  2. docx/api.py +37 -0
  3. docx/blkcntnr.py +101 -0
  4. docx/comments.py +163 -0
  5. docx/dml/__init__.py +0 -0
  6. docx/dml/color.py +112 -0
  7. docx/document.py +285 -0
  8. docx/drawing/__init__.py +59 -0
  9. docx/enum/__init__.py +0 -0
  10. docx/enum/base.py +150 -0
  11. docx/enum/dml.py +103 -0
  12. docx/enum/section.py +86 -0
  13. docx/enum/shape.py +19 -0
  14. docx/enum/style.py +452 -0
  15. docx/enum/table.py +136 -0
  16. docx/enum/text.py +367 -0
  17. docx/exceptions.py +18 -0
  18. docx/image/__init__.py +32 -0
  19. docx/image/bmp.py +43 -0
  20. docx/image/constants.py +172 -0
  21. docx/image/emf.py +46 -0
  22. docx/image/exceptions.py +13 -0
  23. docx/image/gif.py +38 -0
  24. docx/image/helpers.py +86 -0
  25. docx/image/image.py +237 -0
  26. docx/image/jpeg.py +469 -0
  27. docx/image/png.py +253 -0
  28. docx/image/svg.py +118 -0
  29. docx/image/tiff.py +289 -0
  30. docx/opc/__init__.py +0 -0
  31. docx/opc/constants.py +307 -0
  32. docx/opc/coreprops.py +142 -0
  33. docx/opc/customprops.py +59 -0
  34. docx/opc/exceptions.py +12 -0
  35. docx/opc/oxml.py +247 -0
  36. docx/opc/package.py +254 -0
  37. docx/opc/packuri.py +109 -0
  38. docx/opc/part.py +267 -0
  39. docx/opc/parts/__init__.py +0 -0
  40. docx/opc/parts/coreprops.py +48 -0
  41. docx/opc/parts/customprops.py +61 -0
  42. docx/opc/phys_pkg.py +119 -0
  43. docx/opc/pkgreader.py +254 -0
  44. docx/opc/pkgwriter.py +115 -0
  45. docx/opc/rel.py +169 -0
  46. docx/opc/shared.py +31 -0
  47. docx/opc/spec.py +24 -0
  48. docx/oxml/__init__.py +252 -0
  49. docx/oxml/comments.py +124 -0
  50. docx/oxml/coreprops.py +298 -0
  51. docx/oxml/customprops.py +141 -0
  52. docx/oxml/dml/CT_ExtensionList.py +48 -0
  53. docx/oxml/document.py +88 -0
  54. docx/oxml/drawing.py +11 -0
  55. docx/oxml/exceptions.py +10 -0
  56. docx/oxml/ns.py +111 -0
  57. docx/oxml/numbering.py +109 -0
  58. docx/oxml/parser.py +62 -0
  59. docx/oxml/section.py +537 -0
  60. docx/oxml/settings.py +138 -0
  61. docx/oxml/shape.py +426 -0
  62. docx/oxml/shared.py +52 -0
  63. docx/oxml/simpletypes.py +434 -0
  64. docx/oxml/styles.py +320 -0
  65. docx/oxml/table.py +977 -0
  66. docx/oxml/text/__init__.py +0 -0
  67. docx/oxml/text/font.py +358 -0
  68. docx/oxml/text/hyperlink.py +45 -0
  69. docx/oxml/text/pagebreak.py +278 -0
  70. docx/oxml/text/paragraph.py +106 -0
  71. docx/oxml/text/parfmt.py +392 -0
  72. docx/oxml/text/run.py +316 -0
  73. docx/oxml/xmlchemy.py +711 -0
  74. docx/package.py +119 -0
  75. docx/parts/__init__.py +0 -0
  76. docx/parts/comments.py +51 -0
  77. docx/parts/customxml.py +67 -0
  78. docx/parts/document.py +193 -0
  79. docx/parts/hdrftr.py +53 -0
  80. docx/parts/image.py +80 -0
  81. docx/parts/numbering.py +51 -0
  82. docx/parts/settings.py +50 -0
  83. docx/parts/story.py +113 -0
  84. docx/parts/styles.py +42 -0
  85. docx/py.typed +0 -0
  86. docx/section.py +479 -0
  87. docx/settings.py +35 -0
  88. docx/shape.py +103 -0
  89. docx/shared.py +382 -0
  90. docx/styles/__init__.py +40 -0
  91. docx/styles/latent.py +198 -0
  92. docx/styles/style.py +254 -0
  93. docx/styles/styles.py +136 -0
  94. docx/table.py +537 -0
  95. docx/templates/default-comments.xml +12 -0
  96. docx/templates/default-docx-template/[Content_Types].xml +17 -0
  97. docx/templates/default-docx-template/_rels/.rels +7 -0
  98. docx/templates/default-docx-template/customXml/_rels/item1.xml.rels +4 -0
  99. docx/templates/default-docx-template/customXml/item1.xml +2 -0
  100. docx/templates/default-docx-template/customXml/itemProps1.xml +6 -0
  101. docx/templates/default-docx-template/docProps/app.xml +36 -0
  102. docx/templates/default-docx-template/docProps/core.xml +13 -0
  103. docx/templates/default-docx-template/docProps/thumbnail.jpeg +0 -0
  104. docx/templates/default-docx-template/word/_rels/document.xml.rels +11 -0
  105. docx/templates/default-docx-template/word/document.xml +11 -0
  106. docx/templates/default-docx-template/word/fontTable.xml +61 -0
  107. docx/templates/default-docx-template/word/numbering.xml +201 -0
  108. docx/templates/default-docx-template/word/settings.xml +53 -0
  109. docx/templates/default-docx-template/word/styles.xml +11844 -0
  110. docx/templates/default-docx-template/word/stylesWithEffects.xml +11800 -0
  111. docx/templates/default-docx-template/word/theme/theme1.xml +318 -0
  112. docx/templates/default-docx-template/word/webSettings.xml +5 -0
  113. docx/templates/default-footer.xml +27 -0
  114. docx/templates/default-header.xml +27 -0
  115. docx/templates/default-numbering.xml +20 -0
  116. docx/templates/default-settings.xml +26 -0
  117. docx/templates/default-styles.xml +190 -0
  118. docx/templates/default.docx +0 -0
  119. docx/text/__init__.py +0 -0
  120. docx/text/font.py +466 -0
  121. docx/text/hyperlink.py +121 -0
  122. docx/text/pagebreak.py +104 -0
  123. docx/text/paragraph.py +173 -0
  124. docx/text/parfmt.py +286 -0
  125. docx/text/run.py +272 -0
  126. docx/text/tabstops.py +123 -0
  127. docx/types.py +34 -0
  128. python_docx_oss-0.1.0.dist-info/METADATA +57 -0
  129. python_docx_oss-0.1.0.dist-info/RECORD +132 -0
  130. python_docx_oss-0.1.0.dist-info/WHEEL +5 -0
  131. python_docx_oss-0.1.0.dist-info/licenses/LICENSE +21 -0
  132. python_docx_oss-0.1.0.dist-info/top_level.txt +1 -0
docx/package.py ADDED
@@ -0,0 +1,119 @@
1
+ """WordprocessingML Package class and related objects."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import IO, TYPE_CHECKING, cast
6
+
7
+ from docx.image.image import Image
8
+ from docx.opc.constants import RELATIONSHIP_TYPE as RT
9
+ from docx.opc.package import OpcPackage
10
+ from docx.opc.packuri import PackURI
11
+ from docx.parts.image import ImagePart
12
+ from docx.shared import lazyproperty
13
+
14
+ if TYPE_CHECKING:
15
+ from docx.opc.customprops import CustomProperties
16
+
17
+
18
+ class Package(OpcPackage):
19
+ """Customizations specific to a WordprocessingML package."""
20
+
21
+ def after_unmarshal(self):
22
+ """Called by loading code after all parts and relationships have been loaded.
23
+
24
+ This method affords the opportunity for any required post-processing.
25
+ """
26
+ self._gather_image_parts()
27
+
28
+ @property
29
+ def custom_properties(self) -> CustomProperties:
30
+ """|CustomProperties| object providing read/write access to the custom
31
+ properties for this document."""
32
+ return super().custom_properties
33
+
34
+ def get_or_add_image_part(self, image_descriptor: str | IO[bytes]) -> ImagePart:
35
+ """Return |ImagePart| containing image specified by `image_descriptor`.
36
+
37
+ The image-part is newly created if a matching one is not already present in the
38
+ collection.
39
+ """
40
+ return self.image_parts.get_or_add_image_part(image_descriptor)
41
+
42
+ @lazyproperty
43
+ def image_parts(self) -> ImageParts:
44
+ """|ImageParts| collection object for this package."""
45
+ return ImageParts()
46
+
47
+ def _gather_image_parts(self):
48
+ """Load the image part collection with all the image parts in package."""
49
+ for rel in self.iter_rels():
50
+ if rel.is_external:
51
+ continue
52
+ if rel.reltype != RT.IMAGE:
53
+ continue
54
+ if rel.target_part in self.image_parts:
55
+ continue
56
+ self.image_parts.append(cast("ImagePart", rel.target_part))
57
+
58
+
59
+ class ImageParts:
60
+ """Collection of |ImagePart| objects corresponding to images in the package."""
61
+
62
+ def __init__(self):
63
+ self._image_parts: list[ImagePart] = []
64
+
65
+ def __contains__(self, item: object):
66
+ return self._image_parts.__contains__(item)
67
+
68
+ def __iter__(self):
69
+ return self._image_parts.__iter__()
70
+
71
+ def __len__(self):
72
+ return self._image_parts.__len__()
73
+
74
+ def append(self, item: ImagePart):
75
+ self._image_parts.append(item)
76
+
77
+ def get_or_add_image_part(self, image_descriptor: str | IO[bytes]) -> ImagePart:
78
+ """Return |ImagePart| object containing image identified by `image_descriptor`.
79
+
80
+ The image-part is newly created if a matching one is not present in the
81
+ collection.
82
+ """
83
+ image = Image.from_file(image_descriptor)
84
+ matching_image_part = self._get_by_sha1(image.sha1)
85
+ if matching_image_part is not None:
86
+ return matching_image_part
87
+ return self._add_image_part(image)
88
+
89
+ def _add_image_part(self, image: Image):
90
+ """Return |ImagePart| instance newly created from `image` and appended to the collection."""
91
+ partname = self._next_image_partname(image.ext)
92
+ image_part = ImagePart.from_image(image, partname)
93
+ self.append(image_part)
94
+ return image_part
95
+
96
+ def _get_by_sha1(self, sha1: str) -> ImagePart | None:
97
+ """Return the image part in this collection having a SHA1 hash matching `sha1`,
98
+ or |None| if not found."""
99
+ for image_part in self._image_parts:
100
+ if image_part.sha1 == sha1:
101
+ return image_part
102
+ return None
103
+
104
+ def _next_image_partname(self, ext: str) -> PackURI:
105
+ """The next available image partname, starting from ``/word/media/image1.{ext}``
106
+ where unused numbers are reused.
107
+
108
+ The partname is unique by number, without regard to the extension. `ext` does
109
+ not include the leading period.
110
+ """
111
+
112
+ def image_partname(n: int) -> PackURI:
113
+ return PackURI("/word/media/image%d.%s" % (n, ext))
114
+
115
+ used_numbers = [image_part.partname.idx for image_part in self]
116
+ for n in range(1, len(self) + 1):
117
+ if n not in used_numbers:
118
+ return image_partname(n)
119
+ return image_partname(len(self) + 1)
docx/parts/__init__.py ADDED
File without changes
docx/parts/comments.py ADDED
@@ -0,0 +1,51 @@
1
+ """Contains comments added to the document."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from typing import TYPE_CHECKING, cast
7
+
8
+ from typing_extensions import Self
9
+
10
+ from docx.comments import Comments
11
+ from docx.opc.constants import CONTENT_TYPE as CT
12
+ from docx.opc.packuri import PackURI
13
+ from docx.oxml.comments import CT_Comments
14
+ from docx.oxml.parser import parse_xml
15
+ from docx.package import Package
16
+ from docx.parts.story import StoryPart
17
+
18
+ if TYPE_CHECKING:
19
+ from docx.oxml.comments import CT_Comments
20
+ from docx.package import Package
21
+
22
+
23
+ class CommentsPart(StoryPart):
24
+ """Container part for comments added to the document."""
25
+
26
+ def __init__(
27
+ self, partname: PackURI, content_type: str, element: CT_Comments, package: Package
28
+ ):
29
+ super().__init__(partname, content_type, element, package)
30
+ self._comments = element
31
+
32
+ @property
33
+ def comments(self) -> Comments:
34
+ """A |Comments| proxy object for the `w:comments` root element of this part."""
35
+ return Comments(self._comments, self)
36
+
37
+ @classmethod
38
+ def default(cls, package: Package) -> Self:
39
+ """A newly created comments part, containing a default empty `w:comments` element."""
40
+ partname = PackURI("/word/comments.xml")
41
+ content_type = CT.WML_COMMENTS
42
+ element = cast("CT_Comments", parse_xml(cls._default_comments_xml()))
43
+ return cls(partname, content_type, element, package)
44
+
45
+ @classmethod
46
+ def _default_comments_xml(cls) -> bytes:
47
+ """A byte-string containing XML for a default comments part."""
48
+ path = os.path.join(os.path.split(__file__)[0], "..", "templates", "default-comments.xml")
49
+ with open(path, "rb") as f:
50
+ xml_bytes = f.read()
51
+ return xml_bytes
@@ -0,0 +1,67 @@
1
+ """
2
+ |CustomXmlPart| and closely related objects
3
+ """
4
+ from lxml import etree
5
+ from lxml.etree import fromstring
6
+
7
+ from docx.opc.package import OpcPackage
8
+ from docx.opc.packuri import PackURI
9
+ from docx.opc.part import XmlPart
10
+
11
+
12
+ class CustomXmlPart(XmlPart):
13
+ """
14
+ Proxy for the /customXml/*.xml part containing custom items definitions
15
+ """
16
+
17
+ @classmethod
18
+ def default(
19
+ cls,
20
+ file_name: str,
21
+ content_type: str,
22
+ xml: str,
23
+ package: OpcPackage,
24
+ ) -> "CustomXmlPart":
25
+ """
26
+ Create a default xml file in /customXml/*file_name*.xml
27
+ with default content *xml* and type *content_type*
28
+ """
29
+ return cls(
30
+ PackURI(f"/customXml/{file_name}.xml"),
31
+ content_type,
32
+ fromstring(xml),
33
+ package,
34
+ )
35
+
36
+ @property
37
+ def tag(self) -> str:
38
+ """
39
+ Return the root element tag name.
40
+ """
41
+ return self.element.tag
42
+
43
+ @property
44
+ def attrib(self) -> dict:
45
+ """
46
+ Return the root element attributes.
47
+ """
48
+ return self.element.attrib
49
+
50
+ @property
51
+ def items(self) -> list[etree._Element]:
52
+ """
53
+ Return all children belong to root element.
54
+ """
55
+ return self.element.getchildren()
56
+
57
+ def add_item(self, tag: str, text: str = "", **attrib):
58
+ """
59
+ Add a child element to root.
60
+ """
61
+ etree.SubElement(self.element, tag, **attrib).text = text
62
+
63
+ def delete_item(self, item_element: etree._Element):
64
+ """
65
+ Delete a item element. other nodes index may change after deletion.
66
+ """
67
+ self.element.remove(item_element)
docx/parts/document.py ADDED
@@ -0,0 +1,193 @@
1
+ """|DocumentPart| and closely related objects."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import IO, TYPE_CHECKING, cast
6
+
7
+ from docx.document import Document
8
+ from docx.opc.constants import CONTENT_TYPE as CT
9
+ from docx.opc.constants import RELATIONSHIP_TYPE as RT
10
+ from docx.parts.comments import CommentsPart
11
+ from docx.parts.customxml import CustomXmlPart
12
+ from docx.parts.hdrftr import FooterPart, HeaderPart
13
+ from docx.parts.numbering import NumberingPart
14
+ from docx.parts.settings import SettingsPart
15
+ from docx.parts.story import StoryPart
16
+ from docx.parts.styles import StylesPart
17
+ from docx.shape import InlineShapes
18
+ from docx.shared import lazyproperty
19
+
20
+ if TYPE_CHECKING:
21
+ from docx.comments import Comments
22
+ from docx.enum.style import WD_STYLE_TYPE
23
+ from docx.opc.coreprops import CoreProperties
24
+ from docx.opc.customprops import CustomProperties
25
+ from docx.settings import Settings
26
+ from docx.styles.style import BaseStyle
27
+
28
+
29
+ class DocumentPart(StoryPart):
30
+ """Main document part of a WordprocessingML (WML) package, aka a .docx file.
31
+
32
+ Acts as broker to other parts such as image, core properties, and style parts. It
33
+ also acts as a convenient delegate when a mid-document object needs a service
34
+ involving a remote ancestor. The `Parented.part` property inherited by many content
35
+ objects provides access to this part object for that purpose.
36
+ """
37
+
38
+ def add_custom_xml_part(
39
+ self, xml: str, file_name: str = "item1", content_type: str = CT.XML
40
+ ) -> CustomXmlPart:
41
+ """Add a file in /customXml/*file_name*.xml with default content *xml*."""
42
+ part = CustomXmlPart.default(file_name, content_type, xml, self.package)
43
+ return self.rels.get_or_add(RT.CUSTOM_XML, part).target_part
44
+
45
+ def add_footer_part(self):
46
+ """Return (footer_part, rId) pair for newly-created footer part."""
47
+ footer_part = FooterPart.new(self.package)
48
+ rId = self.relate_to(footer_part, RT.FOOTER)
49
+ return footer_part, rId
50
+
51
+ def add_header_part(self):
52
+ """Return (header_part, rId) pair for newly-created header part."""
53
+ header_part = HeaderPart.new(self.package)
54
+ rId = self.relate_to(header_part, RT.HEADER)
55
+ return header_part, rId
56
+
57
+ @property
58
+ def comments(self) -> Comments:
59
+ """|Comments| object providing access to the comments added to this document."""
60
+ return self._comments_part.comments
61
+
62
+ @property
63
+ def core_properties(self) -> CoreProperties:
64
+ """A |CoreProperties| object providing read/write access to the core properties
65
+ of this document."""
66
+ return self.package.core_properties
67
+
68
+ @property
69
+ def custom_properties(self) -> CustomProperties:
70
+ """A |CustomProperties| object providing read/write access to the custom
71
+ properties of this document."""
72
+ return self.package.custom_properties
73
+
74
+ @property
75
+ def custom_xml_parts(self) -> list[CustomXmlPart]:
76
+ """A list of |CustomXmlPart| objects for this document."""
77
+ try:
78
+ return self.rels.parts_with_reltype(RT.CUSTOM_XML)
79
+ except KeyError:
80
+ return []
81
+
82
+ @property
83
+ def document(self):
84
+ """A |Document| object providing access to the content of this document."""
85
+ return Document(self._element, self)
86
+
87
+ def drop_header_part(self, rId: str) -> None:
88
+ """Remove related header part identified by `rId`."""
89
+ self.drop_rel(rId)
90
+
91
+ def footer_part(self, rId: str):
92
+ """Return |FooterPart| related by `rId`."""
93
+ return self.related_parts[rId]
94
+
95
+ def get_style(self, style_id: str | None, style_type: WD_STYLE_TYPE) -> BaseStyle:
96
+ """Return the style in this document matching `style_id`.
97
+
98
+ Returns the default style for `style_type` if `style_id` is |None| or does not
99
+ match a defined style of `style_type`.
100
+ """
101
+ return self.styles.get_by_id(style_id, style_type)
102
+
103
+ def get_style_id(self, style_or_name, style_type):
104
+ """Return the style_id (|str|) of the style of `style_type` matching
105
+ `style_or_name`.
106
+
107
+ Returns |None| if the style resolves to the default style for `style_type` or if
108
+ `style_or_name` is itself |None|. Raises if `style_or_name` is a style of the
109
+ wrong type or names a style not present in the document.
110
+ """
111
+ return self.styles.get_style_id(style_or_name, style_type)
112
+
113
+ def header_part(self, rId: str):
114
+ """Return |HeaderPart| related by `rId`."""
115
+ return self.related_parts[rId]
116
+
117
+ @lazyproperty
118
+ def inline_shapes(self):
119
+ """The |InlineShapes| instance containing the inline shapes in the document."""
120
+ return InlineShapes(self._element.body, self)
121
+
122
+ @lazyproperty
123
+ def numbering_part(self) -> NumberingPart:
124
+ """A |NumberingPart| object providing access to the numbering definitions for this document.
125
+
126
+ Creates an empty numbering part if one is not present.
127
+ """
128
+ try:
129
+ return cast(NumberingPart, self.part_related_by(RT.NUMBERING))
130
+ except KeyError:
131
+ numbering_part = NumberingPart.default(self.package)
132
+ self.relate_to(numbering_part, RT.NUMBERING)
133
+ return numbering_part
134
+
135
+ def save(self, path_or_stream: str | IO[bytes]):
136
+ """Save this document to `path_or_stream`, which can be either a path to a
137
+ filesystem location (a string) or a file-like object."""
138
+ self.package.save(path_or_stream)
139
+
140
+ @property
141
+ def settings(self) -> Settings:
142
+ """A |Settings| object providing access to the settings in the settings part of
143
+ this document."""
144
+ return self._settings_part.settings
145
+
146
+ @property
147
+ def styles(self):
148
+ """A |Styles| object providing access to the styles in the styles part of this
149
+ document."""
150
+ return self._styles_part.styles
151
+
152
+ @property
153
+ def _comments_part(self) -> CommentsPart:
154
+ """A |CommentsPart| object providing access to the comments added to this document.
155
+
156
+ Creates a default comments part if one is not present.
157
+ """
158
+ try:
159
+ return cast(CommentsPart, self.part_related_by(RT.COMMENTS))
160
+ except KeyError:
161
+ assert self.package is not None
162
+ comments_part = CommentsPart.default(self.package)
163
+ self.relate_to(comments_part, RT.COMMENTS)
164
+ return comments_part
165
+
166
+ @property
167
+ def _settings_part(self) -> SettingsPart:
168
+ """A |SettingsPart| object providing access to the document-level settings for
169
+ this document.
170
+
171
+ Creates a default settings part if one is not present.
172
+ """
173
+ try:
174
+ return cast(SettingsPart, self.part_related_by(RT.SETTINGS))
175
+ except KeyError:
176
+ settings_part = SettingsPart.default(self.package)
177
+ self.relate_to(settings_part, RT.SETTINGS)
178
+ return settings_part
179
+
180
+ @property
181
+ def _styles_part(self) -> StylesPart:
182
+ """Instance of |StylesPart| for this document.
183
+
184
+ Creates an empty styles part if one is not present.
185
+ """
186
+ try:
187
+ return cast(StylesPart, self.part_related_by(RT.STYLES))
188
+ except KeyError:
189
+ package = self.package
190
+ assert package is not None
191
+ styles_part = StylesPart.default(package)
192
+ self.relate_to(styles_part, RT.STYLES)
193
+ return styles_part
docx/parts/hdrftr.py ADDED
@@ -0,0 +1,53 @@
1
+ """Header and footer part 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.oxml.parser import parse_xml
10
+ from docx.parts.story import StoryPart
11
+
12
+ if TYPE_CHECKING:
13
+ from docx.package import Package
14
+
15
+
16
+ class FooterPart(StoryPart):
17
+ """Definition of a section footer."""
18
+
19
+ @classmethod
20
+ def new(cls, package: Package):
21
+ """Return newly created footer part."""
22
+ partname = package.next_partname("/word/footer%d.xml")
23
+ content_type = CT.WML_FOOTER
24
+ element = parse_xml(cls._default_footer_xml())
25
+ return cls(partname, content_type, element, package)
26
+
27
+ @classmethod
28
+ def _default_footer_xml(cls):
29
+ """Return bytes containing XML for a default footer part."""
30
+ path = os.path.join(os.path.split(__file__)[0], "..", "templates", "default-footer.xml")
31
+ with open(path, "rb") as f:
32
+ xml_bytes = f.read()
33
+ return xml_bytes
34
+
35
+
36
+ class HeaderPart(StoryPart):
37
+ """Definition of a section header."""
38
+
39
+ @classmethod
40
+ def new(cls, package: Package):
41
+ """Return newly created header part."""
42
+ partname = package.next_partname("/word/header%d.xml")
43
+ content_type = CT.WML_HEADER
44
+ element = parse_xml(cls._default_header_xml())
45
+ return cls(partname, content_type, element, package)
46
+
47
+ @classmethod
48
+ def _default_header_xml(cls):
49
+ """Return bytes containing XML for a default header part."""
50
+ path = os.path.join(os.path.split(__file__)[0], "..", "templates", "default-header.xml")
51
+ with open(path, "rb") as f:
52
+ xml_bytes = f.read()
53
+ return xml_bytes
docx/parts/image.py ADDED
@@ -0,0 +1,80 @@
1
+ """The proxy class for an image part, and related objects."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ from typing import TYPE_CHECKING
7
+
8
+ from docx.image.image import Image
9
+ from docx.opc.part import Part
10
+ from docx.shared import Emu, Inches
11
+
12
+ if TYPE_CHECKING:
13
+ from docx.opc.package import OpcPackage
14
+ from docx.opc.packuri import PackURI
15
+
16
+
17
+ class ImagePart(Part):
18
+ """An image part.
19
+
20
+ Corresponds to the target part of a relationship with type RELATIONSHIP_TYPE.IMAGE.
21
+ """
22
+
23
+ def __init__(
24
+ self, partname: PackURI, content_type: str, blob: bytes, image: Image | None = None
25
+ ):
26
+ super(ImagePart, self).__init__(partname, content_type, blob)
27
+ self._image = image
28
+
29
+ @property
30
+ def default_cx(self):
31
+ """Native width of this image, calculated from its width in pixels and
32
+ horizontal dots per inch (dpi)."""
33
+ px_width = self.image.px_width
34
+ horz_dpi = self.image.horz_dpi
35
+ width_in_inches = px_width / horz_dpi
36
+ return Inches(width_in_inches)
37
+
38
+ @property
39
+ def default_cy(self):
40
+ """Native height of this image, calculated from its height in pixels and
41
+ vertical dots per inch (dpi)."""
42
+ px_height = self.image.px_height
43
+ horz_dpi = self.image.horz_dpi
44
+ height_in_emu = int(round(914400 * px_height / horz_dpi))
45
+ return Emu(height_in_emu)
46
+
47
+ @property
48
+ def filename(self):
49
+ """Filename from which this image part was originally created.
50
+
51
+ A generic name, e.g. 'image.png', is substituted if no name is available, for
52
+ example when the image was loaded from an unnamed stream. In that case a default
53
+ extension is applied based on the detected MIME type of the image.
54
+ """
55
+ if self._image is not None:
56
+ return self._image.filename
57
+ return "image.%s" % self.partname.ext
58
+
59
+ @classmethod
60
+ def from_image(cls, image: Image, partname: PackURI):
61
+ """Return an |ImagePart| instance newly created from `image` and assigned
62
+ `partname`."""
63
+ return ImagePart(partname, image.content_type, image.blob, image)
64
+
65
+ @property
66
+ def image(self) -> Image:
67
+ if self._image is None:
68
+ self._image = Image.from_blob(self.blob)
69
+ return self._image
70
+
71
+ @classmethod
72
+ def load(cls, partname: PackURI, content_type: str, blob: bytes, package: OpcPackage):
73
+ """Called by ``docx.opc.package.PartFactory`` to load an image part from a
74
+ package being opened by ``Document(...)`` call."""
75
+ return cls(partname, content_type, blob)
76
+
77
+ @property
78
+ def sha1(self):
79
+ """SHA1 hash digest of the blob of this image part."""
80
+ return hashlib.sha1(self.blob).hexdigest()
@@ -0,0 +1,51 @@
1
+ """|NumberingPart| and closely related objects."""
2
+
3
+ import os
4
+
5
+ from ..opc.constants import CONTENT_TYPE as CT
6
+ from ..opc.packuri import PackURI
7
+ from ..opc.part import XmlPart
8
+ from ..oxml import parse_xml
9
+ from ..shared import lazyproperty
10
+
11
+
12
+ class NumberingPart(XmlPart):
13
+ """Proxy for the numbering.xml part containing numbering definitions for a document
14
+ or glossary."""
15
+
16
+ @classmethod
17
+ def default(cls, package):
18
+ """Return newly created empty numbering part, containing only the root ``<w:numbering>``
19
+ element."""
20
+ partname = PackURI("/word/numbering.xml")
21
+ content_type = CT.WML_NUMBERING
22
+ element = parse_xml(cls._default_numbering_xml())
23
+ return cls(partname, content_type, element, package)
24
+
25
+ @classmethod
26
+ def _default_numbering_xml(cls):
27
+ """Return a bytestream containing XML for a default numbering part."""
28
+ path = os.path.join(
29
+ os.path.split(__file__)[0], "..", "templates", "default-numbering.xml"
30
+ )
31
+ with open(path, "rb") as f:
32
+ xml_bytes = f.read()
33
+ return xml_bytes
34
+
35
+ @lazyproperty
36
+ def numbering_definitions(self):
37
+ """The |_NumberingDefinitions| instance containing the numbering definitions
38
+ (<w:num> element proxies) for this numbering part."""
39
+ return _NumberingDefinitions(self._element)
40
+
41
+
42
+ class _NumberingDefinitions:
43
+ """Collection of |_NumberingDefinition| instances corresponding to the ``<w:num>``
44
+ elements in a numbering part."""
45
+
46
+ def __init__(self, numbering_elm):
47
+ super(_NumberingDefinitions, self).__init__()
48
+ self._numbering = numbering_elm
49
+
50
+ def __len__(self):
51
+ return len(self._numbering.num_lst)
docx/parts/settings.py ADDED
@@ -0,0 +1,50 @@
1
+ """|SettingsPart| and closely related objects."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from typing import TYPE_CHECKING, cast
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.settings import Settings
13
+
14
+ if TYPE_CHECKING:
15
+ from docx.oxml.settings import CT_Settings
16
+ from docx.package import Package
17
+
18
+
19
+ class SettingsPart(XmlPart):
20
+ """Document-level settings part of a WordprocessingML (WML) package."""
21
+
22
+ def __init__(
23
+ self, partname: PackURI, content_type: str, element: CT_Settings, package: Package
24
+ ):
25
+ super().__init__(partname, content_type, element, package)
26
+ self._settings = element
27
+
28
+ @classmethod
29
+ def default(cls, package: Package):
30
+ """Return a newly created settings part, containing a default `w:settings` element tree."""
31
+ partname = PackURI("/word/settings.xml")
32
+ content_type = CT.WML_SETTINGS
33
+ element = cast("CT_Settings", parse_xml(cls._default_settings_xml()))
34
+ return cls(partname, content_type, element, package)
35
+
36
+ @property
37
+ def settings(self) -> Settings:
38
+ """A |Settings| proxy object for the `w:settings` element in this part.
39
+
40
+ Contains the document-level settings for this document.
41
+ """
42
+ return Settings(self._settings)
43
+
44
+ @classmethod
45
+ def _default_settings_xml(cls):
46
+ """Return a bytestream containing XML for a default settings part."""
47
+ path = os.path.join(os.path.split(__file__)[0], "..", "templates", "default-settings.xml")
48
+ with open(path, "rb") as f:
49
+ xml_bytes = f.read()
50
+ return xml_bytes