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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
|
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""gtk.gdk.Pixbuf extensions"""
import re
import os
import cStringIO
import gtk
import rsvg
import cairo
import logging
from sugar.graphics.xocolor import XoColor
from sugar.util import LRU
def to_file(pixbuf):
"""Convert pixbuf object to file object"""
def push(pixbuf, buffer):
buffer.write(pixbuf)
buffer = cStringIO.StringIO()
pixbuf.save_to_callback(push, 'png', user_data=buffer)
buffer.seek(0)
return buffer
def to_str(pixbuf):
"""Convert pixbuf object to string"""
return to_file(pixbuf).getvalue()
def from_str(str):
"""Convert string to pixbuf object"""
loader = gtk.gdk.pixbuf_loader_new_with_mime_type('image/png')
loader.write(str)
loader.close()
return loader.get_pixbuf()
def from_svg_at_size(filename=None, width=None, height=None, handle=None,
keep_ratio=True):
"""Scale and load SVG into pixbuf"""
if not handle:
handle = rsvg.Handle(filename)
dimensions = handle.get_dimension_data()
icon_width = dimensions[0]
icon_height = dimensions[1]
if icon_width != width or icon_height != height:
ratio_width = float(width) / icon_width
ratio_height = float(height) / icon_height
if keep_ratio:
ratio = min(ratio_width, ratio_height)
if ratio_width != ratio:
ratio_width = ratio
width = int(icon_width * ratio)
elif ratio_height != ratio:
ratio_height = ratio
height = int(icon_height * ratio)
else:
ratio_width = 1
ratio_height = 1
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
context = cairo.Context(surface)
context.scale(ratio_width, ratio_height)
handle.render_cairo(context)
loader = gtk.gdk.pixbuf_loader_new_with_mime_type('image/png')
surface.write_to_png(loader)
loader.close()
return loader.get_pixbuf()
def sugar_icon(file_name=None, icon_name=None,
width=None, height=None,
color=None,
insensitive_widget=None):
"""Load sugar icon into pixbuf
NOTE: Function can load all image formats but makes sense only for SVG
(due to color argument, see load_svg())
NOTE: Function caches results
Arguments:
file_name path to filename with image
(mutually exclusive for icon_name)
icon_name name of icon
(mutually exclusive for icon_name)
width width of final image
height height of final image
color defines stroke and fill colors for final SVG image
in string notion, could be:
* tuple of (stroke_color, fill_color)
* XoColor
* scalar value for stroke and fill colors
insensitive_widget render icon in insensitive mode
"""
def load_svg():
entities = {}
if fill_color:
entities['fill_color'] = fill_color
if stroke_color:
entities['stroke_color'] = stroke_color
f = open(icon_filename, 'r')
icon = f.read()
f.close()
for entity, value in entities.items():
xml = '<!ENTITY %s "%s">' % (entity, value)
icon = re.sub('<!ENTITY %s .*>' % entity, xml, icon)
return rsvg.Handle(data=icon)
def get_insensitive_pixbuf():
if not (insensitive_widget and insensitive_widget.style):
return pixbuf
icon_source = gtk.IconSource()
# Special size meaning "don't touch"
icon_source.set_size(-1)
icon_source.set_pixbuf(pixbuf)
icon_source.set_state(gtk.STATE_INSENSITIVE)
icon_source.set_direction_wildcarded(False)
icon_source.set_size_wildcarded(False)
# Please note that the pixbuf returned by this function is leaked
# with current stable versions of pygtk. The relevant bug is
# http://bugzilla.gnome.org/show_bug.cgi?id=502871
# -- 2007-12-14 Benjamin Berg
pixbuf = insensitive_widget.style.render_icon(icon_source,
insensitive_widget.get_direction(), gtk.STATE_INSENSITIVE, -1,
insensitive_widget, "sugar-icon")
return pixbuf
def get_cache_key():
return (icon_filename, fill_color, stroke_color, width, height,
insensitive_widget is None)
if isinstance(color, XoColor):
stroke_color = color.get_stroke_color()
fill_color = color.get_fill_color()
elif isinstance(color, tuple):
stroke_color = color[0]
fill_color = color[1]
else:
stroke_color = color
fill_color = color
if file_name:
icon_filename = file_name
elif icon_name:
theme = gtk.icon_theme_get_default()
info = theme.lookup_icon(icon_name, width or 50, 0)
if info:
icon_filename = info.get_filename()
del info
else:
logging.warning('No icon with the name %s '
'was found in the theme.' % icon_name)
else:
return None
cache_key = get_cache_key()
if cache_key in _sugar_icon_cache:
return _sugar_icon_cache[cache_key]
logging.debug('sugar_icon: file_name=%s icon_name=%s width=%s height=%s ' \
'color=%s' % (file_name, icon_name, width, height, color))
is_svg = icon_filename.endswith('.svg')
if is_svg:
handle = load_svg()
if width and height:
pixbuf = from_svg_at_size(handle=handle, width=width, height=height,
keep_ratio=True)
else:
pixbuf = handle.get_pixbuf()
else:
if width and height:
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(icon_filename,
width, height)
else:
pixbuf = gtk.gdk.pixbuf_new_from_file(icon_filename)
if insensitive_widget:
pixbuf = get_insensitive_pixbuf()
_sugar_icon_cache[cache_key] = pixbuf
return pixbuf
_sugar_icon_cache = LRU(50)
|