%PDF- %PDF-
Direktori : /usr/lib/calibre/calibre/ebooks/oeb/polish/ |
Current File : //usr/lib/calibre/calibre/ebooks/oeb/polish/images.py |
#!/usr/bin/env python3 # License: GPLv3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net> import os from functools import partial from threading import Thread, Event from calibre import detect_ncpus, human_readable, force_unicode, filesystem_encoding from polyglot.builtins import iteritems from polyglot.queue import Queue, Empty class Worker(Thread): daemon = True def __init__(self, abort, name, queue, results, jpeg_quality, progress_callback): Thread.__init__(self, name=name) self.queue, self.results = queue, results self.progress_callback = progress_callback self.jpeg_quality = jpeg_quality self.abort = abort self.start() def run(self): while not self.abort.is_set(): try: name, path, mt = self.queue.get_nowait() except Empty: break try: self.compress(name, path, mt) except Exception: import traceback self.results[name] = (False, traceback.format_exc()) finally: try: self.progress_callback(name) except Exception: import traceback traceback.print_exc() self.queue.task_done() def compress(self, name, path, mime_type): from calibre.utils.img import optimize_png, optimize_jpeg, encode_jpeg if 'png' in mime_type: func = optimize_png elif self.jpeg_quality is None: func = optimize_jpeg else: func = partial(encode_jpeg, quality=self.jpeg_quality) before = os.path.getsize(path) with lopen(path, 'rb') as f: old_data = f.read() func(path) after = os.path.getsize(path) if after >= before: with lopen(path, 'wb') as f: f.write(old_data) after = before self.results[name] = (True, (before, after)) def get_compressible_images(container): mt_map = container.manifest_type_map images = set() for mt in 'png jpg jpeg'.split(): images |= set(mt_map.get('image/' + mt, ())) return images def compress_images(container, report=None, names=None, jpeg_quality=None, progress_callback=lambda n, t, name:True): images = get_compressible_images(container) if names is not None: images &= set(names) results = {} queue = Queue() abort = Event() seen = set() num_to_process = 0 for name in sorted(images): path = os.path.abspath(container.get_file_path_for_processing(name)) path_key = os.path.normcase(path) if path_key not in seen: num_to_process += 1 queue.put((name, path, container.mime_map[name])) seen.add(path_key) def pc(name): keep_going = progress_callback(len(results), num_to_process, name) if not keep_going: abort.set() progress_callback(0, num_to_process, '') [Worker(abort, 'CompressImage%d' % i, queue, results, jpeg_quality, pc) for i in range(min(detect_ncpus(), num_to_process))] queue.join() before_total = after_total = 0 processed_num = 0 changed = False for name, (ok, res) in iteritems(results): name = force_unicode(name, filesystem_encoding) if ok: before, after = res if before != after: changed = True processed_num += 1 before_total += before after_total += after if report: if before != after: report(_('{0} compressed from {1} to {2} bytes [{3:.1%} reduction]').format( name, human_readable(before), human_readable(after), (before - after)/before)) else: report(_('{0} could not be further compressed').format(name)) else: report(_('Failed to process {0} with error:').format(name)) report(res) if report: if changed: report('') report(_('Total image filesize reduced from {0} to {1} [{2:.1%} reduction, {3} images changed]').format( human_readable(before_total), human_readable(after_total), (before_total - after_total)/before_total, processed_num)) else: report(_('Images are already fully optimized')) return changed, results