From f39ef3e06266991a9f6b6f1265487b756ba7fb67 Mon Sep 17 00:00:00 2001 From: Aleksey Lim Date: Wed, 7 Apr 2010 13:11:04 +0000 Subject: [PATCH] Support svg feed icons #2983195 --- zeroinstall/0launch-gui/iface_browser.py | 26 ++--------------- zeroinstall/gtkui/applistbox.py | 25 +++------------- zeroinstall/gtkui/icon.py | 49 ++++++++++++++++++++++++-------- zeroinstall/injector/fetch.py | 4 +-- 4 files changed, 46 insertions(+), 58 deletions(-) diff --git a/zeroinstall/0launch-gui/iface_browser.py b/zeroinstall/0launch-gui/iface_browser.py index bb19f64..0dd99d1 100644 --- a/zeroinstall/0launch-gui/iface_browser.py +++ b/zeroinstall/0launch-gui/iface_browser.py @@ -8,6 +8,7 @@ from zeroinstall.injector.iface_cache import iface_cache from zeroinstall.injector import model import properties from zeroinstall.gtkui.treetips import TreeTips +from zeroinstall.gtkui.icon import load_icon from zeroinstall import support from logging import warn import utils @@ -256,27 +257,6 @@ class InterfaceBrowser: # Clear icons cache to make sure they're really updated self.cached_icon = {} self.update_icons = update_icons - - def _load_icon(self, path): - assert path - try: - loader = gtk.gdk.PixbufLoader('png') - try: - loader.write(file(path).read()) - finally: - loader.close() - icon = loader.get_pixbuf() - assert icon, "Failed to load cached PNG icon data" - except Exception, ex: - warn(_("Failed to load cached PNG icon: %s"), ex) - return None - w = icon.get_width() - h = icon.get_height() - scale = max(w, h, 1) / ICON_SIZE - icon = icon.scale_simple(int(w / scale), - int(h / scale), - gtk.gdk.INTERP_BILINEAR) - return icon def get_icon(self, iface): """Get an icon for this interface. If the icon is in the cache, use that. @@ -290,7 +270,7 @@ class InterfaceBrowser: iconpath = iface_cache.get_icon_path(iface) if iconpath: - icon = self._load_icon(iconpath) + icon = load_icon(iconpath, ICON_SIZE, ICON_SIZE) # (if icon is None, cache the fact that we can't load it) self.cached_icon[iface.uri] = icon else: @@ -314,7 +294,7 @@ class InterfaceBrowser: # we don't try again. iconpath = iface_cache.get_icon_path(iface) if iconpath: - self.cached_icon[iface.uri] = self._load_icon(iconpath) + self.cached_icon[iface.uri] = load_icon(iconpath, ICON_SIZE, ICON_SIZE) self.build_tree() else: warn("Failed to download icon for '%s'", iface) diff --git a/zeroinstall/gtkui/applistbox.py b/zeroinstall/gtkui/applistbox.py index 3ad5975..a67116d 100644 --- a/zeroinstall/gtkui/applistbox.py +++ b/zeroinstall/gtkui/applistbox.py @@ -127,8 +127,6 @@ class AppListBox: model = self.model model.clear() - default_icon = self.window.render_icon(gtk.STOCK_EXECUTE, gtk.ICON_SIZE_DIALOG) - for uri in self.app_list.get_apps(): itr = model.append() model[itr][AppListBox.URI] = uri @@ -139,29 +137,14 @@ class AppListBox: summary = summary[:1].capitalize() + summary[1:] model[itr][AppListBox.NAME] = name - pixbuf = icon.load_icon(self.iface_cache.get_icon_path(iface)) - if not pixbuf: - pixbuf = default_icon - else: - # Cap icon size, some icons are really high resolution - pixbuf = self.cap_pixbuf_dimensions(pixbuf, default_icon.get_width()) + icon_width, icon_height = gtk.icon_size_lookup(gtk.ICON_SIZE_DIALOG) + pixbuf = icon.load_icon(self.iface_cache.get_icon_path(iface), icon_width, icon_height) + if pixbuf is None: + pixbuf = self.window.render_icon(gtk.STOCK_EXECUTE, gtk.ICON_SIZE_DIALOG) model[itr][AppListBox.ICON] = pixbuf model[itr][AppListBox.MARKUP] = '%s\n%s' % (_pango_escape(name), _pango_escape(summary)) - def cap_pixbuf_dimensions(self, pixbuf, iconsize): - pixbuf_w = pixbuf.get_width() - pixbuf_h = pixbuf.get_height() - if (pixbuf_w > iconsize) or (pixbuf_h > iconsize): - if (pixbuf_w > pixbuf_h): - newheight = (pixbuf_w/pixbuf_h) * iconsize - newwidth = iconsize - else: - newwidth = (pixbuf_h/pixbuf_w) * iconsize - newheight = iconsize - return pixbuf.scale_simple(newwidth, newheight, gtk.gdk.INTERP_BILINEAR) - return pixbuf - def action_run(self, uri): iface = self.iface_cache.get_interface(uri) reader.update_from_cache(iface) diff --git a/zeroinstall/gtkui/icon.py b/zeroinstall/gtkui/icon.py index 1ca1d4c..03dd51f 100644 --- a/zeroinstall/gtkui/icon.py +++ b/zeroinstall/gtkui/icon.py @@ -4,22 +4,47 @@ from zeroinstall import _ import gtk -from logging import warn +from logging import warn, debug -def load_icon(icon_path): +def load_icon(icon_path, icon_width=None, icon_height=None): """Load icon from path. Icon MUST be in PNG format. @param icon_path: pathname of icon, or None to load nothing @return: a GdkPixbuf, or None on failure""" if not icon_path: return None - try: - # Icon format must be PNG (to avoid attacks) - loader = gtk.gdk.PixbufLoader('png') + + def size_prepared_cb(loader, width, height): + dest_width = icon_width or width + dest_height = icon_height or height + + if dest_width == width and dest_height == height: + return + + ratio_width = float(dest_width) / width + ratio_height = float(dest_height) / height + ratio = min(ratio_width, ratio_height) + + # preserve original ration + if ratio_width != ratio: + dest_width = int(math.ceil(width * ratio)) + elif ratio_height != ratio: + dest_height = int(math.ceil(height * ratio)) + + loader.set_size(int(dest_width), int(dest_height)) + + # Restrict icon formats to avoid attacks + for format in ('png', 'svg'): try: - loader.write(file(icon_path).read()) - finally: - loader.close() - return loader.get_pixbuf() - except Exception, ex: - warn(_("Failed to load cached PNG icon: %s") % ex) - return None + loader = gtk.gdk.PixbufLoader(format) + if icon_width or icon_height: + loader.connect('size-prepared', size_prepared_cb) + try: + loader.write(file(icon_path).read()) + finally: + loader.close() + return loader.get_pixbuf() + except Exception, ex: + debug(_("Failed to load icon: %s") % ex) + + warn(_("Failed to load cached icon")) + return None diff --git a/zeroinstall/injector/fetch.py b/zeroinstall/injector/fetch.py index 518b3a4..dff9b07 100644 --- a/zeroinstall/injector/fetch.py +++ b/zeroinstall/injector/fetch.py @@ -371,8 +371,8 @@ class Fetcher(object): # Find a suitable icon to download for icon in interface.get_metadata(XMLNS_IFACE, 'icon'): type = icon.getAttribute('type') - if type != 'image/png': - debug(_('Skipping non-PNG icon')) + if type not in ('image/png', 'image/svg+xml', 'image/svg+xml-compressed'): + debug(_('MIME type %(mime_type)s is not permited for feed icons'), {'mime_type': type}) continue source = icon.getAttribute('href') if source: -- 2.11.4.GIT