fotolab 0.27.2__tar.gz → 0.28.0__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.
Files changed (43) hide show
  1. {fotolab-0.27.2 → fotolab-0.28.0}/CHANGELOG.md +9 -0
  2. {fotolab-0.27.2 → fotolab-0.28.0}/PKG-INFO +53 -18
  3. {fotolab-0.27.2 → fotolab-0.28.0}/README.md +52 -17
  4. {fotolab-0.27.2 → fotolab-0.28.0}/fotolab/__init__.py +45 -11
  5. {fotolab-0.27.2 → fotolab-0.28.0}/fotolab/cli.py +0 -17
  6. {fotolab-0.27.2 → fotolab-0.28.0}/fotolab/subcommands/animate.py +17 -0
  7. {fotolab-0.27.2 → fotolab-0.28.0}/fotolab/subcommands/border.py +17 -0
  8. {fotolab-0.27.2 → fotolab-0.28.0}/fotolab/subcommands/contrast.py +17 -0
  9. {fotolab-0.27.2 → fotolab-0.28.0}/fotolab/subcommands/halftone.py +23 -27
  10. {fotolab-0.27.2 → fotolab-0.28.0}/fotolab/subcommands/info.py +5 -1
  11. {fotolab-0.27.2 → fotolab-0.28.0}/fotolab/subcommands/montage.py +17 -0
  12. {fotolab-0.27.2 → fotolab-0.28.0}/fotolab/subcommands/resize.py +17 -0
  13. {fotolab-0.27.2 → fotolab-0.28.0}/fotolab/subcommands/rotate.py +17 -0
  14. {fotolab-0.27.2 → fotolab-0.28.0}/fotolab/subcommands/sharpen.py +21 -32
  15. {fotolab-0.27.2 → fotolab-0.28.0}/fotolab/subcommands/watermark.py +17 -0
  16. {fotolab-0.27.2 → fotolab-0.28.0}/.coveragerc +0 -0
  17. {fotolab-0.27.2 → fotolab-0.28.0}/.gitignore +0 -0
  18. {fotolab-0.27.2 → fotolab-0.28.0}/.pre-commit-config.yaml +0 -0
  19. {fotolab-0.27.2 → fotolab-0.28.0}/.python-version +0 -0
  20. {fotolab-0.27.2 → fotolab-0.28.0}/CONTRIBUTING.md +0 -0
  21. {fotolab-0.27.2 → fotolab-0.28.0}/LICENSE.md +0 -0
  22. {fotolab-0.27.2 → fotolab-0.28.0}/Pipfile +0 -0
  23. {fotolab-0.27.2 → fotolab-0.28.0}/Pipfile.lock +0 -0
  24. {fotolab-0.27.2 → fotolab-0.28.0}/docs/Makefile +0 -0
  25. {fotolab-0.27.2 → fotolab-0.28.0}/docs/make.bat +0 -0
  26. {fotolab-0.27.2 → fotolab-0.28.0}/docs/source/CHANGELOG.md +0 -0
  27. {fotolab-0.27.2 → fotolab-0.28.0}/docs/source/CONTRIBUTING.md +0 -0
  28. {fotolab-0.27.2 → fotolab-0.28.0}/docs/source/LICENSE.md +0 -0
  29. {fotolab-0.27.2 → fotolab-0.28.0}/docs/source/README.md +0 -0
  30. {fotolab-0.27.2 → fotolab-0.28.0}/docs/source/_static/logo.jpg +0 -0
  31. {fotolab-0.27.2 → fotolab-0.28.0}/docs/source/conf.py +0 -0
  32. {fotolab-0.27.2 → fotolab-0.28.0}/docs/source/index.rst +0 -0
  33. {fotolab-0.27.2 → fotolab-0.28.0}/fotolab/__main__.py +0 -0
  34. {fotolab-0.27.2 → fotolab-0.28.0}/fotolab/subcommands/__init__.py +0 -0
  35. {fotolab-0.27.2 → fotolab-0.28.0}/fotolab/subcommands/auto.py +0 -0
  36. {fotolab-0.27.2 → fotolab-0.28.0}/fotolab/subcommands/env.py +0 -0
  37. {fotolab-0.27.2 → fotolab-0.28.0}/noxfile.py +0 -0
  38. {fotolab-0.27.2 → fotolab-0.28.0}/pyproject.toml +0 -0
  39. {fotolab-0.27.2 → fotolab-0.28.0}/tests/__init__.py +0 -0
  40. {fotolab-0.27.2 → fotolab-0.28.0}/tests/conftest.py +0 -0
  41. {fotolab-0.27.2 → fotolab-0.28.0}/tests/test_env_subcommand.py +0 -0
  42. {fotolab-0.27.2 → fotolab-0.28.0}/tests/test_help_flag.py +0 -0
  43. {fotolab-0.27.2 → fotolab-0.28.0}/tests/test_quiet_flag.py +0 -0
@@ -7,6 +7,15 @@ and this project adheres to [0-based versioning](https://0ver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## v0.28.0 (2025-03-09)
11
+
12
+ - Handle exception of private function with exif info
13
+ - Move `--output_dir` and `--open` args to subcommand that process image
14
+ - Refactor getting output filename when saving image
15
+ - Remove `--output-dir` and `--open` args from main cli
16
+ - Update help message in readme
17
+ - Use single `save_gif_image` function in sharpen and halftone subcommand
18
+
10
19
  ## v0.27.2 (2025-03-02)
11
20
 
12
21
  - Add missing latest copyright year
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fotolab
3
- Version: 0.27.2
3
+ Version: 0.28.0
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] [-op] [-od OUTPUT_DIR] [-q] [-v] [-d] [-V]
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] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
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 set the image filename
198
+ IMAGE_FILENAMES set the image filename
192
199
 
193
200
  options:
194
- -h, --help show this help message and exit
195
- -c, --cutoff CUTOFF set the percentage of lightest or darkest pixels to
196
- discard from histogram (default: '1')
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] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
272
+ usage: fotolab montage [-h] [-op] [-od OUTPUT_DIR]
273
+ IMAGE_FILENAMES [IMAGE_FILENAMES ...]
258
274
 
259
275
  positional arguments:
260
- IMAGE_FILENAMES set the image filenames
276
+ IMAGE_FILENAMES set the image filenames
261
277
 
262
278
  options:
263
- -h, --help show this help message and exit
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] [-op] [-od OUTPUT_DIR] [-q] [-v] [-d] [-V]
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] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
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 set the image filename
175
+ IMAGE_FILENAMES set the image filename
169
176
 
170
177
  options:
171
- -h, --help show this help message and exit
172
- -c, --cutoff CUTOFF set the percentage of lightest or darkest pixels to
173
- discard from histogram (default: '1')
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] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
249
+ usage: fotolab montage [-h] [-op] [-od OUTPUT_DIR]
250
+ IMAGE_FILENAMES [IMAGE_FILENAMES ...]
235
251
 
236
252
  positional arguments:
237
- IMAGE_FILENAMES set the image filenames
253
+ IMAGE_FILENAMES set the image filenames
238
254
 
239
255
  options:
240
- -h, --help show this help message and exit
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 !-->
@@ -15,16 +15,49 @@
15
15
 
16
16
  """A console program that manipulate images."""
17
17
 
18
+ import argparse
18
19
  import logging
19
20
  import subprocess
20
21
  import sys
21
22
  from pathlib import Path
22
23
 
23
- __version__ = "0.27.2"
24
+ from PIL import Image
25
+
26
+ __version__ = "0.28.0"
24
27
 
25
28
  log = logging.getLogger(__name__)
26
29
 
27
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("f{subcommand} gif image: %s", 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
+
28
61
  def save_image(args, new_image, output_filename, subcommand):
29
62
  """Save image after image operation.
30
63
 
@@ -38,16 +71,7 @@ def save_image(args, new_image, output_filename, subcommand):
38
71
  None
39
72
  """
40
73
  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
-
74
+ new_filename = _get_output_filename(args, image_file, subcommand)
51
75
  log.info("%s image: %s", subcommand, new_filename.resolve())
52
76
  new_image.save(new_filename)
53
77
 
@@ -55,6 +79,16 @@ def save_image(args, new_image, output_filename, subcommand):
55
79
  _open_image(new_filename)
56
80
 
57
81
 
82
+ def _get_output_filename(args, image_file, subcommand):
83
+ """Build and return output filename based on the command line options."""
84
+ if args.overwrite:
85
+ return image_file.with_name(image_file.name)
86
+
87
+ output_dir = Path(args.output_dir)
88
+ output_dir.mkdir(parents=True, exist_ok=True)
89
+ return output_dir / image_file.with_name(f"{subcommand}_{image_file.name}")
90
+
91
+
58
92
  def _open_image(filename):
59
93
  """Open generated image using default program."""
60
94
  platform_open_func = {
@@ -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.
@@ -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 _open_image, save_image
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, image_filename, original_image, halftone_image
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
- exif = image._getexif()
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 _open_image, save_image
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(args, image_filename, original_image, sharpen_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.
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