#!/usr/bin/python # -*- coding: utf-8 -*- # Hive Appier Framework # Copyright (c) 2008-2024 Hive Solutions Lda. # # This file is part of Hive Appier Framework. # # Hive Appier Framework is free software: you can redistribute it and/or modify # it under the terms of the Apache License as published by the Apache # Foundation, either version 2.0 of the License, or (at your option) any # later version. # # Hive Appier Framework 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 # Apache License for more details. # # You should have received a copy of the Apache License along with # Hive Appier Framework. If not, see . __author__ = "João Magalhães " """ The author(s) of the module """ __copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda." """ The copyright for the module """ __license__ = "Apache License, Version 2.0" """ The license for the module """ import os from . import legacy from . import exceptions class Compress(object): def __init__(self): self._load_compress() def load_jsmin(self): try: import jsmin except ImportError: self.jsmin = None return self.jsmin = jsmin def type_jpeg(self): return "image/jpeg" def compress(self, file_path, modified=None, method=None): # retrieves the modification data from the requested file and # uses it to construct the complete (cache) key to be used in # cache try, in case there's a match returns the new file modified = modified or os.path.getmtime(file_path) key = "%s:%s" % (method, file_path) result = self.try_cache(key, modified) if result: return (len(result), legacy.BytesIO(result)) # in case there's no provided method a proper not found exception # should be raised so that the end user is notified about the non # existence of such unset compressor (as expected) if method == None: raise exceptions.NotFoundError(message="Compressor is not defined") # in case the compress string is defined, tries to find the proper # compress method and in case it's not found raises an exception if not hasattr(self, "compress_" + method): raise exceptions.NotFoundError(message="Compressor '%s' not found" % method) # retrieves the proper compressor method for the requested compress # technique, this should be used in a dynamic way enforcing some # overhead to avoid extra issues while handling with files compressor = getattr(self, "compress_" + method) # opens the requested file and reads the complete set of information # from it closing the file object after the operation is complete file = open(file_path, "rb") try: data = file.read() finally: file.close() # runs the compressing operation using the target compressor and uses # the resulting (compressed) data as the value to be returned result = compressor(data) # constructs both the size and the file object from the resulting plain # string data (bytes sequence), these values are considered the result result_size = len(result) result_file = legacy.BytesIO(result) # flags the proper values in the cache so that they may be re-used in case # the flag remains the same for the key, then returns the resulting tuple self.flag_cache(key, modified, result) return (result_size, result_file) def compress_jpeg(self, file_path): if self.jinja: return self.compress_jpeg_pil(file_path) return self.compress_fallback(file_path) def compress_jpeg_pil(self, data, quality=80): input = legacy.BytesIO(data) output = legacy.BytesIO() image = self.pil.Image.open(input) image.save(output, format="jpeg", quality=quality, optimize=True) output.seek(0, os.SEEK_SET) data = output.read() return data def compress_js(self, data): if self.jsmin: return self.compress_js_jsmin(data) return self.compress_fallback(data) def compress_js_jsmin(self, data): return self.jsmin.jsmin(data) def compress_fallback(self, data): return data def _load_compress(self): self.load_jsmin()