1# utilities.py 2# 3# Copyright 2018-2021 Romain F. T. 4# 5# This program is free software: you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation, either version 3 of the License, or 8# (at your option) any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18from gi.repository import Gtk, Gio 19 20################################################################################ 21 22def utilities_get_rgba_name(red, green, blue, alpha): 23 """To improve accessibility, it is useful to display the name of the colors. 24 Sadly, it's a mess to implement, and it's quite approximative.""" 25 color_string = "" 26 alpha_string = "" 27 if alpha == 0.0: 28 return _("Transparent") 29 elif alpha < 1.0: 30 alpha_string = ' - ' + _("%s%% transparent") % int(100 - alpha * 100) 31 32 total = red + green + blue 33 orange_coef = 0.0 34 lumin = total / 3.0 35 # print(lumin) 36 if green != 0: 37 orange_coef = (red/green) * lumin 38 39 if total != 0: 40 rgb_percents = [red/total, green/total, blue/total] 41 else: 42 rgb_percents = [0.333, 0.333, 0.333] 43 # print(rgb_percents) 44 45 grey_coef_r = rgb_percents[0] * lumin / 3 46 grey_coef_g = rgb_percents[1] * lumin / 3 47 grey_coef_b = rgb_percents[2] * lumin / 3 48 is_grey = abs(grey_coef_r - grey_coef_g) < 0.01 49 is_grey = is_grey and abs(grey_coef_g - grey_coef_b) < 0.01 50 is_grey = is_grey and abs(grey_coef_b - grey_coef_r) < 0.01 51 52 if is_grey: 53 if lumin > 0.9: 54 color_string = _("White") 55 elif lumin < 0.1: 56 color_string = _("Black") 57 else: 58 color_string = _("Grey") 59 60 elif rgb_percents[0] > 0.5 and rgb_percents[1] > 0.2 and rgb_percents[1] < 0.4: 61 if orange_coef > 0.87: 62 color_string = _("Orange") 63 else: 64 color_string = _("Brown") 65 66 elif rgb_percents[0] > 0.4 and rgb_percents[1] < 0.3 and rgb_percents[2] < 0.3: 67 if lumin < 0.7 and rgb_percents[0] < 0.7: 68 # Context: the name of the current color is provided as a tooltip to 69 # help users with color blindness, but some color names don't have a 70 # clear definition. Here, the app thinks it's probably brown. 71 color_string = _("Probably brown") 72 else: 73 color_string = _("Red") 74 elif rgb_percents[1] > 0.4 and rgb_percents[0] < 0.4 and rgb_percents[2] < 0.4: 75 color_string = _("Green") 76 elif rgb_percents[2] > 0.4 and rgb_percents[0] < 0.3 and rgb_percents[1] < 0.4: 77 color_string = _("Blue") 78 79 elif rgb_percents[0] > 0.3 and rgb_percents[1] > 0.3 and rgb_percents[2] < 0.3: 80 if rgb_percents[1] < 0.4: 81 color_string = _("Probably brown") 82 else: 83 color_string = _("Yellow") 84 elif rgb_percents[0] > 0.3 and rgb_percents[2] > 0.3 and rgb_percents[1] < 0.3: 85 if lumin > 0.6 and rgb_percents[1] < 0.1: 86 color_string = _("Magenta") 87 else: 88 color_string = _("Purple") 89 elif rgb_percents[1] > 0.3 and rgb_percents[2] > 0.3 and rgb_percents[0] < 0.2: 90 if lumin > 0.7: 91 color_string = _("Cyan") 92 else: 93 # Context: the name of the current color is provided as a tooltip to 94 # help users with color blindness, but some color names don't have a 95 # clear definition. Here, the app thinks it's probably teal. 96 # You can translate "teal" with the name of approaching color, like 97 # turquoise or green-blue. 98 color_string = _("Probably teal") 99 100 else: 101 # Context: the name of the current color is provided as a tooltip to 102 # help users with color blindness, but some color names don't have a 103 # clear definition. Here, the app can't find a corresponding color name. 104 color_string = _("Unknown color name") 105 106 # print(color_string) 107 return (color_string + alpha_string) 108 109################################################################################ 110 111def utilities_add_filechooser_filters(dialog): 112 """Add file filters for images to file chooser dialogs.""" 113 allPictures = Gtk.FileFilter() 114 allPictures.set_name(_("All pictures")) 115 allPictures.add_mime_type('image/png') 116 allPictures.add_mime_type('image/jpeg') 117 allPictures.add_mime_type('image/bmp') 118 119 pngPictures = Gtk.FileFilter() 120 pngPictures.set_name(_("PNG images")) 121 pngPictures.add_mime_type('image/png') 122 123 jpegPictures = Gtk.FileFilter() 124 jpegPictures.set_name(_("JPEG images")) 125 jpegPictures.add_mime_type('image/jpeg') 126 127 bmpPictures = Gtk.FileFilter() 128 bmpPictures.set_name(_("BMP images")) 129 bmpPictures.add_mime_type('image/bmp') 130 131 dialog.add_filter(allPictures) 132 dialog.add_filter(pngPictures) 133 dialog.add_filter(jpegPictures) 134 dialog.add_filter(bmpPictures) 135 136################################################################################ 137 138def utilities_add_unit_to_spinbtn(spinbutton, width_chars, unit): 139 spinbutton.set_width_chars(width_chars + 3) 140 if unit == 'px': 141 # To translators: it's a measure unit, it appears in tooltips over 142 # numerical inputs 143 _add_spinbutton_icon(spinbutton, 'unit-pixels-symbolic', _("pixels")) 144 elif unit == '%': 145 # To translators: it appears in tooltips over numerical inputs 146 _add_spinbutton_icon(spinbutton, 'unit-percents-symbolic', _("percents")) 147 elif unit == '°': 148 # To translators: it's the angle measure unit, it appears in a tooltip 149 # over a numerical input 150 _add_spinbutton_icon(spinbutton, 'unit-degrees-symbolic', _("degrees")) 151 152def _add_spinbutton_icon(spinbutton, icon, tooltip): 153 p = Gtk.EntryIconPosition.SECONDARY 154 spinbutton.set_icon_from_icon_name(p, icon) 155 spinbutton.set_icon_tooltip_text(p, tooltip) 156 spinbutton.set_icon_sensitive(p, False) 157 158################################################################################ 159 160def utilities_gfile_is_image(gfile, error_msg=""): 161 try: 162 infos = gfile.query_info('standard::*', Gio.FileQueryInfoFlags.NONE, None) 163 if 'image/' in infos.get_content_type(): 164 # The exact file format of the image isn't validated here because i 165 # can't assume what gdkpixbuf is able to read (it's modular, and it 166 # evolves). An InvalidFileFormatException will be raised by DrImage 167 # if the file can't be loaded. 168 return True, error_msg 169 else: 170 error_msg = error_msg + _("%s isn't an image.") % gfile.get_path() 171 except Exception as err: 172 error_msg = error_msg + err.message 173 return False, error_msg 174 175class InvalidFileFormatException(Exception): 176 def __init__(self, initial_message, fpath): 177 self.message = initial_message 178 cpt = 0 179 with open(fpath, 'rb') as f: 180 riff_bytes = f.read(4) 181 size_bytes = f.read(4) 182 webp_bytes = f.read(4) 183 if riff_bytes == b'RIFF' and webp_bytes == b'WEBP': 184 msg = _("Sorry, WEBP images can't be loaded by this app.") + " " 185 if fpath[-5:] != '.webp': 186 # Context: an error message, %s is a file path 187 msg = msg + _("Despite its name, %s is a WEBP file.") % fpath 188 self.message = msg 189 super().__init__(self.message) 190 # This exception is meant to be raised when a file is detected as an image 191 # by Gio (see utility function above) BUT can't be loaded into a GdkPixbuf. 192 # It usually means the file is corrupted, or has a deceptive name (for 193 # example "xxx.jpeg" despite being a text file), or is just an image format 194 # not supported by the GdkPixbuf version installed by the user. 195 196################################################################################ 197 198