annotate sat/tools/image.py @ 3725:9b45f0f168cf

tools (common): new `async_utils` module with an async version of `lru_cache`
author Goffi <goffi@goffi.org>
date Tue, 25 Jan 2022 17:25:10 +0100
parents 7550ae9cfbac
children 524856bd7b19
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
1 #!/usr/bin/env python3
3137
559a625a236b fixed shebangs
Goffi <goffi@goffi.org>
parents: 3136
diff changeset
2
3480
7550ae9cfbac Renamed the project from "Salut à Toi" to "Libervia":
Goffi <goffi@goffi.org>
parents: 3479
diff changeset
3 # Libervia: an XMPP client
3479
be6d91572633 date update
Goffi <goffi@goffi.org>
parents: 3477
diff changeset
4 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org)
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
5
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
6 # This program is free software: you can redistribute it and/or modify
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
7 # it under the terms of the GNU Affero General Public License as published by
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
8 # the Free Software Foundation, either version 3 of the License, or
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
9 # (at your option) any later version.
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
10
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
11 # This program is distributed in the hope that it will be useful,
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
14 # GNU Affero General Public License for more details.
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
15
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
16 # You should have received a copy of the GNU Affero General Public License
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
18
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
19 """Methods to manipulate images"""
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
20
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
21 import tempfile
3252
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
22 import mimetypes
3332
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
23 from PIL import Image, ImageOps
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
24 from pathlib import Path
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
25 from twisted.internet import threads
3259
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
26 from sat.core.i18n import _
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
27 from sat.core import exceptions
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
28 from sat.core.log import getLogger
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
29
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
30 log = getLogger(__name__)
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
31
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
32 try:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
33 import cairosvg
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
34 except Exception as e:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
35 log.warning(_("SVG support not available, please install cairosvg: {e}").format(
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
36 e=e))
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
37 cairosvg = None
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
38
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
39
3220
4fbea7f1e012 tools (images): methods renaming
Goffi <goffi@goffi.org>
parents: 3200
diff changeset
40 def check(host, path, max_size=None):
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
41 """Analyze image and return a report
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
42
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
43 report will indicate if image is too large, and the recommended new size if this is
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
44 the case
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
45 @param host: SàT instance
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
46 @param path(str, pathlib.Path): image to open
3200
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
47 @param max_size(tuple[int, int]): maximum accepted size of image
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
48 None to use value set in config
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
49 @return dict: report on image, with following keys:
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
50 - too_large: true if image is oversized
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
51 - recommended_size: if too_large is True, recommended size to use
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
52 """
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
53 report = {}
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
54 image = Image.open(path)
3200
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
55 if max_size is None:
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
56 max_size = tuple(host.memory.getConfig(None, "image_max", (1200, 720)))
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
57 if image.size > max_size:
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
58 report['too_large'] = True
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
59 if image.size[0] > max_size[0]:
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
60 factor = max_size[0] / image.size[0]
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
61 if image.size[1] * factor > max_size[1]:
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
62 factor = max_size[1] / image.size[1]
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
63 else:
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
64 factor = max_size[1] / image.size[1]
3157
8b4354b5c05f tools (images): fixed type for recommended_size.
Goffi <goffi@goffi.org>
parents: 3137
diff changeset
65 report['recommended_size'] = [int(image.width*factor), int(image.height*factor)]
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
66 else:
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
67 report['too_large'] = False
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
68
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
69 return report
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
70
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
71
3332
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
72 def _resize_blocking(image_path, new_size, dest, fix_orientation):
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
73 im_path = Path(image_path)
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
74 im = Image.open(im_path)
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
75 resized = im.resize(new_size, Image.LANCZOS)
3332
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
76 if fix_orientation:
3477
9498f32ba6f7 tools (image): fix bad method name used when `fix_orientation` is set in `resize`
Goffi <goffi@goffi.org>
parents: 3332
diff changeset
77 resized = ImageOps.exif_transpose(resized)
3200
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
78
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
79 if dest is None:
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
80 dest = tempfile.NamedTemporaryFile(suffix=im_path.suffix, delete=False)
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
81 elif isinstance(dest, Path):
3220
4fbea7f1e012 tools (images): methods renaming
Goffi <goffi@goffi.org>
parents: 3200
diff changeset
82 dest = dest.open('wb')
3200
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
83
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
84 with dest as f:
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
85 resized.save(f, format=im.format)
3200
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
86
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
87 return Path(f.name)
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
88
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
89
3332
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
90 def resize(image_path, new_size, dest=None, fix_orientation=True):
3200
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
91 """Resize an image to a new file, and return its path
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
92
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
93 @param image_path(str, Path): path of the original image
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
94 @param new_size(tuple[int, int]): size to use for new image
3200
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
95 @param dest(None, Path, file): where the resized image must be stored, can be:
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
96 - None: use a temporary file
3259
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
97 file will be converted to PNG
3200
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
98 - Path: path to the file to create/overwrite
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
99 - file: a file object which must be opened for writing in binary mode
3332
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
100 @param fix_orientation: if True, use EXIF data to set orientation
3200
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
101 @return (Path): path of the resized file.
5c3bf37f2202 tools (images): max_size can now be manually specified in checkImage and dest in resizeImage:
Goffi <goffi@goffi.org>
parents: 3157
diff changeset
102 The image at this path should be deleted after use
3066
2cc2f65379f7 core: added imageCheck and imageResize methods:
Goffi <goffi@goffi.org>
parents:
diff changeset
103 """
3332
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
104 return threads.deferToThread(
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
105 _resize_blocking, image_path, new_size, dest, fix_orientation)
3252
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
106
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
107
3332
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
108 def _convert_blocking(image_path, dest, extra):
3259
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
109 media_type = mimetypes.guess_type(str(image_path), strict=False)[0]
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
110
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
111 if dest is None:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
112 dest = tempfile.NamedTemporaryFile(suffix=".png", delete=False)
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
113 filepath = Path(dest.name)
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
114 elif isinstance(dest, Path):
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
115 filepath = dest
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
116 else:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
117 # we should have a file-like object
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
118 try:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
119 name = dest.name
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
120 except AttributeError:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
121 name = None
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
122 if name:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
123 try:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
124 filepath = Path(name)
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
125 except TypeError:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
126 filepath = Path('noname.png')
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
127 else:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
128 filepath = Path('noname.png')
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
129
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
130 if media_type == "image/svg+xml":
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
131 if cairosvg is None:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
132 raise exceptions.MissingModule(
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
133 f"Can't convert SVG image at {image_path} due to missing CairoSVG module")
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
134 width, height = extra.get('width'), extra.get('height')
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
135 cairosvg.svg2png(
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
136 url=str(image_path), write_to=dest,
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
137 output_width=width, output_height=height
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
138 )
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
139 else:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
140 suffix = filepath.suffix
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
141 if not suffix:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
142 raise ValueError(
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
143 "A suffix is missing for destination, it is needed to determine file "
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
144 "format")
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
145 if not suffix in Image.EXTENSION:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
146 Image.init()
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
147 try:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
148 im_format = Image.EXTENSION[suffix]
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
149 except KeyError:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
150 raise ValueError(
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
151 "Dest image format can't be determined, {suffix!r} suffix is unknown"
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
152 )
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
153 im = Image.open(image_path)
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
154 im.save(dest, format=im_format)
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
155
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
156 log.debug(f"image {image_path} has been converted to {filepath}")
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
157 return filepath
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
158
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
159
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
160 def convert(image_path, dest=None, extra=None):
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
161 """Convert an image to a new file, and return its path
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
162
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
163 @param image_path(str, Path): path of the image to convert
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
164 @param dest(None, Path, file): where the converted image must be stored, can be:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
165 - None: use a temporary file
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
166 - Path: path to the file to create/overwrite
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
167 - file: a file object which must be opened for writing in binary mode
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
168 @param extra(None, dict): conversion options
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
169 if image_path link to a SVG file, following options can be used:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
170 - width: destination width
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
171 - height: destination height
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
172 @return (Path): path of the converted file.
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
173 a generic name is used if dest is an unnamed file like object
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
174 """
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
175 image_path = Path(image_path)
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
176 if not image_path.is_file():
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
177 raise ValueError(f"There is no file at {image_path}!")
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
178 if extra is None:
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
179 extra = {}
3332
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
180 return threads.deferToThread(_convert_blocking, image_path, dest, extra)
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
181
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
182
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
183 def __fix_orientation_blocking(image_path):
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
184 im = Image.open(image_path)
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
185 im_format = im.format
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
186 exif = im.getexif()
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
187 orientation = exif.get(0x0112)
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
188 if orientation is None or orientation<2:
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
189 # nothing to do
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
190 return False
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
191 im = ImageOps.exif_transpose(im)
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
192 im.save(image_path, im_format)
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
193 log.debug(f"image {image_path} orientation has been fixed")
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
194 return True
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
195
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
196
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
197 def fix_orientation(image_path: Path) -> bool:
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
198 """Apply orientation found in EXIF data if any
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
199
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
200 @param image_path: image location, image will be modified in place
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
201 @return True if image has been modified
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
202 """
1512cbd6c4ac tools (image): fix_orientation on resize + `fix_orientation` method:
Goffi <goffi@goffi.org>
parents: 3259
diff changeset
203 return threads.deferToThread(__fix_orientation_blocking, image_path)
3259
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
204
f300d78f08f3 core: image convertion + SVG support:
Goffi <goffi@goffi.org>
parents: 3252
diff changeset
205
3252
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
206 def guess_type(source):
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
207 """Guess image media type
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
208
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
209 @param source(str, Path, file): image to guess type
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
210 @return (str, None): media type, or None if we can't guess
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
211 """
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
212 if isinstance(source, str):
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
213 source = Path(source)
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
214
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
215 if isinstance(source, Path):
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
216 # we first try to guess from file name
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
217 media_type = mimetypes.guess_type(source, strict=False)[0]
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
218 if media_type is not None:
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
219 return media_type
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
220
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
221 # file name is not enough, we try to open it
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
222 img = Image.open(source)
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
223 try:
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
224 return Image.MIME[img.format]
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
225 except KeyError:
54934ee3f69c tools (image): added a guess_type method to guess media type:
Goffi <goffi@goffi.org>
parents: 3220
diff changeset
226 return None