fotolab 0.27.2__tar.gz → 0.28.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fotolab-0.27.2 → fotolab-0.28.1}/.gitignore +2 -1
- {fotolab-0.27.2 → fotolab-0.28.1}/CHANGELOG.md +18 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/PKG-INFO +53 -18
- {fotolab-0.27.2 → fotolab-0.28.1}/README.md +52 -17
- fotolab-0.28.1/fotolab/__init__.py +106 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/fotolab/cli.py +0 -17
- {fotolab-0.27.2 → fotolab-0.28.1}/fotolab/subcommands/animate.py +17 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/fotolab/subcommands/border.py +17 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/fotolab/subcommands/contrast.py +28 -6
- {fotolab-0.27.2 → fotolab-0.28.1}/fotolab/subcommands/halftone.py +23 -27
- {fotolab-0.27.2 → fotolab-0.28.1}/fotolab/subcommands/info.py +5 -1
- {fotolab-0.27.2 → fotolab-0.28.1}/fotolab/subcommands/montage.py +17 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/fotolab/subcommands/resize.py +17 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/fotolab/subcommands/rotate.py +17 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/fotolab/subcommands/sharpen.py +21 -32
- {fotolab-0.27.2 → fotolab-0.28.1}/fotolab/subcommands/watermark.py +17 -0
- fotolab-0.27.2/fotolab/__init__.py +0 -70
- {fotolab-0.27.2 → fotolab-0.28.1}/.coveragerc +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/.pre-commit-config.yaml +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/.python-version +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/CONTRIBUTING.md +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/LICENSE.md +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/Pipfile +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/Pipfile.lock +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/docs/Makefile +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/docs/make.bat +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/docs/source/CHANGELOG.md +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/docs/source/CONTRIBUTING.md +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/docs/source/LICENSE.md +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/docs/source/README.md +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/docs/source/_static/logo.jpg +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/docs/source/conf.py +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/docs/source/index.rst +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/fotolab/__main__.py +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/fotolab/subcommands/__init__.py +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/fotolab/subcommands/auto.py +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/fotolab/subcommands/env.py +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/noxfile.py +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/pyproject.toml +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/tests/__init__.py +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/tests/conftest.py +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/tests/test_env_subcommand.py +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/tests/test_help_flag.py +0 -0
- {fotolab-0.27.2 → fotolab-0.28.1}/tests/test_quiet_flag.py +0 -0
@@ -7,6 +7,24 @@ and this project adheres to [0-based versioning](https://0ver.org/).
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
## v0.28.1 (2025-03-16)
|
11
|
+
|
12
|
+
- Add typehint to `_get_output_filename` function
|
13
|
+
- Fix incorrect log message formatting in save_gif_image function
|
14
|
+
- Handle `FileNotFoundError` and `IOError` exceptions when opening images
|
15
|
+
- Handle `OSError` when opening image files.
|
16
|
+
- Ignore aider files
|
17
|
+
- Revert refactoring on open image using default program
|
18
|
+
|
19
|
+
## v0.28.0 (2025-03-09)
|
20
|
+
|
21
|
+
- Handle exception of private function with exif info
|
22
|
+
- Move `--output_dir` and `--open` args to subcommand that process image
|
23
|
+
- Refactor getting output filename when saving image
|
24
|
+
- Remove `--output-dir` and `--open` args from main cli
|
25
|
+
- Update help message in readme
|
26
|
+
- Use single `save_gif_image` function in sharpen and halftone subcommand
|
27
|
+
|
10
28
|
## v0.27.2 (2025-03-02)
|
11
29
|
|
12
30
|
- Add missing latest copyright year
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fotolab
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.28.1
|
4
4
|
Summary: A console program that manipulate images.
|
5
5
|
Keywords: photography,photo
|
6
6
|
Author-email: Kian-Meng Ang <kianmeng@cpan.org>
|
@@ -60,7 +60,7 @@ fotolab -h
|
|
60
60
|
<!--help !-->
|
61
61
|
|
62
62
|
```console
|
63
|
-
usage: fotolab [-h] [-o] [-
|
63
|
+
usage: fotolab [-h] [-o] [-q] [-v] [-d] [-V]
|
64
64
|
{animate,auto,border,contrast,env,halftone,info,montage,resize,rotate,sharpen,watermark} ...
|
65
65
|
|
66
66
|
A console program to manipulate photos.
|
@@ -88,9 +88,6 @@ positional arguments:
|
|
88
88
|
options:
|
89
89
|
-h, --help show this help message and exit
|
90
90
|
-o, --overwrite overwrite existing image
|
91
|
-
-op, --open open the image using default program (default: 'False')
|
92
|
-
-od, --output-dir OUTPUT_DIR
|
93
|
-
set default output folder (default: 'output')
|
94
91
|
-q, --quiet suppress all logging
|
95
92
|
-v, --verbose show verbosity of debugging log, use -vv, -vvv for more details
|
96
93
|
-d, --debug show debugging log and stacktrace
|
@@ -108,7 +105,8 @@ fotolab animate -h
|
|
108
105
|
<!--help-animate !-->
|
109
106
|
|
110
107
|
```console
|
111
|
-
usage: fotolab animate [-h] [-f FORMAT] [-d DURATION] [-l LOOP]
|
108
|
+
usage: fotolab animate [-h] [-f FORMAT] [-d DURATION] [-l LOOP] [-op]
|
109
|
+
[-od OUTPUT_DIR]
|
112
110
|
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
113
111
|
|
114
112
|
positional arguments:
|
@@ -120,6 +118,10 @@ options:
|
|
120
118
|
-d, --duration DURATION
|
121
119
|
set the duration in milliseconds (default: '2500')
|
122
120
|
-l, --loop LOOP set the loop cycle (default: '0')
|
121
|
+
-op, --open open the image using default program (default:
|
122
|
+
'False')
|
123
|
+
-od, --output-dir OUTPUT_DIR
|
124
|
+
set default output folder (default: 'output')
|
123
125
|
```
|
124
126
|
|
125
127
|
<!--help-animate !-->
|
@@ -154,7 +156,7 @@ fotolab border -h
|
|
154
156
|
|
155
157
|
```console
|
156
158
|
usage: fotolab border [-h] [-c COLOR] [-w WIDTH] [-wt WIDTH] [-wr WIDTH]
|
157
|
-
[-wb WIDTH] [-wl WIDTH]
|
159
|
+
[-wb WIDTH] [-wl WIDTH] [-op] [-od OUTPUT_DIR]
|
158
160
|
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
159
161
|
|
160
162
|
positional arguments:
|
@@ -172,6 +174,10 @@ options:
|
|
172
174
|
set the width of bottom border (default: '0')
|
173
175
|
-wl, --width-left WIDTH
|
174
176
|
set the width of left border (default: '0')
|
177
|
+
-op, --open open the image using default program (default:
|
178
|
+
'False')
|
179
|
+
-od, --output-dir OUTPUT_DIR
|
180
|
+
set default output folder (default: 'output')
|
175
181
|
```
|
176
182
|
|
177
183
|
<!--help-border !-->
|
@@ -185,15 +191,20 @@ fotolab contrast -h
|
|
185
191
|
<!--help-contrast !-->
|
186
192
|
|
187
193
|
```console
|
188
|
-
usage: fotolab contrast [-h] [-c CUTOFF]
|
194
|
+
usage: fotolab contrast [-h] [-c CUTOFF] [-op] [-od OUTPUT_DIR]
|
195
|
+
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
189
196
|
|
190
197
|
positional arguments:
|
191
|
-
IMAGE_FILENAMES
|
198
|
+
IMAGE_FILENAMES set the image filename
|
192
199
|
|
193
200
|
options:
|
194
|
-
-h, --help
|
195
|
-
-c, --cutoff CUTOFF
|
196
|
-
|
201
|
+
-h, --help show this help message and exit
|
202
|
+
-c, --cutoff CUTOFF set the percentage of lightest or darkest pixels to
|
203
|
+
discard from histogram (default: '1')
|
204
|
+
-op, --open open the image using default program (default:
|
205
|
+
'False')
|
206
|
+
-od, --output-dir OUTPUT_DIR
|
207
|
+
set default output folder (default: 'output')
|
197
208
|
```
|
198
209
|
|
199
210
|
<!--help-contrast !-->
|
@@ -230,7 +241,7 @@ fotolab rotate -h
|
|
230
241
|
<!--help-rotate !-->
|
231
242
|
|
232
243
|
```console
|
233
|
-
usage: fotolab rotate [-h] [-r ROTATION] [-cw]
|
244
|
+
usage: fotolab rotate [-h] [-r ROTATION] [-cw] [-op] [-od OUTPUT_DIR]
|
234
245
|
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
235
246
|
|
236
247
|
positional arguments:
|
@@ -241,6 +252,10 @@ options:
|
|
241
252
|
-r, --rotation ROTATION
|
242
253
|
Rotation angle in degrees (default: '0')
|
243
254
|
-cw, --clockwise Rotate clockwise (default: 'False)
|
255
|
+
-op, --open open the image using default program (default:
|
256
|
+
'False')
|
257
|
+
-od, --output-dir OUTPUT_DIR
|
258
|
+
set default output folder (default: 'output')
|
244
259
|
```
|
245
260
|
|
246
261
|
<!--help-rotate !-->
|
@@ -254,13 +269,18 @@ fotolab montage -h
|
|
254
269
|
<!--help-montage !-->
|
255
270
|
|
256
271
|
```console
|
257
|
-
usage: fotolab montage [-h]
|
272
|
+
usage: fotolab montage [-h] [-op] [-od OUTPUT_DIR]
|
273
|
+
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
258
274
|
|
259
275
|
positional arguments:
|
260
|
-
IMAGE_FILENAMES
|
276
|
+
IMAGE_FILENAMES set the image filenames
|
261
277
|
|
262
278
|
options:
|
263
|
-
-h, --help
|
279
|
+
-h, --help show this help message and exit
|
280
|
+
-op, --open open the image using default program (default:
|
281
|
+
'False')
|
282
|
+
-od, --output-dir OUTPUT_DIR
|
283
|
+
set default output folder (default: 'output')
|
264
284
|
```
|
265
285
|
|
266
286
|
<!--help-montage !-->
|
@@ -274,7 +294,8 @@ fotolab resize -h
|
|
274
294
|
<!--help-resize !-->
|
275
295
|
|
276
296
|
```console
|
277
|
-
usage: fotolab resize [-h] [-c] [-l CANVAS_COLOR] [-W WIDTH | -H HEIGHT]
|
297
|
+
usage: fotolab resize [-h] [-c] [-l CANVAS_COLOR] [-W WIDTH | -H HEIGHT] [-op]
|
298
|
+
[-od OUTPUT_DIR]
|
278
299
|
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
279
300
|
|
280
301
|
positional arguments:
|
@@ -288,6 +309,10 @@ options:
|
|
288
309
|
'black')
|
289
310
|
-W, --width WIDTH set the width of the image (default: '600')
|
290
311
|
-H, --height HEIGHT set the height of the image (default: '277')
|
312
|
+
-op, --open open the image using default program (default:
|
313
|
+
'False')
|
314
|
+
-od, --output-dir OUTPUT_DIR
|
315
|
+
set default output folder (default: 'output')
|
291
316
|
```
|
292
317
|
|
293
318
|
<!--help-resize !-->
|
@@ -302,6 +327,7 @@ fotolab sharpen -h
|
|
302
327
|
|
303
328
|
```console
|
304
329
|
usage: fotolab sharpen [-h] [-r RADIUS] [-p PERCENT] [-t THRESHOLD] [-ba]
|
330
|
+
[-op] [-od OUTPUT_DIR]
|
305
331
|
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
306
332
|
|
307
333
|
positional arguments:
|
@@ -317,6 +343,10 @@ options:
|
|
317
343
|
set the minimum brightness changed to be sharpened
|
318
344
|
(default: '3')
|
319
345
|
-ba, --before-after generate a GIF showing before and after changes
|
346
|
+
-op, --open open the image using default program (default:
|
347
|
+
'False')
|
348
|
+
-od, --output-dir OUTPUT_DIR
|
349
|
+
set default output folder (default: 'output')
|
320
350
|
```
|
321
351
|
|
322
352
|
<!--help-sharpen !-->
|
@@ -334,7 +364,8 @@ usage: fotolab watermark [-h] [-t WATERMARK_TEXT]
|
|
334
364
|
[-p {top-left,top-right,bottom-left,bottom-right}]
|
335
365
|
[-pd PADDING] [-fs FONT_SIZE] [-fc FONT_COLOR]
|
336
366
|
[-ow OUTLINE_WIDTH] [-oc OUTLINE_COLOR] [--camera]
|
337
|
-
[-l | --lowercase | --no-lowercase]
|
367
|
+
[-l | --lowercase | --no-lowercase] [-op]
|
368
|
+
[-od OUTPUT_DIR]
|
338
369
|
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
339
370
|
|
340
371
|
positional arguments:
|
@@ -364,6 +395,10 @@ options:
|
|
364
395
|
--camera use camera metadata as watermark
|
365
396
|
-l, --lowercase, --no-lowercase
|
366
397
|
lowercase the watermark text
|
398
|
+
-op, --open open the image using default program (default:
|
399
|
+
'False')
|
400
|
+
-od, --output-dir OUTPUT_DIR
|
401
|
+
set default output folder (default: 'output')
|
367
402
|
```
|
368
403
|
|
369
404
|
<!--help-watermark !-->
|
@@ -37,7 +37,7 @@ fotolab -h
|
|
37
37
|
<!--help !-->
|
38
38
|
|
39
39
|
```console
|
40
|
-
usage: fotolab [-h] [-o] [-
|
40
|
+
usage: fotolab [-h] [-o] [-q] [-v] [-d] [-V]
|
41
41
|
{animate,auto,border,contrast,env,halftone,info,montage,resize,rotate,sharpen,watermark} ...
|
42
42
|
|
43
43
|
A console program to manipulate photos.
|
@@ -65,9 +65,6 @@ positional arguments:
|
|
65
65
|
options:
|
66
66
|
-h, --help show this help message and exit
|
67
67
|
-o, --overwrite overwrite existing image
|
68
|
-
-op, --open open the image using default program (default: 'False')
|
69
|
-
-od, --output-dir OUTPUT_DIR
|
70
|
-
set default output folder (default: 'output')
|
71
68
|
-q, --quiet suppress all logging
|
72
69
|
-v, --verbose show verbosity of debugging log, use -vv, -vvv for more details
|
73
70
|
-d, --debug show debugging log and stacktrace
|
@@ -85,7 +82,8 @@ fotolab animate -h
|
|
85
82
|
<!--help-animate !-->
|
86
83
|
|
87
84
|
```console
|
88
|
-
usage: fotolab animate [-h] [-f FORMAT] [-d DURATION] [-l LOOP]
|
85
|
+
usage: fotolab animate [-h] [-f FORMAT] [-d DURATION] [-l LOOP] [-op]
|
86
|
+
[-od OUTPUT_DIR]
|
89
87
|
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
90
88
|
|
91
89
|
positional arguments:
|
@@ -97,6 +95,10 @@ options:
|
|
97
95
|
-d, --duration DURATION
|
98
96
|
set the duration in milliseconds (default: '2500')
|
99
97
|
-l, --loop LOOP set the loop cycle (default: '0')
|
98
|
+
-op, --open open the image using default program (default:
|
99
|
+
'False')
|
100
|
+
-od, --output-dir OUTPUT_DIR
|
101
|
+
set default output folder (default: 'output')
|
100
102
|
```
|
101
103
|
|
102
104
|
<!--help-animate !-->
|
@@ -131,7 +133,7 @@ fotolab border -h
|
|
131
133
|
|
132
134
|
```console
|
133
135
|
usage: fotolab border [-h] [-c COLOR] [-w WIDTH] [-wt WIDTH] [-wr WIDTH]
|
134
|
-
[-wb WIDTH] [-wl WIDTH]
|
136
|
+
[-wb WIDTH] [-wl WIDTH] [-op] [-od OUTPUT_DIR]
|
135
137
|
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
136
138
|
|
137
139
|
positional arguments:
|
@@ -149,6 +151,10 @@ options:
|
|
149
151
|
set the width of bottom border (default: '0')
|
150
152
|
-wl, --width-left WIDTH
|
151
153
|
set the width of left border (default: '0')
|
154
|
+
-op, --open open the image using default program (default:
|
155
|
+
'False')
|
156
|
+
-od, --output-dir OUTPUT_DIR
|
157
|
+
set default output folder (default: 'output')
|
152
158
|
```
|
153
159
|
|
154
160
|
<!--help-border !-->
|
@@ -162,15 +168,20 @@ fotolab contrast -h
|
|
162
168
|
<!--help-contrast !-->
|
163
169
|
|
164
170
|
```console
|
165
|
-
usage: fotolab contrast [-h] [-c CUTOFF]
|
171
|
+
usage: fotolab contrast [-h] [-c CUTOFF] [-op] [-od OUTPUT_DIR]
|
172
|
+
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
166
173
|
|
167
174
|
positional arguments:
|
168
|
-
IMAGE_FILENAMES
|
175
|
+
IMAGE_FILENAMES set the image filename
|
169
176
|
|
170
177
|
options:
|
171
|
-
-h, --help
|
172
|
-
-c, --cutoff CUTOFF
|
173
|
-
|
178
|
+
-h, --help show this help message and exit
|
179
|
+
-c, --cutoff CUTOFF set the percentage of lightest or darkest pixels to
|
180
|
+
discard from histogram (default: '1')
|
181
|
+
-op, --open open the image using default program (default:
|
182
|
+
'False')
|
183
|
+
-od, --output-dir OUTPUT_DIR
|
184
|
+
set default output folder (default: 'output')
|
174
185
|
```
|
175
186
|
|
176
187
|
<!--help-contrast !-->
|
@@ -207,7 +218,7 @@ fotolab rotate -h
|
|
207
218
|
<!--help-rotate !-->
|
208
219
|
|
209
220
|
```console
|
210
|
-
usage: fotolab rotate [-h] [-r ROTATION] [-cw]
|
221
|
+
usage: fotolab rotate [-h] [-r ROTATION] [-cw] [-op] [-od OUTPUT_DIR]
|
211
222
|
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
212
223
|
|
213
224
|
positional arguments:
|
@@ -218,6 +229,10 @@ options:
|
|
218
229
|
-r, --rotation ROTATION
|
219
230
|
Rotation angle in degrees (default: '0')
|
220
231
|
-cw, --clockwise Rotate clockwise (default: 'False)
|
232
|
+
-op, --open open the image using default program (default:
|
233
|
+
'False')
|
234
|
+
-od, --output-dir OUTPUT_DIR
|
235
|
+
set default output folder (default: 'output')
|
221
236
|
```
|
222
237
|
|
223
238
|
<!--help-rotate !-->
|
@@ -231,13 +246,18 @@ fotolab montage -h
|
|
231
246
|
<!--help-montage !-->
|
232
247
|
|
233
248
|
```console
|
234
|
-
usage: fotolab montage [-h]
|
249
|
+
usage: fotolab montage [-h] [-op] [-od OUTPUT_DIR]
|
250
|
+
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
235
251
|
|
236
252
|
positional arguments:
|
237
|
-
IMAGE_FILENAMES
|
253
|
+
IMAGE_FILENAMES set the image filenames
|
238
254
|
|
239
255
|
options:
|
240
|
-
-h, --help
|
256
|
+
-h, --help show this help message and exit
|
257
|
+
-op, --open open the image using default program (default:
|
258
|
+
'False')
|
259
|
+
-od, --output-dir OUTPUT_DIR
|
260
|
+
set default output folder (default: 'output')
|
241
261
|
```
|
242
262
|
|
243
263
|
<!--help-montage !-->
|
@@ -251,7 +271,8 @@ fotolab resize -h
|
|
251
271
|
<!--help-resize !-->
|
252
272
|
|
253
273
|
```console
|
254
|
-
usage: fotolab resize [-h] [-c] [-l CANVAS_COLOR] [-W WIDTH | -H HEIGHT]
|
274
|
+
usage: fotolab resize [-h] [-c] [-l CANVAS_COLOR] [-W WIDTH | -H HEIGHT] [-op]
|
275
|
+
[-od OUTPUT_DIR]
|
255
276
|
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
256
277
|
|
257
278
|
positional arguments:
|
@@ -265,6 +286,10 @@ options:
|
|
265
286
|
'black')
|
266
287
|
-W, --width WIDTH set the width of the image (default: '600')
|
267
288
|
-H, --height HEIGHT set the height of the image (default: '277')
|
289
|
+
-op, --open open the image using default program (default:
|
290
|
+
'False')
|
291
|
+
-od, --output-dir OUTPUT_DIR
|
292
|
+
set default output folder (default: 'output')
|
268
293
|
```
|
269
294
|
|
270
295
|
<!--help-resize !-->
|
@@ -279,6 +304,7 @@ fotolab sharpen -h
|
|
279
304
|
|
280
305
|
```console
|
281
306
|
usage: fotolab sharpen [-h] [-r RADIUS] [-p PERCENT] [-t THRESHOLD] [-ba]
|
307
|
+
[-op] [-od OUTPUT_DIR]
|
282
308
|
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
283
309
|
|
284
310
|
positional arguments:
|
@@ -294,6 +320,10 @@ options:
|
|
294
320
|
set the minimum brightness changed to be sharpened
|
295
321
|
(default: '3')
|
296
322
|
-ba, --before-after generate a GIF showing before and after changes
|
323
|
+
-op, --open open the image using default program (default:
|
324
|
+
'False')
|
325
|
+
-od, --output-dir OUTPUT_DIR
|
326
|
+
set default output folder (default: 'output')
|
297
327
|
```
|
298
328
|
|
299
329
|
<!--help-sharpen !-->
|
@@ -311,7 +341,8 @@ usage: fotolab watermark [-h] [-t WATERMARK_TEXT]
|
|
311
341
|
[-p {top-left,top-right,bottom-left,bottom-right}]
|
312
342
|
[-pd PADDING] [-fs FONT_SIZE] [-fc FONT_COLOR]
|
313
343
|
[-ow OUTLINE_WIDTH] [-oc OUTLINE_COLOR] [--camera]
|
314
|
-
[-l | --lowercase | --no-lowercase]
|
344
|
+
[-l | --lowercase | --no-lowercase] [-op]
|
345
|
+
[-od OUTPUT_DIR]
|
315
346
|
IMAGE_FILENAMES [IMAGE_FILENAMES ...]
|
316
347
|
|
317
348
|
positional arguments:
|
@@ -341,6 +372,10 @@ options:
|
|
341
372
|
--camera use camera metadata as watermark
|
342
373
|
-l, --lowercase, --no-lowercase
|
343
374
|
lowercase the watermark text
|
375
|
+
-op, --open open the image using default program (default:
|
376
|
+
'False')
|
377
|
+
-od, --output-dir OUTPUT_DIR
|
378
|
+
set default output folder (default: 'output')
|
344
379
|
```
|
345
380
|
|
346
381
|
<!--help-watermark !-->
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# Copyright (C) 2024,2025 Kian-Meng Ang
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify it under
|
4
|
+
# the terms of the GNU Affero General Public License as published by the Free
|
5
|
+
# Software Foundation, either version 3 of the License, or (at your option) any
|
6
|
+
# later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
9
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
10
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
11
|
+
# details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Affero General Public License
|
14
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
"""A console program that manipulate images."""
|
17
|
+
|
18
|
+
import argparse
|
19
|
+
import logging
|
20
|
+
import subprocess
|
21
|
+
import sys
|
22
|
+
from pathlib import Path
|
23
|
+
|
24
|
+
from PIL import Image
|
25
|
+
|
26
|
+
__version__ = "0.28.1"
|
27
|
+
|
28
|
+
log = logging.getLogger(__name__)
|
29
|
+
|
30
|
+
|
31
|
+
def save_gif_image(
|
32
|
+
args: argparse.Namespace,
|
33
|
+
image_filename: str,
|
34
|
+
original_image: Image.Image,
|
35
|
+
after_image: Image.Image,
|
36
|
+
subcommand: str,
|
37
|
+
) -> None:
|
38
|
+
"""Save the original and after image."""
|
39
|
+
image_file = Path(image_filename)
|
40
|
+
new_filename = Path(
|
41
|
+
args.output_dir,
|
42
|
+
image_file.with_name(f"{subcommand}_gif_{image_file.stem}.gif"),
|
43
|
+
)
|
44
|
+
new_filename.parent.mkdir(parents=True, exist_ok=True)
|
45
|
+
|
46
|
+
log.info("%s gif image: %s", subcommand, new_filename)
|
47
|
+
original_image.save(
|
48
|
+
new_filename,
|
49
|
+
format="gif",
|
50
|
+
append_images=[after_image],
|
51
|
+
save_all=True,
|
52
|
+
duration=2500,
|
53
|
+
loop=0,
|
54
|
+
optimize=True,
|
55
|
+
)
|
56
|
+
|
57
|
+
if args.open:
|
58
|
+
_open_image(new_filename)
|
59
|
+
|
60
|
+
|
61
|
+
def save_image(args, new_image, output_filename, subcommand):
|
62
|
+
"""Save image after image operation.
|
63
|
+
|
64
|
+
Args:
|
65
|
+
args (argparse.Namespace): Config from command line arguments
|
66
|
+
new_image(PIL.Image.Image): Modified image
|
67
|
+
output_filename(str): Save filename image
|
68
|
+
subcommand(str): Subcommand used to call this function
|
69
|
+
|
70
|
+
Returns:
|
71
|
+
None
|
72
|
+
"""
|
73
|
+
image_file = Path(output_filename)
|
74
|
+
new_filename = _get_output_filename(args, image_file, subcommand)
|
75
|
+
log.info("%s image: %s", subcommand, new_filename.resolve())
|
76
|
+
new_image.save(new_filename)
|
77
|
+
|
78
|
+
if args.open:
|
79
|
+
_open_image(new_filename)
|
80
|
+
|
81
|
+
|
82
|
+
def _get_output_filename(
|
83
|
+
args: argparse.Namespace, image_file: Path, subcommand: str
|
84
|
+
) -> Path:
|
85
|
+
"""Build and return output filename based on the command line options."""
|
86
|
+
if args.overwrite:
|
87
|
+
return image_file.with_name(image_file.name)
|
88
|
+
|
89
|
+
output_dir = Path(args.output_dir)
|
90
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
91
|
+
return output_dir / image_file.with_name(f"{subcommand}_{image_file.name}")
|
92
|
+
|
93
|
+
|
94
|
+
def _open_image(filename):
|
95
|
+
"""Open generated image using default program."""
|
96
|
+
try:
|
97
|
+
if sys.platform == "linux":
|
98
|
+
subprocess.call(["xdg-open", filename])
|
99
|
+
elif sys.platform == "darwin":
|
100
|
+
subprocess.call(["open", filename])
|
101
|
+
elif sys.platform == "windows":
|
102
|
+
subprocess.Popen(["open", filename])
|
103
|
+
log.info("open image: %s", filename.resolve())
|
104
|
+
|
105
|
+
except OSError as e:
|
106
|
+
log.error("Error opening image: %s -> %s", filename, e)
|
@@ -78,23 +78,6 @@ def build_parser() -> argparse.ArgumentParser:
|
|
78
78
|
help="overwrite existing image",
|
79
79
|
)
|
80
80
|
|
81
|
-
parser.add_argument(
|
82
|
-
"-op",
|
83
|
-
"--open",
|
84
|
-
default=False,
|
85
|
-
action="store_true",
|
86
|
-
dest="open",
|
87
|
-
help="open the image using default program (default: '%(default)s')",
|
88
|
-
)
|
89
|
-
|
90
|
-
parser.add_argument(
|
91
|
-
"-od",
|
92
|
-
"--output-dir",
|
93
|
-
dest="output_dir",
|
94
|
-
default="output",
|
95
|
-
help="set default output folder (default: '%(default)s')",
|
96
|
-
)
|
97
|
-
|
98
81
|
parser.add_argument(
|
99
82
|
"-q",
|
100
83
|
"--quiet",
|
@@ -72,6 +72,23 @@ def build_subparser(subparsers) -> None:
|
|
72
72
|
metavar="LOOP",
|
73
73
|
)
|
74
74
|
|
75
|
+
animate_parser.add_argument(
|
76
|
+
"-op",
|
77
|
+
"--open",
|
78
|
+
default=False,
|
79
|
+
action="store_true",
|
80
|
+
dest="open",
|
81
|
+
help="open the image using default program (default: '%(default)s')",
|
82
|
+
)
|
83
|
+
|
84
|
+
animate_parser.add_argument(
|
85
|
+
"-od",
|
86
|
+
"--output-dir",
|
87
|
+
dest="output_dir",
|
88
|
+
default="output",
|
89
|
+
help="set default output folder (default: '%(default)s')",
|
90
|
+
)
|
91
|
+
|
75
92
|
|
76
93
|
def run(args: argparse.Namespace) -> None:
|
77
94
|
"""Run animate subcommand.
|
@@ -100,6 +100,23 @@ def build_subparser(subparsers) -> None:
|
|
100
100
|
metavar="WIDTH",
|
101
101
|
)
|
102
102
|
|
103
|
+
border_parser.add_argument(
|
104
|
+
"-op",
|
105
|
+
"--open",
|
106
|
+
default=False,
|
107
|
+
action="store_true",
|
108
|
+
dest="open",
|
109
|
+
help="open the image using default program (default: '%(default)s')",
|
110
|
+
)
|
111
|
+
|
112
|
+
border_parser.add_argument(
|
113
|
+
"-od",
|
114
|
+
"--output-dir",
|
115
|
+
dest="output_dir",
|
116
|
+
default="output",
|
117
|
+
help="set default output folder (default: '%(default)s')",
|
118
|
+
)
|
119
|
+
|
103
120
|
|
104
121
|
def run(args: argparse.Namespace) -> None:
|
105
122
|
"""Run border subcommand.
|
@@ -56,6 +56,23 @@ def build_subparser(subparsers) -> None:
|
|
56
56
|
metavar="CUTOFF",
|
57
57
|
)
|
58
58
|
|
59
|
+
contrast_parser.add_argument(
|
60
|
+
"-op",
|
61
|
+
"--open",
|
62
|
+
default=False,
|
63
|
+
action="store_true",
|
64
|
+
dest="open",
|
65
|
+
help="open the image using default program (default: '%(default)s')",
|
66
|
+
)
|
67
|
+
|
68
|
+
contrast_parser.add_argument(
|
69
|
+
"-od",
|
70
|
+
"--output-dir",
|
71
|
+
dest="output_dir",
|
72
|
+
default="output",
|
73
|
+
help="set default output folder (default: '%(default)s')",
|
74
|
+
)
|
75
|
+
|
59
76
|
|
60
77
|
def run(args: argparse.Namespace) -> None:
|
61
78
|
"""Run contrast subcommand.
|
@@ -69,9 +86,14 @@ def run(args: argparse.Namespace) -> None:
|
|
69
86
|
log.debug(args)
|
70
87
|
|
71
88
|
for image_filename in args.image_filenames:
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
89
|
+
try:
|
90
|
+
original_image = Image.open(image_filename)
|
91
|
+
contrast_image = ImageOps.autocontrast(
|
92
|
+
original_image, cutoff=args.cutoff
|
93
|
+
)
|
94
|
+
|
95
|
+
save_image(args, contrast_image, image_filename, "contrast")
|
96
|
+
except FileNotFoundError:
|
97
|
+
log.error(f"Image file not found: {image_filename}")
|
98
|
+
except IOError:
|
99
|
+
log.error(f"Cannot open or read image file: {image_filename}")
|
@@ -18,11 +18,10 @@
|
|
18
18
|
import argparse
|
19
19
|
import logging
|
20
20
|
import math
|
21
|
-
from pathlib import Path
|
22
21
|
|
23
22
|
from PIL import Image, ImageDraw
|
24
23
|
|
25
|
-
from fotolab import
|
24
|
+
from fotolab import save_gif_image, save_image
|
26
25
|
|
27
26
|
log = logging.getLogger(__name__)
|
28
27
|
|
@@ -53,6 +52,23 @@ def build_subparser(subparsers) -> None:
|
|
53
52
|
help="generate a GIF showing before and after changes",
|
54
53
|
)
|
55
54
|
|
55
|
+
halftone_parser.add_argument(
|
56
|
+
"-op",
|
57
|
+
"--open",
|
58
|
+
default=False,
|
59
|
+
action="store_true",
|
60
|
+
dest="open",
|
61
|
+
help="open the image using default program (default: '%(default)s')",
|
62
|
+
)
|
63
|
+
|
64
|
+
halftone_parser.add_argument(
|
65
|
+
"-od",
|
66
|
+
"--output-dir",
|
67
|
+
dest="output_dir",
|
68
|
+
default="output",
|
69
|
+
help="set default output folder (default: '%(default)s')",
|
70
|
+
)
|
71
|
+
|
56
72
|
|
57
73
|
def run(args: argparse.Namespace) -> None:
|
58
74
|
"""Run halftone subcommand.
|
@@ -71,7 +87,11 @@ def run(args: argparse.Namespace) -> None:
|
|
71
87
|
|
72
88
|
if args.before_after:
|
73
89
|
save_gif_image(
|
74
|
-
args,
|
90
|
+
args,
|
91
|
+
image_filename,
|
92
|
+
original_image,
|
93
|
+
halftone_image,
|
94
|
+
"halftone",
|
75
95
|
)
|
76
96
|
else:
|
77
97
|
save_image(args, halftone_image, image_filename, "halftone")
|
@@ -123,27 +143,3 @@ def create_halftone_image(
|
|
123
143
|
)
|
124
144
|
|
125
145
|
return halftone_image
|
126
|
-
|
127
|
-
|
128
|
-
def save_gif_image(args, image_filename, original_image, sharpen_image):
|
129
|
-
"""Save the original and sharpen image."""
|
130
|
-
image_file = Path(image_filename)
|
131
|
-
new_filename = Path(
|
132
|
-
args.output_dir,
|
133
|
-
image_file.with_name(f"sharpen_gif_{image_file.stem}.gif"),
|
134
|
-
)
|
135
|
-
new_filename.parent.mkdir(parents=True, exist_ok=True)
|
136
|
-
|
137
|
-
log.info("sharpen gif image: %s", new_filename)
|
138
|
-
original_image.save(
|
139
|
-
new_filename,
|
140
|
-
format="gif",
|
141
|
-
append_images=[sharpen_image],
|
142
|
-
save_all=True,
|
143
|
-
duration=1000,
|
144
|
-
loop=0,
|
145
|
-
optimize=True,
|
146
|
-
)
|
147
|
-
|
148
|
-
if args.open:
|
149
|
-
_open_image(new_filename)
|
@@ -97,7 +97,11 @@ def run(args: argparse.Namespace) -> None:
|
|
97
97
|
|
98
98
|
def extract_exif_tags(image: Image.Image, sort: bool = False) -> dict:
|
99
99
|
"""Extract Exif metadata from image."""
|
100
|
-
|
100
|
+
try:
|
101
|
+
exif = image._getexif()
|
102
|
+
except AttributeError:
|
103
|
+
exif = None
|
104
|
+
|
101
105
|
log.debug(exif)
|
102
106
|
|
103
107
|
info = {}
|
@@ -42,6 +42,23 @@ def build_subparser(subparsers) -> None:
|
|
42
42
|
metavar="IMAGE_FILENAMES",
|
43
43
|
)
|
44
44
|
|
45
|
+
montage_parser.add_argument(
|
46
|
+
"-op",
|
47
|
+
"--open",
|
48
|
+
default=False,
|
49
|
+
action="store_true",
|
50
|
+
dest="open",
|
51
|
+
help="open the image using default program (default: '%(default)s')",
|
52
|
+
)
|
53
|
+
|
54
|
+
montage_parser.add_argument(
|
55
|
+
"-od",
|
56
|
+
"--output-dir",
|
57
|
+
dest="output_dir",
|
58
|
+
default="output",
|
59
|
+
help="set default output folder (default: '%(default)s')",
|
60
|
+
)
|
61
|
+
|
45
62
|
|
46
63
|
def run(args: argparse.Namespace) -> None:
|
47
64
|
"""Run montage subcommand.
|
@@ -108,6 +108,23 @@ def build_subparser(subparsers) -> None:
|
|
108
108
|
metavar="HEIGHT",
|
109
109
|
)
|
110
110
|
|
111
|
+
resize_parser.add_argument(
|
112
|
+
"-op",
|
113
|
+
"--open",
|
114
|
+
default=False,
|
115
|
+
action="store_true",
|
116
|
+
dest="open",
|
117
|
+
help="open the image using default program (default: '%(default)s')",
|
118
|
+
)
|
119
|
+
|
120
|
+
resize_parser.add_argument(
|
121
|
+
"-od",
|
122
|
+
"--output-dir",
|
123
|
+
dest="output_dir",
|
124
|
+
default="output",
|
125
|
+
help="set default output folder (default: '%(default)s')",
|
126
|
+
)
|
127
|
+
|
111
128
|
|
112
129
|
def run(args: argparse.Namespace) -> None:
|
113
130
|
"""Run resize subcommand.
|
@@ -55,6 +55,23 @@ def build_subparser(subparsers) -> None:
|
|
55
55
|
help="Rotate clockwise (default: '%(default)s)",
|
56
56
|
)
|
57
57
|
|
58
|
+
rotate_parser.add_argument(
|
59
|
+
"-op",
|
60
|
+
"--open",
|
61
|
+
default=False,
|
62
|
+
action="store_true",
|
63
|
+
dest="open",
|
64
|
+
help="open the image using default program (default: '%(default)s')",
|
65
|
+
)
|
66
|
+
|
67
|
+
rotate_parser.add_argument(
|
68
|
+
"-od",
|
69
|
+
"--output-dir",
|
70
|
+
dest="output_dir",
|
71
|
+
default="output",
|
72
|
+
help="set default output folder (default: '%(default)s')",
|
73
|
+
)
|
74
|
+
|
58
75
|
|
59
76
|
def run(args: argparse.Namespace) -> None:
|
60
77
|
"""Run rotate subcommand.
|
@@ -17,11 +17,10 @@
|
|
17
17
|
|
18
18
|
import argparse
|
19
19
|
import logging
|
20
|
-
from pathlib import Path
|
21
20
|
|
22
21
|
from PIL import Image, ImageFilter
|
23
22
|
|
24
|
-
from fotolab import
|
23
|
+
from fotolab import save_gif_image, save_image
|
25
24
|
|
26
25
|
log = logging.getLogger(__name__)
|
27
26
|
|
@@ -86,6 +85,23 @@ def build_subparser(subparsers) -> None:
|
|
86
85
|
help="generate a GIF showing before and after changes",
|
87
86
|
)
|
88
87
|
|
88
|
+
sharpen_parser.add_argument(
|
89
|
+
"-op",
|
90
|
+
"--open",
|
91
|
+
default=False,
|
92
|
+
action="store_true",
|
93
|
+
dest="open",
|
94
|
+
help="open the image using default program (default: '%(default)s')",
|
95
|
+
)
|
96
|
+
|
97
|
+
sharpen_parser.add_argument(
|
98
|
+
"-od",
|
99
|
+
"--output-dir",
|
100
|
+
dest="output_dir",
|
101
|
+
default="output",
|
102
|
+
help="set default output folder (default: '%(default)s')",
|
103
|
+
)
|
104
|
+
|
89
105
|
|
90
106
|
def run(args: argparse.Namespace) -> None:
|
91
107
|
"""Run sharpen subcommand.
|
@@ -106,35 +122,8 @@ def run(args: argparse.Namespace) -> None:
|
|
106
122
|
)
|
107
123
|
)
|
108
124
|
if args.before_after:
|
109
|
-
save_gif_image(
|
125
|
+
save_gif_image(
|
126
|
+
args, image_filename, original_image, sharpen_image, "sharpen"
|
127
|
+
)
|
110
128
|
else:
|
111
129
|
save_image(args, sharpen_image, image_filename, "sharpen")
|
112
|
-
|
113
|
-
|
114
|
-
def save_gif_image(
|
115
|
-
args: argparse.Namespace,
|
116
|
-
image_filename: str,
|
117
|
-
original_image: Image.Image,
|
118
|
-
sharpen_image: Image.Image,
|
119
|
-
) -> None:
|
120
|
-
"""Save the original and sharpen image."""
|
121
|
-
image_file = Path(image_filename)
|
122
|
-
new_filename = Path(
|
123
|
-
args.output_dir,
|
124
|
-
image_file.with_name(f"sharpen_gif_{image_file.stem}.gif"),
|
125
|
-
)
|
126
|
-
new_filename.parent.mkdir(parents=True, exist_ok=True)
|
127
|
-
|
128
|
-
log.info("sharpen gif image: %s", new_filename)
|
129
|
-
original_image.save(
|
130
|
-
new_filename,
|
131
|
-
format="gif",
|
132
|
-
append_images=[sharpen_image],
|
133
|
-
save_all=True,
|
134
|
-
duration=2500,
|
135
|
-
loop=0,
|
136
|
-
optimize=True,
|
137
|
-
)
|
138
|
-
|
139
|
-
if args.open:
|
140
|
-
_open_image(new_filename)
|
@@ -145,6 +145,23 @@ def build_subparser(subparsers) -> None:
|
|
145
145
|
help="lowercase the watermark text",
|
146
146
|
)
|
147
147
|
|
148
|
+
watermark_parser.add_argument(
|
149
|
+
"-op",
|
150
|
+
"--open",
|
151
|
+
default=False,
|
152
|
+
action="store_true",
|
153
|
+
dest="open",
|
154
|
+
help="open the image using default program (default: '%(default)s')",
|
155
|
+
)
|
156
|
+
|
157
|
+
watermark_parser.add_argument(
|
158
|
+
"-od",
|
159
|
+
"--output-dir",
|
160
|
+
dest="output_dir",
|
161
|
+
default="output",
|
162
|
+
help="set default output folder (default: '%(default)s')",
|
163
|
+
)
|
164
|
+
|
148
165
|
|
149
166
|
def run(args: argparse.Namespace) -> None:
|
150
167
|
"""Run watermark subcommand.
|
@@ -1,70 +0,0 @@
|
|
1
|
-
# Copyright (C) 2024,2025 Kian-Meng Ang
|
2
|
-
#
|
3
|
-
# This program is free software: you can redistribute it and/or modify it under
|
4
|
-
# the terms of the GNU Affero General Public License as published by the Free
|
5
|
-
# Software Foundation, either version 3 of the License, or (at your option) any
|
6
|
-
# later version.
|
7
|
-
#
|
8
|
-
# This program is distributed in the hope that it will be useful, but WITHOUT
|
9
|
-
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
10
|
-
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
11
|
-
# details.
|
12
|
-
#
|
13
|
-
# You should have received a copy of the GNU Affero General Public License
|
14
|
-
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
15
|
-
|
16
|
-
"""A console program that manipulate images."""
|
17
|
-
|
18
|
-
import logging
|
19
|
-
import subprocess
|
20
|
-
import sys
|
21
|
-
from pathlib import Path
|
22
|
-
|
23
|
-
__version__ = "0.27.2"
|
24
|
-
|
25
|
-
log = logging.getLogger(__name__)
|
26
|
-
|
27
|
-
|
28
|
-
def save_image(args, new_image, output_filename, subcommand):
|
29
|
-
"""Save image after image operation.
|
30
|
-
|
31
|
-
Args:
|
32
|
-
args (argparse.Namespace): Config from command line arguments
|
33
|
-
new_image(PIL.Image.Image): Modified image
|
34
|
-
output_filename(str): Save filename image
|
35
|
-
subcommand(str): Subcommand used to call this function
|
36
|
-
|
37
|
-
Returns:
|
38
|
-
None
|
39
|
-
"""
|
40
|
-
image_file = Path(output_filename)
|
41
|
-
|
42
|
-
if args.overwrite:
|
43
|
-
new_filename = image_file.with_name(image_file.name)
|
44
|
-
else:
|
45
|
-
new_filename = Path(
|
46
|
-
args.output_dir,
|
47
|
-
image_file.with_name(f"{subcommand}_{image_file.name}"),
|
48
|
-
)
|
49
|
-
new_filename.parent.mkdir(parents=True, exist_ok=True)
|
50
|
-
|
51
|
-
log.info("%s image: %s", subcommand, new_filename.resolve())
|
52
|
-
new_image.save(new_filename)
|
53
|
-
|
54
|
-
if args.open:
|
55
|
-
_open_image(new_filename)
|
56
|
-
|
57
|
-
|
58
|
-
def _open_image(filename):
|
59
|
-
"""Open generated image using default program."""
|
60
|
-
platform_open_func = {
|
61
|
-
"linux": subprocess.call(["xdg-open", filename]),
|
62
|
-
"darwin": subprocess.call(["open", filename]),
|
63
|
-
"windows": subprocess.call(["start", filename]),
|
64
|
-
}
|
65
|
-
try:
|
66
|
-
platform_open_func[sys.platform]
|
67
|
-
except KeyError:
|
68
|
-
print(f"Unsupported platform: {sys.platform}")
|
69
|
-
else:
|
70
|
-
log.info("open image: %s", filename.resolve())
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|