-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Copy pathstyles.py
145 lines (117 loc) · 5.58 KB
/
styles.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
"""Styles object, container for all objects in the styles part."""
from __future__ import annotations
from warnings import warn
from docx.enum.style import WD_STYLE_TYPE
from docx.oxml.styles import CT_Styles
from docx.shared import ElementProxy
from docx.styles import BabelFish
from docx.styles.latent import LatentStyles
from docx.styles.style import BaseStyle, StyleFactory
class Styles(ElementProxy):
"""Provides access to the styles defined in a document.
Accessed using the :attr:`.Document.styles` property. Supports ``len()``, iteration,
and dictionary-style access by style name.
"""
def __init__(self, styles: CT_Styles):
super().__init__(styles)
self._element = styles
def __contains__(self, name):
"""Enables `in` operator on style name."""
internal_name = BabelFish.ui2internal(name)
return any(style.name_val == internal_name for style in self._element.style_lst)
def __getitem__(self, key: str):
"""Enables dictionary-style access by UI name.
Lookup by style id is deprecated, triggers a warning, and will be removed in a
near-future release.
"""
style_elm = self._element.get_by_name(BabelFish.ui2internal(key))
if style_elm is not None:
return StyleFactory(style_elm)
style_elm = self._element.get_by_id(key)
if style_elm is not None:
msg = (
"style lookup by style_id is deprecated. Use style name as "
"key instead."
)
warn(msg, UserWarning, stacklevel=2)
return StyleFactory(style_elm)
raise KeyError("no style with name '%s'" % key)
def __iter__(self):
return (StyleFactory(style) for style in self._element.style_lst)
def __len__(self):
return len(self._element.style_lst)
def add_style(self, name, style_type, builtin=False):
"""Return a newly added style object of `style_type` and identified by `name`.
A builtin style can be defined by passing True for the optional `builtin`
argument.
"""
style_name = BabelFish.ui2internal(name)
if style_name in self:
raise ValueError("document already contains style '%s'" % name)
style = self._element.add_style_of_type(style_name, style_type, builtin)
return StyleFactory(style)
def default(self, style_type: WD_STYLE_TYPE):
"""Return the default style for `style_type` or |None| if no default is defined
for that type (not common)."""
style = self._element.default_for(style_type)
if style is None:
return None
return StyleFactory(style)
def get_by_id(self, style_id: str | None, style_type: WD_STYLE_TYPE):
"""Return the style of `style_type` matching `style_id`.
Returns the default for `style_type` if `style_id` is not found or is |None|, or
if the style having `style_id` is not of `style_type`.
"""
if style_id is None:
return self.default(style_type)
return self._get_by_id(style_id, style_type)
def get_style_id(self, style_or_name, style_type):
"""Return the id of the style corresponding to `style_or_name`, or |None| if
`style_or_name` is |None|.
If `style_or_name` is not a style object, the style is looked up using
`style_or_name` as a style name, raising |ValueError| if no style with that name
is defined. Raises |ValueError| if the target style is not of `style_type`.
"""
if style_or_name is None:
return None
elif isinstance(style_or_name, BaseStyle):
return self._get_style_id_from_style(style_or_name, style_type)
else:
return self._get_style_id_from_name(style_or_name, style_type)
@property
def latent_styles(self):
"""A |LatentStyles| object providing access to the default behaviors for latent
styles and the collection of |_LatentStyle| objects that define overrides of
those defaults for a particular named latent style."""
return LatentStyles(self._element.get_or_add_latentStyles())
def _get_by_id(self, style_id: str | None, style_type: WD_STYLE_TYPE):
"""Return the style of `style_type` matching `style_id`.
Returns the default for `style_type` if `style_id` is not found or if the style
having `style_id` is not of `style_type`.
"""
style = self._element.get_by_id(style_id) if style_id else None
if style is None or style.type != style_type:
return self.default(style_type)
return StyleFactory(style)
def _get_style_id_from_name(
self, style_name: str, style_type: WD_STYLE_TYPE
) -> str | None:
"""Return the id of the style of `style_type` corresponding to `style_name`.
Returns |None| if that style is the default style for `style_type`. Raises
|ValueError| if the named style is not found in the document or does not match
`style_type`.
"""
return self._get_style_id_from_style(self[style_name], style_type)
def _get_style_id_from_style(
self, style: BaseStyle, style_type: WD_STYLE_TYPE
) -> str | None:
"""Id of `style`, or |None| if it is the default style of `style_type`.
Raises |ValueError| if style is not of `style_type`.
"""
if style.type != style_type:
raise ValueError(
"assigned style is type %s, need type %s" % (style.type, style_type)
)
if style == self.default(style_type):
return None
return style.style_id