Skip to content

Commit e66d0f2

Browse files
committed
support opacity on export
1 parent 278b47b commit e66d0f2

5 files changed

Lines changed: 162 additions & 1 deletion

File tree

src/pptx/dml/color.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ def brightness(self, value):
3838
self._validate_brightness_value(value)
3939
self._color.brightness = value
4040

41+
@property
42+
def transparency(self):
43+
"""
44+
Read/write float value between 0.0 and 1.0 indicating the transparency
45+
of this color, e.g. 0.0 is completely opaque and 1.0 is completely
46+
transparent. 0.5 is 50% transparent.
47+
"""
48+
return self._color.transparency
49+
50+
@transparency.setter
51+
def transparency(self, value):
52+
self._validate_transparency_value(value)
53+
self._color.transparency = value
54+
4155
@classmethod
4256
def from_colorchoice_parent(cls, eg_colorChoice_parent):
4357
xClr = eg_colorChoice_parent.eg_colorChoice
@@ -106,6 +120,16 @@ def _validate_brightness_value(self, value):
106120
)
107121
raise ValueError(msg)
108122

123+
def _validate_transparency_value(self, value):
124+
if value < 0.0 or value > 1.0:
125+
raise ValueError("transparency must be number in range 0.0 to 1.0")
126+
if isinstance(self._color, _NoneColor):
127+
msg = (
128+
"can't set transparency when color.type is None. Set color.rgb"
129+
" or .theme_color first."
130+
)
131+
raise ValueError(msg)
132+
109133

110134
class _Color(object):
111135
"""
@@ -153,6 +177,42 @@ def brightness(self, value):
153177
else:
154178
self._xClr.clear_lum()
155179

180+
@property
181+
def transparency(self):
182+
"""
183+
Read/write float value between 0.0 and 1.0 indicating the transparency
184+
of this color. 0.0 is completely opaque, 1.0 is completely transparent.
185+
"""
186+
if self._xClr is None:
187+
# NoneColor has no transparency
188+
return 0.0
189+
alpha = self._xClr.alpha
190+
if alpha is not None:
191+
# alpha.val is in range 0.0-1.0, where 1.0 = 100% opaque
192+
# transparency is 1.0 - alpha.val
193+
return 1.0 - alpha.val
194+
# no alpha element means fully opaque (0% transparent)
195+
return 0.0
196+
197+
@transparency.setter
198+
def transparency(self, value):
199+
if self._xClr is None:
200+
# NoneColor cannot have transparency set
201+
msg = (
202+
"can't set transparency when color.type is None. Set color.rgb"
203+
" or .theme_color first."
204+
)
205+
raise ValueError(msg)
206+
if value == 0.0:
207+
# fully opaque, remove alpha element
208+
self._xClr.clear_alpha()
209+
else:
210+
# convert transparency (0.0-1.0) to alpha value (1.0-0.0)
211+
# alpha = 1.0 - transparency
212+
alpha_val = 1.0 - value
213+
self._xClr.clear_alpha()
214+
self._xClr.add_alpha(alpha_val)
215+
156216
@property
157217
def color_type(self): # pragma: no cover
158218
tmpl = ".color_type property must be implemented on %s"

src/pptx/dml/fill.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,22 @@ def fore_color(self):
7070
"""
7171
return self._fill.fore_color
7272

73+
@property
74+
def transparency(self):
75+
"""
76+
Read/write float value between 0.0 and 1.0 indicating the transparency
77+
of this fill, e.g. 0.0 is completely opaque and 1.0 is completely
78+
transparent. 0.5 is 50% transparent.
79+
80+
This property is only applicable to solid fills. For other fill types,
81+
accessing this property will raise a TypeError.
82+
"""
83+
return self._fill.transparency
84+
85+
@transparency.setter
86+
def transparency(self, value):
87+
self._fill.transparency = value
88+
7389
def gradient(self):
7490
"""Sets the fill type to gradient.
7591
@@ -205,6 +221,18 @@ def pattern(self):
205221
tmpl = "fill type %s has no pattern, call .patterned() first"
206222
raise TypeError(tmpl % self.__class__.__name__)
207223

224+
@property
225+
def transparency(self):
226+
"""Raise TypeError for fills that do not override this property."""
227+
tmpl = "fill type %s has no transparency, call .solid() first"
228+
raise TypeError(tmpl % self.__class__.__name__)
229+
230+
@transparency.setter
231+
def transparency(self, value):
232+
"""Raise TypeError for fills that do not override this property."""
233+
tmpl = "fill type %s has no transparency, call .solid() first"
234+
raise TypeError(tmpl % self.__class__.__name__)
235+
208236
@property
209237
def type(self) -> MSO_FILL_TYPE: # pragma: no cover
210238
raise NotImplementedError(
@@ -343,6 +371,18 @@ def fore_color(self):
343371
"""Return |ColorFormat| object controlling fill color."""
344372
return ColorFormat.from_colorchoice_parent(self._solidFill)
345373

374+
@property
375+
def transparency(self):
376+
"""
377+
Read/write float value between 0.0 and 1.0 indicating the transparency
378+
of this solid fill. 0.0 is completely opaque, 1.0 is completely transparent.
379+
"""
380+
return self.fore_color.transparency
381+
382+
@transparency.setter
383+
def transparency(self, value):
384+
self.fore_color.transparency = value
385+
346386
@property
347387
def type(self):
348388
return MSO_FILL.SOLID

src/pptx/oxml/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,13 +221,17 @@ def register_element_cls(nsptagname: str, cls: Type[BaseOxmlElement]):
221221
CT_Color,
222222
CT_HslColor,
223223
CT_Percentage,
224+
CT_PositiveFixedPercentage,
224225
CT_PresetColor,
225226
CT_SchemeColor,
226227
CT_ScRgbColor,
227228
CT_SRgbColor,
228229
CT_SystemColor,
229230
)
230231

232+
register_element_cls("a:alpha", CT_PositiveFixedPercentage)
233+
register_element_cls("a:alphaOff", CT_PositiveFixedPercentage)
234+
register_element_cls("a:alphaMod", CT_PositiveFixedPercentage)
231235
register_element_cls("a:bgClr", CT_Color)
232236
register_element_cls("a:fgClr", CT_Color)
233237
register_element_cls("a:hslClr", CT_HslColor)

src/pptx/oxml/dml/color.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from __future__ import annotations
44

55
from pptx.enum.dml import MSO_THEME_COLOR
6-
from pptx.oxml.simpletypes import ST_HexColorRGB, ST_Percentage
6+
from pptx.oxml.simpletypes import ST_HexColorRGB, ST_Percentage, ST_PositiveFixedPercentage
77
from pptx.oxml.xmlchemy import (
88
BaseOxmlElement,
99
Choice,
@@ -20,6 +20,9 @@ class _BaseColorElement(BaseOxmlElement):
2020

2121
lumMod = ZeroOrOne("a:lumMod")
2222
lumOff = ZeroOrOne("a:lumOff")
23+
alpha = ZeroOrOne("a:alpha")
24+
alphaOff = ZeroOrOne("a:alphaOff")
25+
alphaMod = ZeroOrOne("a:alphaMod")
2326

2427
def add_lumMod(self, value):
2528
"""
@@ -37,6 +40,30 @@ def add_lumOff(self, value):
3740
lumOff.val = value
3841
return lumOff
3942

43+
def add_alpha(self, value):
44+
"""
45+
Return a newly added <a:alpha> child element.
46+
"""
47+
alpha = self._add_alpha()
48+
alpha.val = value
49+
return alpha
50+
51+
def add_alphaOff(self, value):
52+
"""
53+
Return a newly added <a:alphaOff> child element.
54+
"""
55+
alphaOff = self._add_alphaOff()
56+
alphaOff.val = value
57+
return alphaOff
58+
59+
def add_alphaMod(self, value):
60+
"""
61+
Return a newly added <a:alphaMod> child element.
62+
"""
63+
alphaMod = self._add_alphaMod()
64+
alphaMod.val = value
65+
return alphaMod
66+
4067
def clear_lum(self):
4168
"""
4269
Return self after removing any <a:lumMod> and <a:lumOff> child
@@ -46,6 +73,16 @@ def clear_lum(self):
4673
self._remove_lumOff()
4774
return self
4875

76+
def clear_alpha(self):
77+
"""
78+
Return self after removing any <a:alpha>, <a:alphaOff> and <a:alphaMod> child
79+
elements.
80+
"""
81+
self._remove_alpha()
82+
self._remove_alphaOff()
83+
self._remove_alphaMod()
84+
return self
85+
4986

5087
class CT_Color(BaseOxmlElement):
5188
"""Custom element class for `a:fgClr`, `a:bgClr` and perhaps others."""
@@ -77,6 +114,14 @@ class CT_Percentage(BaseOxmlElement):
77114
val = RequiredAttribute("val", ST_Percentage)
78115

79116

117+
class CT_PositiveFixedPercentage(BaseOxmlElement):
118+
"""
119+
Custom element class for <a:alpha>, <a:alphaOff> and <a:alphaMod> elements.
120+
"""
121+
122+
val = RequiredAttribute("val", ST_PositiveFixedPercentage)
123+
124+
80125
class CT_PresetColor(_BaseColorElement):
81126
"""
82127
Custom element class for <a:prstClr> element.

tests/oxml/unitdata/dml.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ def a_lumOff():
7070
return CT_PercentageBuilder("a:lumOff")
7171

7272

73+
def a_alpha():
74+
return CT_PercentageBuilder("a:alpha")
75+
76+
77+
def a_alphaOff():
78+
return CT_PercentageBuilder("a:alphaOff")
79+
80+
81+
def a_alphaMod():
82+
return CT_PercentageBuilder("a:alphaMod")
83+
84+
7385
def a_prstClr():
7486
return CT_PresetColorBuilder()
7587

0 commit comments

Comments
 (0)