diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c7d7d3..13c189a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## [1.7.0] - DEV ### Added +- 🌟 SVG support. - New start screen with an AdwStatusPage. - Add debug information in about window. diff --git a/README.md b/README.md index 453bc98..35c5c5c 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ ## Compress your images -Curtail (previously ImCompressor) is an useful image compressor, supporting PNG, JPEG and WebP file types. +Curtail (previously ImCompressor) is an useful image compressor, supporting PNG, JPEG, WebP and SVG file types. It support both lossless and lossy compression modes with an option to whether keep or not metadata of images. It is inspired by [Trimage](https://github.com/Kilian/Trimage) and [Image-Optimizer](https://github.com/GijsGoudzwaard/Image-Optimizer). ### Supported formats -PNG, JPEG, WebP +PNG, JPEG, WebP, SVG ## Screenshot diff --git a/com.github.huluti.Curtail.json b/com.github.huluti.Curtail.json index def6969..478006d 100644 --- a/com.github.huluti.Curtail.json +++ b/com.github.huluti.Curtail.json @@ -69,6 +69,7 @@ } ] }, + "python3-scour.json", { "name": "curtail", "builddir": true, diff --git a/data/com.github.huluti.Curtail.appdata.xml.in b/data/com.github.huluti.Curtail.appdata.xml.in index c63d992..b5c73ab 100644 --- a/data/com.github.huluti.Curtail.appdata.xml.in +++ b/data/com.github.huluti.Curtail.appdata.xml.in @@ -8,7 +8,7 @@ Curtail Compress your images -

Optimize your images with Curtail, a useful image compressor that supports PNG, JPEG and WebP file types.

+

Optimize your images with Curtail, a useful image compressor that supports PNG, JPEG, WebP and SVG file types.

It supports both lossless and lossy compression modes with an option to whether keep or not metadata of images.

Hugo Posnic diff --git a/data/com.github.huluti.Curtail.gschema.xml b/data/com.github.huluti.Curtail.gschema.xml index e6adb0a..7912891 100644 --- a/data/com.github.huluti.Curtail.gschema.xml +++ b/data/com.github.huluti.Curtail.gschema.xml @@ -53,8 +53,13 @@ false - Enable progressive encoding for jpegs + Enable progressive encoding for JPEG images. Optionally encode jpeg images progressively. + + + false + Enable maximum compression for SVG images. + Optionally enable maximum cleaning of SVG images. 30 diff --git a/data/ui/preferences.ui b/data/ui/preferences.ui index 464ab41..8f9c88b 100644 --- a/data/ui/preferences.ui +++ b/data/ui/preferences.ui @@ -64,7 +64,7 @@ center - " + @@ -199,6 +199,22 @@ + + + SVG + + + SVG Maximum Compression Level + Can be more destructive for the image + + + center + + + + + + diff --git a/po/curtail.pot b/po/curtail.pot index 91f126e..4ed8e74 100644 --- a/po/curtail.pot +++ b/po/curtail.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: curtail\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-03 17:17+0200\n" +"POT-Creation-Date: 2023-04-04 22:47+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -25,7 +25,7 @@ msgstr "" #: data/com.github.huluti.Curtail.appdata.xml.in:11 msgid "" "Optimize your images with Curtail, a useful image compressor that supports " -"PNG, JPEG and WebP file types." +"PNG, JPEG, WebP and SVG file types." msgstr "" #: data/com.github.huluti.Curtail.appdata.xml.in:12 @@ -559,7 +559,7 @@ msgid "Lossless compression level to use for WebP images." msgstr "" #: data/com.github.huluti.Curtail.gschema.xml:56 -msgid "Enable progressive encoding for jpegs" +msgid "Enable progressive encoding for JPEG images." msgstr "" #: data/com.github.huluti.Curtail.gschema.xml:57 @@ -567,10 +567,18 @@ msgid "Optionally encode jpeg images progressively." msgstr "" #: data/com.github.huluti.Curtail.gschema.xml:61 -msgid "Compression Timeout" +msgid "Enable maximum compression for SVG images." msgstr "" #: data/com.github.huluti.Curtail.gschema.xml:62 +msgid "Optionally enable maximum cleaning of SVG images." +msgstr "" + +#: data/com.github.huluti.Curtail.gschema.xml:66 +msgid "Compression Timeout" +msgstr "" + +#: data/com.github.huluti.Curtail.gschema.xml:67 msgid "Compression timeout for each image." msgstr "" @@ -631,6 +639,14 @@ msgstr "" msgid "Progressive Encode JPG" msgstr "" +#: data/ui/preferences.ui:207 +msgid "SVG Maximum Compression Level" +msgstr "" + +#: data/ui/preferences.ui:208 +msgid "Can be more destructive for the image" +msgstr "" + #: data/ui/menu.ui:10 msgid "Keyboard Shortcuts" msgstr "" @@ -667,11 +683,11 @@ msgstr "" msgid "Main Menu" msgstr "" -#: src/compressor.py:86 +#: src/compressor.py:90 msgid "Compression has reached the configured timeout of {} seconds." msgstr "" -#: src/compressor.py:90 +#: src/compressor.py:94 msgid "An unknown error has occured" msgstr "" @@ -679,20 +695,24 @@ msgstr "" msgid "All images" msgstr "" -#: src/tools.py:36 +#: src/tools.py:37 msgid "PNG images" msgstr "" -#: src/tools.py:40 +#: src/tools.py:41 msgid "JPEG images" msgstr "" -#: src/tools.py:44 +#: src/tools.py:45 msgid "WebP images" msgstr "" -#: src/tools.py:113 src/tools.py:121 src/tools.py:128 src/tools.py:135 -#: src/tools.py:163 +#: src/tools.py:49 +msgid "SVG images" +msgstr "" + +#: src/tools.py:121 src/tools.py:128 src/tools.py:135 src/tools.py:142 +#: src/tools.py:149 src/tools.py:179 msgid "Version not found" msgstr "" diff --git a/python3-scour.json b/python3-scour.json new file mode 100644 index 0000000..86e5624 --- /dev/null +++ b/python3-scour.json @@ -0,0 +1,14 @@ +{ + "name": "python3-scour", + "buildsystem": "simple", + "build-commands": [ + "pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"scour\" --no-build-isolation" + ], + "sources": [ + { + "type": "file", + "url": "https://files.pythonhosted.org/packages/75/19/f519ef8aa2f379935a44212c5744e2b3a46173bf04e0110fb7f4af4028c9/scour-0.38.2.tar.gz", + "sha256": "6881ec26660c130c5ecd996ac6f6b03939dd574198f50773f2508b81a68e0daf" + } + ] +} \ No newline at end of file diff --git a/src/compressor.py b/src/compressor.py index a52f276..28fab4a 100644 --- a/src/compressor.py +++ b/src/compressor.py @@ -49,11 +49,13 @@ class Compressor(): self.png_lossless_level = self._settings.get_int('png-lossless-level') self.jpg_lossy_level = self._settings.get_int('jpg-lossy-level') - self.do_jpg_progressive = self._settings.get_boolean('jpg-progressive') + self.jpg_progressive = self._settings.get_boolean('jpg-progressive') self.webp_lossless_level = self._settings.get_int('webp-lossless-level') self.webp_lossy_level = self._settings.get_int('webp-lossy-level') + self.svg_maximum_level = self._settings.get_boolean('svg-maximum-level') + def compress_images(self): self.thread = threading.Thread(target=self._compress_images) self.thread.start() @@ -68,6 +70,8 @@ class Compressor(): command = self.build_jpg_command(result_item) elif file_type == 'webp': command = self.build_webp_command(result_item) + elif file_type == 'svg': + command = self.build_svg_command(result_item) self.run_command(command, result_item) # compress image GLib.idle_add(self.c_enable_compression, True) @@ -129,7 +133,7 @@ class Compressor(): jpegoptim = 'jpegoptim --max={} -o -f "{}"' jpegoptim2 = 'jpegoptim -o -f "{}"' - if self.do_jpg_progressive: + if self.jpg_progressive: jpegoptim += ' --all-progressive' jpegoptim2 += ' --all-progressive' @@ -174,3 +178,13 @@ class Compressor(): return command + def build_svg_command(self, result_item): + command = 'scour -i {} -o {}'.format(result_item.filename, + result_item.new_filename) + + if self.svg_maximum_level: + command += ' --enable-viewboxing --enable-id-stripping' + command += ' --enable-comment-stripping --shorten-ids --indent=none' + + return command + diff --git a/src/preferences.py b/src/preferences.py index 8b43acf..d5cb2b1 100644 --- a/src/preferences.py +++ b/src/preferences.py @@ -37,6 +37,7 @@ class CurtailPrefsWindow(Adw.PreferencesWindow): spin_jpg_lossy_level = Gtk.Template.Child() spin_webp_lossy_level = Gtk.Template.Child() toggle_jpg_progressive = Gtk.Template.Child() + toggle_svg_maximum_level = Gtk.Template.Child() _settings = Gio.Settings.new(SETTINGS_SCHEMA) @@ -113,6 +114,11 @@ class CurtailPrefsWindow(Adw.PreferencesWindow): self.toggle_jpg_progressive.connect('notify::active', self.on_bool_changed, 'jpg-progressive') + # Maxium SVG compression + self.toggle_svg_maximum_level.set_active(self._settings.get_boolean('svg-maximum-level')) + self.toggle_svg_maximum_level.connect('notify::active', self.on_bool_changed, + 'svg-maximum-level') + def on_bool_changed(self, switch, state, key): self._settings.set_boolean(key, switch.get_active()) # Additional actions diff --git a/src/tools.py b/src/tools.py index 65e3e86..b25f032 100644 --- a/src/tools.py +++ b/src/tools.py @@ -31,6 +31,7 @@ def add_filechooser_filters(dialog): all_images.add_mime_type('image/jpeg') all_images.add_mime_type('image/png') all_images.add_mime_type('image/webp') + all_images.add_mime_type('image/svg+xml') png_images = Gtk.FileFilter() png_images.set_name(_("PNG images")) @@ -44,11 +45,16 @@ def add_filechooser_filters(dialog): webp_images.set_name(_("WebP images")) webp_images.add_mime_type('image/webp') + svg_images = Gtk.FileFilter() + svg_images.set_name(_("SVG images")) + svg_images.add_mime_type('image/svg+xml') + file_filters = Gio.ListStore.new(Gtk.FileFilter) file_filters.append(all_images) file_filters.append(png_images) file_filters.append(jpeg_images) file_filters.append(webp_images) + file_filters.append(svg_images) dialog.set_filters(file_filters) @@ -62,6 +68,8 @@ def get_file_type(filename): return 'png' elif content_type == 'image/webp': return 'webp' + elif content_type == 'image/svg+xml': + return 'svg' else: return None else: @@ -109,42 +117,51 @@ def debug_infos(): try: jpegoptim = subprocess.check_output(['jpegoptim', '--version']) jpegoptim = extract_version(jpegoptim.decode('utf-8')) - except Exception as err: + except Exception: jpegoptim = _('Version not found') # Oxipng try: oxipng = subprocess.check_output(['oxipng', '--version']) oxipng = extract_version(oxipng.decode('utf-8')) - except Exception as err: + except Exception: oxipng = _('Version not found') # pngquant try: pngquant = subprocess.check_output(['pngquant', '--version']) pngquant = extract_version(pngquant.decode('utf-8')) - except Exception as err: + except Exception: pngquant = _('Version not found') # Libwebp try: libwebp = subprocess.check_output(['cwebp', '-version']) libwebp = extract_version(libwebp.decode('utf-8')) - except Exception as err: + except Exception: libwebp = _('Version not found') + # Scour + try: + scour = subprocess.check_output(['scour', '--version']) + scour = extract_version(scour.decode('utf-8')) + except Exception: + scour = _('Version not found') + debug = '''Python: {}\n Gtk: {}\n Jpegoptim: {}\n Oxipng: {}\n pngquant: {}\n -Libwebp: {}\n'''.format( +Libwebp: {}\n +Scour: {}\n'''.format( python_version, gtk_version, jpegoptim, oxipng, pngquant, - libwebp + libwebp, + scour ) return debug diff --git a/src/window.py b/src/window.py index 9471e56..581b6a3 100644 --- a/src/window.py +++ b/src/window.py @@ -226,7 +226,7 @@ class CurtailWindow(Gtk.ApplicationWindow): def check_extension(self, filename): file_type = get_file_type(filename) if file_type: - return file_type in ('png', 'jpg', 'webp') + return file_type in ('png', 'jpg', 'webp', 'svg') else: return False