# HG changeset patch # User Goffi # Date 1584877963 -3600 # Node ID 4fbea7f1e0123d0105271b00969d9a8036a9e2ae # Parent 2ba602aef90eb177c34c79a64ea44654ef8d7ef4 tools (images): methods renaming diff -r 2ba602aef90e -r 4fbea7f1e012 sat/core/sat_main.py --- a/sat/core/sat_main.py Wed Mar 18 20:25:02 2020 +0100 +++ b/sat/core/sat_main.py Sun Mar 22 12:52:43 2020 +0100 @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# SAT: a jabber client +# SàT: an XMPP client # Copyright (C) 2009-2020 Jérôme Poisson (goffi@goffi.org) # This program is free software: you can redistribute it and/or modify @@ -42,7 +42,7 @@ from sat.memory import encryption from sat.tools import async_trigger as trigger from sat.tools import utils -from sat.tools import images +from sat.tools import image from sat.tools.common import dynamic_import from sat.tools.common import regex from sat.tools.common import data_format @@ -740,11 +740,11 @@ # images def _imageCheck(self, path): - report = images.checkImage(self, path) + report = image.check(self, path) return data_format.serialise(report) def _imageResize(self, path, width, height): - d = images.resizeImage(path, (width, height)) + d = image.resize(path, (width, height)) d.addCallback(lambda new_image_path: str(new_image_path)) return d @@ -760,7 +760,7 @@ @param path(Path): path to the image @return (Path): path to the generated preview """ - report = images.checkImage(self, path, max_size=(300, 300)) + report = image.check(self, path, max_size=(300, 300)) if not report['too_large']: # in the unlikely case that image is already smaller than a preview @@ -779,7 +779,7 @@ uid=uid, filename=filename) as cache_f: - preview_path = await images.resizeImage( + preview_path = await image.resize( path, new_size=report['recommended_size'], dest=cache_f diff -r 2ba602aef90e -r 4fbea7f1e012 sat/tools/image.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sat/tools/image.py Sun Mar 22 12:52:43 2020 +0100 @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +# SàT: an XMPP client +# Copyright (C) 2009-2020 Jérôme Poisson (goffi@goffi.org) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program 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 +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +"""Methods to manipulate images""" + +import tempfile +from PIL import Image +from pathlib import Path +from twisted.internet import threads + + +def check(host, path, max_size=None): + """Analyze image and return a report + + report will indicate if image is too large, and the recommended new size if this is + the case + @param host: SàT instance + @param path(str, pathlib.Path): image to open + @param max_size(tuple[int, int]): maximum accepted size of image + None to use value set in config + @return dict: report on image, with following keys: + - too_large: true if image is oversized + - recommended_size: if too_large is True, recommended size to use + """ + report = {} + image = Image.open(path) + if max_size is None: + max_size = tuple(host.memory.getConfig(None, "image_max", (1200, 720))) + if image.size > max_size: + report['too_large'] = True + if image.size[0] > max_size[0]: + factor = max_size[0] / image.size[0] + if image.size[1] * factor > max_size[1]: + factor = max_size[1] / image.size[1] + else: + factor = max_size[1] / image.size[1] + report['recommended_size'] = [int(image.width*factor), int(image.height*factor)] + else: + report['too_large'] = False + + return report + + +def _resizeBlocking(image_path, new_size, dest=None): + im_path = Path(image_path) + im = Image.open(im_path) + resized = im.resize(new_size, Image.LANCZOS) + + if dest is None: + dest = tempfile.NamedTemporaryFile(suffix=im_path.suffix, delete=False) + elif isinstance(dest, Path): + dest = dest.open('wb') + + with dest as f: + resized.save(f, format=im.format) + + return Path(f.name) + + +def resize(image_path, new_size, dest=None): + """Resize an image to a new file, and return its path + + @param image_path(str, Path): path of the original image + @param new_size(tuple[int, int]): size to use for new image + @param dest(None, Path, file): where the resized image must be stored, can be: + - None: use a temporary file + - Path: path to the file to create/overwrite + - file: a file object which must be opened for writing in binary mode + @return (Path): path of the resized file. + The image at this path should be deleted after use + """ + return threads.deferToThread(_resizeBlocking, image_path, new_size, dest) diff -r 2ba602aef90e -r 4fbea7f1e012 sat/tools/images.py --- a/sat/tools/images.py Wed Mar 18 20:25:02 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,88 +0,0 @@ -#!/usr/bin/env python3 - - -# SAT: a jabber client -# Copyright (C) 2009-2020 Jérôme Poisson (goffi@goffi.org) - -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -# This program 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 -# GNU Affero General Public License for more details. - -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -"""Methods to manipulate images""" - -import tempfile -from PIL import Image -from pathlib import Path -from twisted.internet import threads - - -def checkImage(host, path, max_size=None): - """Analyze image and return a report - - report will indicate if image is too large, and the recommended new size if this is - the case - @param host: SàT instance - @param path(str, pathlib.Path): image to open - @param max_size(tuple[int, int]): maximum accepted size of image - None to use value set in config - @return dict: report on image, with following keys: - - too_large: true if image is oversized - - recommended_size: if too_large is True, recommended size to use - """ - report = {} - image = Image.open(path) - if max_size is None: - max_size = tuple(host.memory.getConfig(None, "image_max", (1200, 720))) - if image.size > max_size: - report['too_large'] = True - if image.size[0] > max_size[0]: - factor = max_size[0] / image.size[0] - if image.size[1] * factor > max_size[1]: - factor = max_size[1] / image.size[1] - else: - factor = max_size[1] / image.size[1] - report['recommended_size'] = [int(image.width*factor), int(image.height*factor)] - else: - report['too_large'] = False - - return report - - -def _resizeImageBlocking(image_path, new_size, dest=None): - im_path = Path(image_path) - im = Image.open(im_path) - resized = im.resize(new_size, Image.LANCZOS) - - if dest is None: - dest = tempfile.NamedTemporaryFile(suffix=im_path.suffix, delete=False) - elif isinstance(dest, Path): - dest = Path.open('wb') - - with dest as f: - resized.save(f, format=im.format) - - return Path(f.name) - - -def resizeImage(image_path, new_size, dest=None): - """Resize an image to a new file, and return its path - - @param image_path(str, Path): path of the original image - @param new_size(tuple[int, int]): size to use for new image - @param dest(None, Path, file): where the resized image must be stored, can be: - - None: use a temporary file - - Path: path to the file to create/overwrite - - file: a file object which must be opened for writing in binary mode - @return (Path): path of the resized file. - The image at this path should be deleted after use - """ - return threads.deferToThread(_resizeImageBlocking, image_path, new_size, dest)