comparison sat/tools/image.py @ 3220:4fbea7f1e012

tools (images): methods renaming
author Goffi <goffi@goffi.org>
date Sun, 22 Mar 2020 12:52:43 +0100
parents sat/tools/images.py@5c3bf37f2202
children 54934ee3f69c
comparison
equal deleted inserted replaced
3219:2ba602aef90e 3220:4fbea7f1e012
1 #!/usr/bin/env python3
2
3 # SàT: an XMPP client
4 # Copyright (C) 2009-2020 Jérôme Poisson (goffi@goffi.org)
5
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Affero General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Affero General Public License for more details.
15
16 # You should have received a copy of the GNU Affero General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 """Methods to manipulate images"""
20
21 import tempfile
22 from PIL import Image
23 from pathlib import Path
24 from twisted.internet import threads
25
26
27 def check(host, path, max_size=None):
28 """Analyze image and return a report
29
30 report will indicate if image is too large, and the recommended new size if this is
31 the case
32 @param host: SàT instance
33 @param path(str, pathlib.Path): image to open
34 @param max_size(tuple[int, int]): maximum accepted size of image
35 None to use value set in config
36 @return dict: report on image, with following keys:
37 - too_large: true if image is oversized
38 - recommended_size: if too_large is True, recommended size to use
39 """
40 report = {}
41 image = Image.open(path)
42 if max_size is None:
43 max_size = tuple(host.memory.getConfig(None, "image_max", (1200, 720)))
44 if image.size > max_size:
45 report['too_large'] = True
46 if image.size[0] > max_size[0]:
47 factor = max_size[0] / image.size[0]
48 if image.size[1] * factor > max_size[1]:
49 factor = max_size[1] / image.size[1]
50 else:
51 factor = max_size[1] / image.size[1]
52 report['recommended_size'] = [int(image.width*factor), int(image.height*factor)]
53 else:
54 report['too_large'] = False
55
56 return report
57
58
59 def _resizeBlocking(image_path, new_size, dest=None):
60 im_path = Path(image_path)
61 im = Image.open(im_path)
62 resized = im.resize(new_size, Image.LANCZOS)
63
64 if dest is None:
65 dest = tempfile.NamedTemporaryFile(suffix=im_path.suffix, delete=False)
66 elif isinstance(dest, Path):
67 dest = dest.open('wb')
68
69 with dest as f:
70 resized.save(f, format=im.format)
71
72 return Path(f.name)
73
74
75 def resize(image_path, new_size, dest=None):
76 """Resize an image to a new file, and return its path
77
78 @param image_path(str, Path): path of the original image
79 @param new_size(tuple[int, int]): size to use for new image
80 @param dest(None, Path, file): where the resized image must be stored, can be:
81 - None: use a temporary file
82 - Path: path to the file to create/overwrite
83 - file: a file object which must be opened for writing in binary mode
84 @return (Path): path of the resized file.
85 The image at this path should be deleted after use
86 """
87 return threads.deferToThread(_resizeBlocking, image_path, new_size, dest)