annotate sat/tools/image.py @ 3582:71516731d0aa

core (memory/sqla): database migration using Alembic: Alembic database migration tool, which is the recommended one for SQLAlchemy has been integrated. When a database is created, it will be used to stamp to current (head) revision, otherwise, DB will be checked to see if it needs to be updated, and upgrade will be triggered if necessary.
author Goffi <goffi@goffi.org>
date Fri, 25 Jun 2021 17:55:23 +0200
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