fotolab 0.27.0__tar.gz → 0.27.2__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.0 → fotolab-0.27.2}/.pre-commit-config.yaml +2 -2
  2. {fotolab-0.27.0 → fotolab-0.27.2}/CHANGELOG.md +17 -0
  3. {fotolab-0.27.0 → fotolab-0.27.2}/PKG-INFO +1 -1
  4. {fotolab-0.27.0 → fotolab-0.27.2}/fotolab/__init__.py +2 -1
  5. {fotolab-0.27.0 → fotolab-0.27.2}/fotolab/cli.py +10 -9
  6. {fotolab-0.27.0 → fotolab-0.27.2}/fotolab/subcommands/animate.py +1 -1
  7. {fotolab-0.27.0 → fotolab-0.27.2}/fotolab/subcommands/auto.py +1 -1
  8. {fotolab-0.27.0 → fotolab-0.27.2}/fotolab/subcommands/border.py +1 -1
  9. {fotolab-0.27.0 → fotolab-0.27.2}/fotolab/subcommands/contrast.py +1 -1
  10. {fotolab-0.27.0 → fotolab-0.27.2}/fotolab/subcommands/env.py +1 -1
  11. {fotolab-0.27.0 → fotolab-0.27.2}/fotolab/subcommands/halftone.py +1 -1
  12. {fotolab-0.27.0 → fotolab-0.27.2}/fotolab/subcommands/info.py +1 -1
  13. {fotolab-0.27.0 → fotolab-0.27.2}/fotolab/subcommands/montage.py +1 -1
  14. {fotolab-0.27.0 → fotolab-0.27.2}/fotolab/subcommands/rotate.py +1 -1
  15. {fotolab-0.27.0 → fotolab-0.27.2}/fotolab/subcommands/sharpen.py +7 -2
  16. {fotolab-0.27.0 → fotolab-0.27.2}/fotolab/subcommands/watermark.py +40 -8
  17. {fotolab-0.27.0 → fotolab-0.27.2}/noxfile.py +2 -2
  18. {fotolab-0.27.0 → fotolab-0.27.2}/.coveragerc +0 -0
  19. {fotolab-0.27.0 → fotolab-0.27.2}/.gitignore +0 -0
  20. {fotolab-0.27.0 → fotolab-0.27.2}/.python-version +0 -0
  21. {fotolab-0.27.0 → fotolab-0.27.2}/CONTRIBUTING.md +0 -0
  22. {fotolab-0.27.0 → fotolab-0.27.2}/LICENSE.md +0 -0
  23. {fotolab-0.27.0 → fotolab-0.27.2}/Pipfile +0 -0
  24. {fotolab-0.27.0 → fotolab-0.27.2}/Pipfile.lock +0 -0
  25. {fotolab-0.27.0 → fotolab-0.27.2}/README.md +0 -0
  26. {fotolab-0.27.0 → fotolab-0.27.2}/docs/Makefile +0 -0
  27. {fotolab-0.27.0 → fotolab-0.27.2}/docs/make.bat +0 -0
  28. {fotolab-0.27.0 → fotolab-0.27.2}/docs/source/CHANGELOG.md +0 -0
  29. {fotolab-0.27.0 → fotolab-0.27.2}/docs/source/CONTRIBUTING.md +0 -0
  30. {fotolab-0.27.0 → fotolab-0.27.2}/docs/source/LICENSE.md +0 -0
  31. {fotolab-0.27.0 → fotolab-0.27.2}/docs/source/README.md +0 -0
  32. {fotolab-0.27.0 → fotolab-0.27.2}/docs/source/_static/logo.jpg +0 -0
  33. {fotolab-0.27.0 → fotolab-0.27.2}/docs/source/conf.py +0 -0
  34. {fotolab-0.27.0 → fotolab-0.27.2}/docs/source/index.rst +0 -0
  35. {fotolab-0.27.0 → fotolab-0.27.2}/fotolab/__main__.py +0 -0
  36. {fotolab-0.27.0 → fotolab-0.27.2}/fotolab/subcommands/__init__.py +0 -0
  37. {fotolab-0.27.0 → fotolab-0.27.2}/fotolab/subcommands/resize.py +0 -0
  38. {fotolab-0.27.0 → fotolab-0.27.2}/pyproject.toml +0 -0
  39. {fotolab-0.27.0 → fotolab-0.27.2}/tests/__init__.py +0 -0
  40. {fotolab-0.27.0 → fotolab-0.27.2}/tests/conftest.py +0 -0
  41. {fotolab-0.27.0 → fotolab-0.27.2}/tests/test_env_subcommand.py +0 -0
  42. {fotolab-0.27.0 → fotolab-0.27.2}/tests/test_help_flag.py +0 -0
  43. {fotolab-0.27.0 → fotolab-0.27.2}/tests/test_quiet_flag.py +0 -0
@@ -34,7 +34,7 @@ repos:
34
34
  exclude: (Pipfile.lock)
35
35
 
36
36
  - repo: https://github.com/pycqa/isort
37
- rev: 6.0.0
37
+ rev: 6.0.1
38
38
  hooks:
39
39
  - id: isort
40
40
  additional_dependencies:
@@ -77,7 +77,7 @@ repos:
77
77
  language_version: python3.12
78
78
 
79
79
  - repo: https://github.com/PyCQA/flake8
80
- rev: 7.1.1
80
+ rev: 7.1.2
81
81
  hooks:
82
82
  - id: flake8
83
83
  language_version: python3.12
@@ -7,6 +7,23 @@ and this project adheres to [0-based versioning](https://0ver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## v0.27.2 (2025-03-02)
11
+
12
+ - Add missing latest copyright year
13
+ - Add missing typehints
14
+ - Bump pre-commit hook for isort
15
+ - Fix cannot save watermarked gif image
16
+ - Refactor main logic
17
+ - Update docstring to `watermark_gif_image` function
18
+
19
+ ## v0.27.1 (2025-02-23)
20
+
21
+ - Bump pre-commit hooks
22
+ - Fix incorrect arg type in comment in all subcommands
23
+ - Fix missing type from comment
24
+ - Fix saving watermark image only by hardcoded name
25
+ - Fix typos in license header
26
+
10
27
  ## v0.27.0 (2025-02-16)
11
28
 
12
29
  - Explicitly name the attribute for the subcommand
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fotolab
3
- Version: 0.27.0
3
+ Version: 0.27.2
4
4
  Summary: A console program that manipulate images.
5
5
  Keywords: photography,photo
6
6
  Author-email: Kian-Meng Ang <kianmeng@cpan.org>
@@ -20,7 +20,7 @@ import subprocess
20
20
  import sys
21
21
  from pathlib import Path
22
22
 
23
- __version__ = "0.27.0"
23
+ __version__ = "0.27.2"
24
24
 
25
25
  log = logging.getLogger(__name__)
26
26
 
@@ -31,6 +31,7 @@ def save_image(args, new_image, output_filename, subcommand):
31
31
  Args:
32
32
  args (argparse.Namespace): Config from command line arguments
33
33
  new_image(PIL.Image.Image): Modified image
34
+ output_filename(str): Save filename image
34
35
  subcommand(str): Subcommand used to call this function
35
36
 
36
37
  Returns:
@@ -8,7 +8,7 @@
8
8
  # This program is distributed in the hope that it will be useful,
9
9
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
- # GNU Generals Public License for more details.
11
+ # GNU General Public License for more details.
12
12
 
13
13
  # You should have received a copy of the GNU General Public License
14
14
  # along with this program. If not, see <https://www.gnu.org/licenses/>.
@@ -147,15 +147,16 @@ def main(args: Optional[Sequence[str]] = None) -> None:
147
147
  parser = build_parser()
148
148
  if len(args) == 0:
149
149
  parser.print_help(sys.stderr)
150
+ return
151
+
152
+ parsed_args = parser.parse_args(args)
153
+ setup_logging(parsed_args)
154
+
155
+ if hasattr(parsed_args, "func"):
156
+ log.debug(parsed_args)
157
+ parsed_args.func(parsed_args)
150
158
  else:
151
- parsed_args = parser.parse_args(args)
152
- setup_logging(parsed_args)
153
-
154
- if hasattr(parsed_args, "func"):
155
- log.debug(parsed_args)
156
- parsed_args.func(parsed_args)
157
- else:
158
- parser.print_help(sys.stderr)
159
+ parser.print_help(sys.stderr)
159
160
 
160
161
  except Exception as error:
161
162
  log.error(
@@ -77,7 +77,7 @@ def run(args: argparse.Namespace) -> None:
77
77
  """Run animate subcommand.
78
78
 
79
79
  Args:
80
- config (argparse.Namespace): Config from command line arguments
80
+ args (argparse.Namespace): Config from command line arguments
81
81
 
82
82
  Returns:
83
83
  None
@@ -48,7 +48,7 @@ def run(args: argparse.Namespace) -> None:
48
48
  """Run auto subcommand.
49
49
 
50
50
  Args:
51
- config (argparse.Namespace): Config from command line arguments
51
+ args (argparse.Namespace): Config from command line arguments
52
52
 
53
53
  Returns:
54
54
  None
@@ -105,7 +105,7 @@ def run(args: argparse.Namespace) -> None:
105
105
  """Run border subcommand.
106
106
 
107
107
  Args:
108
- config (argparse.Namespace): Config from command line arguments
108
+ args (argparse.Namespace): Config from command line arguments
109
109
 
110
110
  Returns:
111
111
  None
@@ -61,7 +61,7 @@ def run(args: argparse.Namespace) -> None:
61
61
  """Run contrast subcommand.
62
62
 
63
63
  Args:
64
- config (argparse.Namespace): Config from command line arguments
64
+ args (argparse.Namespace): Config from command line arguments
65
65
 
66
66
  Returns:
67
67
  None
@@ -38,7 +38,7 @@ def run(_args: argparse.Namespace) -> None:
38
38
  """Run env subcommand.
39
39
 
40
40
  Args:
41
- config (argparse.Namespace): Config from command line arguments
41
+ args (argparse.Namespace): Config from command line arguments
42
42
 
43
43
  Returns:
44
44
  None
@@ -58,7 +58,7 @@ def run(args: argparse.Namespace) -> None:
58
58
  """Run halftone subcommand.
59
59
 
60
60
  Args:
61
- config (argparse.Namespace): Config from command line arguments
61
+ args (argparse.Namespace): Config from command line arguments
62
62
 
63
63
  Returns:
64
64
  None
@@ -67,7 +67,7 @@ def run(args: argparse.Namespace) -> None:
67
67
  """Run info subcommand.
68
68
 
69
69
  Args:
70
- config (argparse.Namespace): Config from command line arguments
70
+ args (argparse.Namespace): Config from command line arguments
71
71
 
72
72
  Returns:
73
73
  None
@@ -47,7 +47,7 @@ def run(args: argparse.Namespace) -> None:
47
47
  """Run montage subcommand.
48
48
 
49
49
  Args:
50
- config (argparse.Namespace): Config from command line arguments
50
+ args (argparse.Namespace): Config from command line arguments
51
51
 
52
52
  Returns:
53
53
  None
@@ -60,7 +60,7 @@ def run(args: argparse.Namespace) -> None:
60
60
  """Run rotate subcommand.
61
61
 
62
62
  Args:
63
- config (argparse.Namespace): Config from command line arguments
63
+ args (argparse.Namespace): Config from command line arguments
64
64
 
65
65
  Returns:
66
66
  None
@@ -91,7 +91,7 @@ def run(args: argparse.Namespace) -> None:
91
91
  """Run sharpen subcommand.
92
92
 
93
93
  Args:
94
- config (argparse.Namespace): Config from command line arguments
94
+ args (argparse.Namespace): Config from command line arguments
95
95
 
96
96
  Returns:
97
97
  None
@@ -111,7 +111,12 @@ def run(args: argparse.Namespace) -> None:
111
111
  save_image(args, sharpen_image, image_filename, "sharpen")
112
112
 
113
113
 
114
- def save_gif_image(args, image_filename, original_image, sharpen_image):
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:
115
120
  """Save the original and sharpen image."""
116
121
  image_file = Path(image_filename)
117
122
  new_filename = Path(
@@ -18,6 +18,7 @@
18
18
  import argparse
19
19
  import logging
20
20
  import math
21
+ from pathlib import Path
21
22
 
22
23
  from PIL import Image, ImageColor, ImageDraw, ImageFont, ImageSequence
23
24
 
@@ -159,24 +160,47 @@ def run(args: argparse.Namespace) -> None:
159
160
  for image_filename in args.image_filenames:
160
161
  image = Image.open(image_filename)
161
162
  if image.format == "GIF":
162
- watermark_gif_image(image, args)
163
+ watermark_gif_image(image, image_filename, args)
163
164
  else:
164
165
  watermarked_image = watermark_non_gif_image(image, args)
165
166
  save_image(args, watermarked_image, image_filename, "watermark")
166
167
 
167
168
 
168
- def watermark_gif_image(original_image: Image.Image, args: argparse.Namespace):
169
- """Watermark the image."""
169
+ def watermark_gif_image(
170
+ original_image: Image.Image, output_filename: str, args: argparse.Namespace
171
+ ) -> None:
172
+ """Watermark a GIF image.
173
+
174
+ Args:
175
+ original_image (Image.Image): The original GIF image
176
+ output_filename (str): Path to save the watermarked GIF
177
+ args (argparse.Namespace): Command line arguments
178
+
179
+ Returns:
180
+ None
181
+ """
170
182
  frames = []
171
183
  for frame in ImageSequence.Iterator(original_image):
172
184
  watermarked_frame = watermark_image(args, frame.convert("RGBA"))
173
185
  frames.append(watermarked_frame)
174
186
 
187
+ image_file = Path(output_filename)
188
+
189
+ if args.overwrite:
190
+ new_filename = original_image.with_name(image_file.name)
191
+ else:
192
+ new_filename = Path(
193
+ args.output_dir,
194
+ image_file.with_name(f"watermark_{image_file.name}"),
195
+ )
196
+ new_filename.parent.mkdir(parents=True, exist_ok=True)
197
+
198
+ log.info("%s image: %s", "watermark", new_filename.resolve())
199
+
175
200
  frames[0].save(
176
- "foo.gif",
177
- format="GIF",
178
- append_images=frames[1:],
201
+ new_filename,
179
202
  save_all=True,
203
+ append_images=frames[1:],
180
204
  duration=original_image.info.get("duration", 100),
181
205
  loop=original_image.info.get("loop", 0),
182
206
  disposal=original_image.info.get("disposal", 2),
@@ -185,8 +209,16 @@ def watermark_gif_image(original_image: Image.Image, args: argparse.Namespace):
185
209
 
186
210
  def watermark_non_gif_image(
187
211
  original_image: Image.Image, args: argparse.Namespace
188
- ):
189
- """Watermark the image."""
212
+ ) -> Image.Image:
213
+ """Watermark a non-GIF image.
214
+
215
+ Args:
216
+ original_image (Image.Image): The original image
217
+ args (argparse.Namespace): Command line arguments
218
+
219
+ Returns:
220
+ Image.Image: The watermarked image
221
+ """
190
222
  return watermark_image(args, original_image)
191
223
 
192
224
 
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2022,2023,2024 Kian-Meng Ang
1
+ # Copyright (c) 2022,2023,2024,2025 Kian-Meng Ang
2
2
 
3
3
  # This program is free software: you can redistribute it and/or modify
4
4
  # it under the terms of the GNU General Public License as published by
@@ -8,7 +8,7 @@
8
8
  # This program is distributed in the hope that it will be useful,
9
9
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
- # GNU Generals Public License for more details.
11
+ # GNU General Public License for more details.
12
12
 
13
13
  # You should have received a copy of the GNU General Public License
14
14
  # along with this program. If not, see <https://www.gnu.org/licenses/>.
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