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.
Files changed (126) hide show
  1. docx/__init__.py +68 -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 +275 -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 +23 -0
  19. docx/image/bmp.py +43 -0
  20. docx/image/constants.py +172 -0
  21. docx/image/exceptions.py +13 -0
  22. docx/image/gif.py +38 -0
  23. docx/image/helpers.py +86 -0
  24. docx/image/image.py +234 -0
  25. docx/image/jpeg.py +425 -0
  26. docx/image/png.py +253 -0
  27. docx/image/tiff.py +289 -0
  28. docx/opc/__init__.py +0 -0
  29. docx/opc/constants.py +306 -0
  30. docx/opc/coreprops.py +142 -0
  31. docx/opc/exceptions.py +12 -0
  32. docx/opc/oxml.py +247 -0
  33. docx/opc/package.py +219 -0
  34. docx/opc/packuri.py +109 -0
  35. docx/opc/part.py +247 -0
  36. docx/opc/parts/__init__.py +0 -0
  37. docx/opc/parts/coreprops.py +48 -0
  38. docx/opc/phys_pkg.py +119 -0
  39. docx/opc/pkgreader.py +254 -0
  40. docx/opc/pkgwriter.py +115 -0
  41. docx/opc/rel.py +153 -0
  42. docx/opc/shared.py +31 -0
  43. docx/opc/spec.py +24 -0
  44. docx/oxml/__init__.py +261 -0
  45. docx/oxml/comments.py +124 -0
  46. docx/oxml/coreprops.py +298 -0
  47. docx/oxml/document.py +88 -0
  48. docx/oxml/drawing.py +11 -0
  49. docx/oxml/exceptions.py +10 -0
  50. docx/oxml/ns.py +109 -0
  51. docx/oxml/numbering.py +109 -0
  52. docx/oxml/parser.py +62 -0
  53. docx/oxml/section.py +537 -0
  54. docx/oxml/settings.py +138 -0
  55. docx/oxml/shape.py +299 -0
  56. docx/oxml/shared.py +52 -0
  57. docx/oxml/simpletypes.py +434 -0
  58. docx/oxml/styles.py +341 -0
  59. docx/oxml/table.py +977 -0
  60. docx/oxml/text/__init__.py +0 -0
  61. docx/oxml/text/font.py +333 -0
  62. docx/oxml/text/hyperlink.py +45 -0
  63. docx/oxml/text/pagebreak.py +278 -0
  64. docx/oxml/text/paragraph.py +106 -0
  65. docx/oxml/text/parfmt.py +392 -0
  66. docx/oxml/text/run.py +307 -0
  67. docx/oxml/xmlchemy.py +696 -0
  68. docx/package.py +110 -0
  69. docx/parts/__init__.py +0 -0
  70. docx/parts/comments.py +51 -0
  71. docx/parts/document.py +182 -0
  72. docx/parts/hdrftr.py +53 -0
  73. docx/parts/image.py +80 -0
  74. docx/parts/numbering.py +32 -0
  75. docx/parts/settings.py +50 -0
  76. docx/parts/story.py +95 -0
  77. docx/parts/styles.py +42 -0
  78. docx/parts/theme.py +53 -0
  79. docx/py.typed +0 -0
  80. docx/section.py +479 -0
  81. docx/settings.py +35 -0
  82. docx/shape.py +103 -0
  83. docx/shared.py +382 -0
  84. docx/styles/__init__.py +40 -0
  85. docx/styles/latent.py +198 -0
  86. docx/styles/style.py +264 -0
  87. docx/styles/styles.py +147 -0
  88. docx/table.py +537 -0
  89. docx/templates/default-comments.xml +12 -0
  90. docx/templates/default-docx-template/[Content_Types].xml +17 -0
  91. docx/templates/default-docx-template/_rels/.rels +7 -0
  92. docx/templates/default-docx-template/customXml/_rels/item1.xml.rels +4 -0
  93. docx/templates/default-docx-template/customXml/item1.xml +2 -0
  94. docx/templates/default-docx-template/customXml/itemProps1.xml +6 -0
  95. docx/templates/default-docx-template/docProps/app.xml +36 -0
  96. docx/templates/default-docx-template/docProps/core.xml +13 -0
  97. docx/templates/default-docx-template/docProps/thumbnail.jpeg +0 -0
  98. docx/templates/default-docx-template/word/_rels/document.xml.rels +11 -0
  99. docx/templates/default-docx-template/word/document.xml +11 -0
  100. docx/templates/default-docx-template/word/fontTable.xml +61 -0
  101. docx/templates/default-docx-template/word/numbering.xml +201 -0
  102. docx/templates/default-docx-template/word/settings.xml +53 -0
  103. docx/templates/default-docx-template/word/styles.xml +11844 -0
  104. docx/templates/default-docx-template/word/stylesWithEffects.xml +11800 -0
  105. docx/templates/default-docx-template/word/theme/theme1.xml +318 -0
  106. docx/templates/default-docx-template/word/webSettings.xml +5 -0
  107. docx/templates/default-footer.xml +27 -0
  108. docx/templates/default-header.xml +27 -0
  109. docx/templates/default-settings.xml +26 -0
  110. docx/templates/default-styles.xml +190 -0
  111. docx/templates/default.docx +0 -0
  112. docx/text/__init__.py +0 -0
  113. docx/text/font.py +472 -0
  114. docx/text/hyperlink.py +121 -0
  115. docx/text/pagebreak.py +104 -0
  116. docx/text/paragraph.py +173 -0
  117. docx/text/parfmt.py +286 -0
  118. docx/text/run.py +257 -0
  119. docx/text/tabstops.py +123 -0
  120. docx/theme.py +67 -0
  121. docx/types.py +34 -0
  122. ps_python_docx-1.3.0.dist-info/METADATA +77 -0
  123. ps_python_docx-1.3.0.dist-info/RECORD +126 -0
  124. ps_python_docx-1.3.0.dist-info/WHEEL +5 -0
  125. ps_python_docx-1.3.0.dist-info/licenses/LICENSE +20 -0
  126. ps_python_docx-1.3.0.dist-info/top_level.txt +1 -0
docx/__init__.py ADDED
@@ -0,0 +1,68 @@
1
+ """Initialize `docx` package.
2
+
3
+ Export the `Document` constructor function and establish the mapping of part-type to
4
+ the part-classe that implements that type.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import TYPE_CHECKING, Type
10
+
11
+ from docx.api import Document
12
+
13
+ if TYPE_CHECKING:
14
+ from docx.opc.part import Part
15
+
16
+ __version__ = "1.3.0"
17
+
18
+
19
+ __all__ = ["Document"]
20
+
21
+
22
+ # -- register custom Part classes with opc package reader --
23
+
24
+ from docx.opc.constants import CONTENT_TYPE as CT
25
+ from docx.opc.constants import RELATIONSHIP_TYPE as RT
26
+ from docx.opc.part import PartFactory
27
+ from docx.opc.parts.coreprops import CorePropertiesPart
28
+ from docx.parts.comments import CommentsPart
29
+ from docx.parts.document import DocumentPart
30
+ from docx.parts.hdrftr import FooterPart, HeaderPart
31
+ from docx.parts.image import ImagePart
32
+ from docx.parts.numbering import NumberingPart
33
+ from docx.parts.settings import SettingsPart
34
+ from docx.parts.styles import StylesPart
35
+ from docx.parts.theme import ThemePart
36
+
37
+
38
+ def part_class_selector(content_type: str, reltype: str) -> Type[Part] | None:
39
+ if reltype == RT.IMAGE:
40
+ return ImagePart
41
+ return None
42
+
43
+
44
+ PartFactory.part_class_selector = part_class_selector
45
+ PartFactory.part_type_for[CT.OPC_CORE_PROPERTIES] = CorePropertiesPart
46
+ PartFactory.part_type_for[CT.WML_COMMENTS] = CommentsPart
47
+ PartFactory.part_type_for[CT.WML_DOCUMENT_MAIN] = DocumentPart
48
+ PartFactory.part_type_for[CT.WML_FOOTER] = FooterPart
49
+ PartFactory.part_type_for[CT.WML_HEADER] = HeaderPart
50
+ PartFactory.part_type_for[CT.WML_NUMBERING] = NumberingPart
51
+ PartFactory.part_type_for[CT.WML_SETTINGS] = SettingsPart
52
+ PartFactory.part_type_for[CT.WML_STYLES] = StylesPart
53
+ PartFactory.part_type_for[CT.OFC_THEME] = ThemePart
54
+
55
+ del (
56
+ CT,
57
+ CorePropertiesPart,
58
+ CommentsPart,
59
+ DocumentPart,
60
+ FooterPart,
61
+ HeaderPart,
62
+ NumberingPart,
63
+ PartFactory,
64
+ SettingsPart,
65
+ StylesPart,
66
+ ThemePart,
67
+ part_class_selector,
68
+ )
docx/api.py ADDED
@@ -0,0 +1,37 @@
1
+ """Directly exposed API functions and classes, :func:`Document` for now.
2
+
3
+ Provides a syntactically more convenient API for interacting with the OpcPackage graph.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import os
9
+ from typing import IO, TYPE_CHECKING, cast
10
+
11
+ from docx.opc.constants import CONTENT_TYPE as CT
12
+ from docx.package import Package
13
+
14
+ if TYPE_CHECKING:
15
+ from docx.document import Document as DocumentObject
16
+ from docx.parts.document import DocumentPart
17
+
18
+
19
+ def Document(docx: str | IO[bytes] | None = None) -> DocumentObject:
20
+ """Return a |Document| object loaded from `docx`, where `docx` can be either a path
21
+ to a ``.docx`` file (a string) or a file-like object.
22
+
23
+ If `docx` is missing or ``None``, the built-in default document "template" is
24
+ loaded.
25
+ """
26
+ docx = _default_docx_path() if docx is None else docx
27
+ document_part = cast("DocumentPart", Package.open(docx).main_document_part)
28
+ if document_part.content_type != CT.WML_DOCUMENT_MAIN:
29
+ tmpl = "file '%s' is not a Word file, content type is '%s'"
30
+ raise ValueError(tmpl % (docx, document_part.content_type))
31
+ return document_part.document
32
+
33
+
34
+ def _default_docx_path():
35
+ """Return the path to the built-in default .docx package."""
36
+ _thisdir = os.path.split(__file__)[0]
37
+ return os.path.join(_thisdir, "templates", "default.docx")
docx/blkcntnr.py ADDED
@@ -0,0 +1,101 @@
1
+ # pyright: reportImportCycles=false
2
+
3
+ """Block item container, used by body, cell, header, etc.
4
+
5
+ Block level items are things like paragraph and table, although there are a few other
6
+ specialized ones like structured document tags.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import TYPE_CHECKING, Iterator
12
+
13
+ from typing_extensions import TypeAlias
14
+
15
+ from docx.oxml.table import CT_Tbl
16
+ from docx.oxml.text.paragraph import CT_P
17
+ from docx.shared import StoryChild
18
+ from docx.text.paragraph import Paragraph
19
+
20
+ if TYPE_CHECKING:
21
+ import docx.types as t
22
+ from docx.oxml.comments import CT_Comment
23
+ from docx.oxml.document import CT_Body
24
+ from docx.oxml.section import CT_HdrFtr
25
+ from docx.oxml.table import CT_Tc
26
+ from docx.shared import Length
27
+ from docx.styles.style import ParagraphStyle
28
+ from docx.table import Table
29
+
30
+ BlockItemElement: TypeAlias = "CT_Body | CT_Comment | CT_HdrFtr | CT_Tc"
31
+
32
+
33
+ class BlockItemContainer(StoryChild):
34
+ """Base class for proxy objects that can contain block items.
35
+
36
+ These containers include _Body, _Cell, header, footer, footnote, endnote, comment,
37
+ and text box objects. Provides the shared functionality to add a block item like a
38
+ paragraph or table.
39
+ """
40
+
41
+ def __init__(self, element: BlockItemElement, parent: t.ProvidesStoryPart):
42
+ super(BlockItemContainer, self).__init__(parent)
43
+ self._element = element
44
+
45
+ def add_paragraph(self, text: str = "", style: str | ParagraphStyle | None = None) -> Paragraph:
46
+ """Return paragraph newly added to the end of the content in this container.
47
+
48
+ The paragraph has `text` in a single run if present, and is given paragraph
49
+ style `style`.
50
+
51
+ If `style` is |None|, no paragraph style is applied, which has the same effect
52
+ as applying the 'Normal' style.
53
+ """
54
+ paragraph = self._add_paragraph()
55
+ if text:
56
+ paragraph.add_run(text)
57
+ if style is not None:
58
+ paragraph.style = style
59
+ return paragraph
60
+
61
+ def add_table(self, rows: int, cols: int, width: Length) -> Table:
62
+ """Return table of `width` having `rows` rows and `cols` columns.
63
+
64
+ The table is appended appended at the end of the content in this container.
65
+
66
+ `width` is evenly distributed between the table columns.
67
+ """
68
+ from docx.table import Table
69
+
70
+ tbl = CT_Tbl.new_tbl(rows, cols, width)
71
+ self._element._insert_tbl(tbl) # pyright: ignore[reportPrivateUsage]
72
+ return Table(tbl, self)
73
+
74
+ def iter_inner_content(self) -> Iterator[Paragraph | Table]:
75
+ """Generate each `Paragraph` or `Table` in this container in document order."""
76
+ from docx.table import Table
77
+
78
+ for element in self._element.inner_content_elements:
79
+ yield (Paragraph(element, self) if isinstance(element, CT_P) else Table(element, self))
80
+
81
+ @property
82
+ def paragraphs(self):
83
+ """A list containing the paragraphs in this container, in document order.
84
+
85
+ Read-only.
86
+ """
87
+ return [Paragraph(p, self) for p in self._element.p_lst]
88
+
89
+ @property
90
+ def tables(self):
91
+ """A list containing the tables in this container, in document order.
92
+
93
+ Read-only.
94
+ """
95
+ from docx.table import Table
96
+
97
+ return [Table(tbl, self) for tbl in self._element.tbl_lst]
98
+
99
+ def _add_paragraph(self):
100
+ """Return paragraph newly added to the end of the content in this container."""
101
+ return Paragraph(self._element.add_p(), self)
docx/comments.py ADDED
@@ -0,0 +1,163 @@
1
+ """Collection providing access to comments added to this document."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import datetime as dt
6
+ from typing import TYPE_CHECKING, Iterator
7
+
8
+ from docx.blkcntnr import BlockItemContainer
9
+
10
+ if TYPE_CHECKING:
11
+ from docx.oxml.comments import CT_Comment, CT_Comments
12
+ from docx.parts.comments import CommentsPart
13
+ from docx.styles.style import ParagraphStyle
14
+ from docx.text.paragraph import Paragraph
15
+
16
+
17
+ class Comments:
18
+ """Collection containing the comments added to this document."""
19
+
20
+ def __init__(self, comments_elm: CT_Comments, comments_part: CommentsPart):
21
+ self._comments_elm = comments_elm
22
+ self._comments_part = comments_part
23
+
24
+ def __iter__(self) -> Iterator[Comment]:
25
+ """Iterator over the comments in this collection."""
26
+ return (
27
+ Comment(comment_elm, self._comments_part)
28
+ for comment_elm in self._comments_elm.comment_lst
29
+ )
30
+
31
+ def __len__(self) -> int:
32
+ """The number of comments in this collection."""
33
+ return len(self._comments_elm.comment_lst)
34
+
35
+ def add_comment(self, text: str = "", author: str = "", initials: str | None = "") -> Comment:
36
+ """Add a new comment to the document and return it.
37
+
38
+ The comment is added to the end of the comments collection and is assigned a unique
39
+ comment-id.
40
+
41
+ If `text` is provided, it is added to the comment. This option provides for the common
42
+ case where a comment contains a modest passage of plain text. Multiple paragraphs can be
43
+ added using the `text` argument by separating their text with newlines (`"\\\\n"`).
44
+ Between newlines, text is interpreted as it is in `Document.add_paragraph(text=...)`.
45
+
46
+ The default is to place a single empty paragraph in the comment, which is the same
47
+ behavior as the Word UI when you add a comment. New runs can be added to the first
48
+ paragraph in the empty comment with `comments.paragraphs[0].add_run()` to adding more
49
+ complex text with emphasis or images. Additional paragraphs can be added using
50
+ `.add_paragraph()`.
51
+
52
+ `author` is a required attribute, set to the empty string by default.
53
+
54
+ `initials` is an optional attribute, set to the empty string by default. Passing |None|
55
+ for the `initials` parameter causes that attribute to be omitted from the XML.
56
+ """
57
+ comment_elm = self._comments_elm.add_comment()
58
+ comment_elm.author = author
59
+ comment_elm.initials = initials
60
+ comment_elm.date = dt.datetime.now(dt.timezone.utc)
61
+ comment = Comment(comment_elm, self._comments_part)
62
+
63
+ if text == "":
64
+ return comment
65
+
66
+ para_text_iter = iter(text.split("\n"))
67
+
68
+ first_para_text = next(para_text_iter)
69
+ first_para = comment.paragraphs[0]
70
+ first_para.add_run(first_para_text)
71
+
72
+ for s in para_text_iter:
73
+ comment.add_paragraph(text=s)
74
+
75
+ return comment
76
+
77
+ def get(self, comment_id: int) -> Comment | None:
78
+ """Return the comment identified by `comment_id`, or |None| if not found."""
79
+ comment_elm = self._comments_elm.get_comment_by_id(comment_id)
80
+ return Comment(comment_elm, self._comments_part) if comment_elm is not None else None
81
+
82
+
83
+ class Comment(BlockItemContainer):
84
+ """Proxy for a single comment in the document.
85
+
86
+ Provides methods to access comment metadata such as author, initials, and date.
87
+
88
+ A comment is also a block-item container, similar to a table cell, so it can contain both
89
+ paragraphs and tables and its paragraphs can contain rich text, hyperlinks and images,
90
+ although the common case is that a comment contains a single paragraph of plain text like a
91
+ sentence or phrase.
92
+
93
+ Note that certain content like tables may not be displayed in the Word comment sidebar due to
94
+ space limitations. Such "over-sized" content can still be viewed in the review pane.
95
+ """
96
+
97
+ def __init__(self, comment_elm: CT_Comment, comments_part: CommentsPart):
98
+ super().__init__(comment_elm, comments_part)
99
+ self._comment_elm = comment_elm
100
+
101
+ def add_paragraph(self, text: str = "", style: str | ParagraphStyle | None = None) -> Paragraph:
102
+ """Return paragraph newly added to the end of the content in this container.
103
+
104
+ The paragraph has `text` in a single run if present, and is given paragraph style `style`.
105
+ When `style` is |None| or ommitted, the "CommentText" paragraph style is applied, which is
106
+ the default style for comments.
107
+ """
108
+ paragraph = super().add_paragraph(text, style)
109
+
110
+ # -- have to assign style directly to element because `paragraph.style` raises when
111
+ # -- a style is not present in the styles part
112
+ if style is None:
113
+ paragraph._p.style = "CommentText" # pyright: ignore[reportPrivateUsage]
114
+
115
+ return paragraph
116
+
117
+ @property
118
+ def author(self) -> str:
119
+ """Read/write. The recorded author of this comment.
120
+
121
+ This field is required but can be set to the empty string.
122
+ """
123
+ return self._comment_elm.author
124
+
125
+ @author.setter
126
+ def author(self, value: str):
127
+ self._comment_elm.author = value
128
+
129
+ @property
130
+ def comment_id(self) -> int:
131
+ """The unique identifier of this comment."""
132
+ return self._comment_elm.id
133
+
134
+ @property
135
+ def initials(self) -> str | None:
136
+ """Read/write. The recorded initials of the comment author.
137
+
138
+ This attribute is optional in the XML, returns |None| if not set. Assigning |None| removes
139
+ any existing initials from the XML.
140
+ """
141
+ return self._comment_elm.initials
142
+
143
+ @initials.setter
144
+ def initials(self, value: str | None):
145
+ self._comment_elm.initials = value
146
+
147
+ @property
148
+ def text(self) -> str:
149
+ """The text content of this comment as a string.
150
+
151
+ Only content in paragraphs is included and of course all emphasis and styling is stripped.
152
+
153
+ Paragraph boundaries are indicated with a newline (`"\\\\n"`)
154
+ """
155
+ return "\n".join(p.text for p in self.paragraphs)
156
+
157
+ @property
158
+ def timestamp(self) -> dt.datetime | None:
159
+ """The date and time this comment was authored.
160
+
161
+ This attribute is optional in the XML, returns |None| if not set.
162
+ """
163
+ return self._comment_elm.date
docx/dml/__init__.py ADDED
File without changes
docx/dml/color.py ADDED
@@ -0,0 +1,112 @@
1
+ """DrawingML objects related to color, ColorFormat being the most prominent."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, cast
6
+
7
+ from typing_extensions import TypeAlias
8
+
9
+ from docx.enum.dml import MSO_COLOR_TYPE
10
+ from docx.oxml.simpletypes import ST_HexColorAuto
11
+ from docx.shared import ElementProxy, RGBColor
12
+
13
+ if TYPE_CHECKING:
14
+ from docx.enum.dml import MSO_THEME_COLOR
15
+ from docx.oxml.text.font import CT_Color
16
+ from docx.oxml.text.run import CT_R
17
+
18
+ # -- other element types can be a parent of an `w:rPr` element, but for now only `w:r` is --
19
+ RPrParent: TypeAlias = "CT_R"
20
+
21
+
22
+ class ColorFormat(ElementProxy):
23
+ """Provides access to color settings like RGB color, theme color, and luminance adjustments."""
24
+
25
+ def __init__(self, rPr_parent: RPrParent):
26
+ super(ColorFormat, self).__init__(rPr_parent)
27
+ self._element = rPr_parent
28
+
29
+ @property
30
+ def rgb(self) -> RGBColor | None:
31
+ """An |RGBColor| value or |None| if no RGB color is specified.
32
+
33
+ When :attr:`type` is `MSO_COLOR_TYPE.RGB`, the value of this property will always be an
34
+ |RGBColor| value. It may also be an |RGBColor| value if :attr:`type` is
35
+ `MSO_COLOR_TYPE.THEME`, as Word writes the current value of a theme color when one is
36
+ assigned. In that case, the RGB value should be interpreted as no more than a good guess
37
+ however, as the theme color takes precedence at rendering time. Its value is |None|
38
+ whenever :attr:`type` is either |None| or `MSO_COLOR_TYPE.AUTO`.
39
+
40
+ Assigning an |RGBColor| value causes :attr:`type` to become `MSO_COLOR_TYPE.RGB` and any
41
+ theme color is removed. Assigning |None| causes any color to be removed such that the
42
+ effective color is inherited from the style hierarchy.
43
+ """
44
+ color = self._color
45
+ if color is None:
46
+ return None
47
+ if color.val == ST_HexColorAuto.AUTO:
48
+ return None
49
+ return cast(RGBColor, color.val)
50
+
51
+ @rgb.setter
52
+ def rgb(self, value: RGBColor | None):
53
+ if value is None and self._color is None:
54
+ return
55
+ rPr = self._element.get_or_add_rPr()
56
+ rPr._remove_color() # pyright: ignore[reportPrivateUsage]
57
+ if value is not None:
58
+ rPr.get_or_add_color().val = value
59
+
60
+ @property
61
+ def theme_color(self) -> MSO_THEME_COLOR | None:
62
+ """Member of :ref:`MsoThemeColorIndex` or |None| if no theme color is specified.
63
+
64
+ When :attr:`type` is `MSO_COLOR_TYPE.THEME`, the value of this property will always be a
65
+ member of :ref:`MsoThemeColorIndex`. When :attr:`type` has any other value, the value of
66
+ this property is |None|.
67
+
68
+ Assigning a member of :ref:`MsoThemeColorIndex` causes :attr:`type` to become
69
+ `MSO_COLOR_TYPE.THEME`. Any existing RGB value is retained but ignored by Word. Assigning
70
+ |None| causes any color specification to be removed such that the effective color is
71
+ inherited from the style hierarchy.
72
+ """
73
+ color = self._color
74
+ if color is None:
75
+ return None
76
+ return color.themeColor
77
+
78
+ @theme_color.setter
79
+ def theme_color(self, value: MSO_THEME_COLOR | None):
80
+ if value is None:
81
+ if self._color is not None and self._element.rPr is not None:
82
+ self._element.rPr._remove_color() # pyright: ignore[reportPrivateUsage]
83
+ return
84
+ self._element.get_or_add_rPr().get_or_add_color().themeColor = value
85
+
86
+ @property
87
+ def type(self) -> MSO_COLOR_TYPE | None:
88
+ """Read-only.
89
+
90
+ A member of :ref:`MsoColorType`, one of RGB, THEME, or AUTO, corresponding to the way this
91
+ color is defined. Its value is |None| if no color is applied at this level, which causes
92
+ the effective color to be inherited from the style hierarchy.
93
+ """
94
+ color = self._color
95
+ if color is None:
96
+ return None
97
+ if color.themeColor is not None:
98
+ return MSO_COLOR_TYPE.THEME
99
+ if color.val == ST_HexColorAuto.AUTO:
100
+ return MSO_COLOR_TYPE.AUTO
101
+ return MSO_COLOR_TYPE.RGB
102
+
103
+ @property
104
+ def _color(self) -> CT_Color | None:
105
+ """Return `w:rPr/w:color` or |None| if not present.
106
+
107
+ Helper to factor out repetitive element access.
108
+ """
109
+ rPr = self._element.rPr
110
+ if rPr is None:
111
+ return None
112
+ return rPr.color