fotolab 0.17.0__tar.gz → 0.18.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.
Files changed (41) hide show
  1. {fotolab-0.17.0 → fotolab-0.18.1}/.pre-commit-config.yaml +1 -1
  2. {fotolab-0.17.0 → fotolab-0.18.1}/.python-version +1 -1
  3. {fotolab-0.17.0 → fotolab-0.18.1}/CHANGELOG.md +12 -0
  4. {fotolab-0.17.0 → fotolab-0.18.1}/PKG-INFO +3 -2
  5. {fotolab-0.17.0 → fotolab-0.18.1}/Pipfile.lock +7 -7
  6. {fotolab-0.17.0 → fotolab-0.18.1}/README.md +2 -1
  7. {fotolab-0.17.0 → fotolab-0.18.1}/fotolab/__init__.py +1 -1
  8. {fotolab-0.17.0 → fotolab-0.18.1}/fotolab/auto.py +2 -0
  9. {fotolab-0.17.0 → fotolab-0.18.1}/fotolab/cli.py +1 -1
  10. {fotolab-0.17.0 → fotolab-0.18.1}/fotolab/env.py +1 -3
  11. {fotolab-0.17.0 → fotolab-0.18.1}/fotolab/info.py +11 -5
  12. {fotolab-0.17.0 → fotolab-0.18.1}/fotolab/watermark.py +35 -3
  13. {fotolab-0.17.0 → fotolab-0.18.1}/.coveragerc +0 -0
  14. {fotolab-0.17.0 → fotolab-0.18.1}/.gitignore +0 -0
  15. {fotolab-0.17.0 → fotolab-0.18.1}/CONTRIBUTING.md +0 -0
  16. {fotolab-0.17.0 → fotolab-0.18.1}/LICENSE.md +0 -0
  17. {fotolab-0.17.0 → fotolab-0.18.1}/Pipfile +0 -0
  18. {fotolab-0.17.0 → fotolab-0.18.1}/docs/Makefile +0 -0
  19. {fotolab-0.17.0 → fotolab-0.18.1}/docs/make.bat +0 -0
  20. {fotolab-0.17.0 → fotolab-0.18.1}/docs/source/CHANGELOG.md +0 -0
  21. {fotolab-0.17.0 → fotolab-0.18.1}/docs/source/CONTRIBUTING.md +0 -0
  22. {fotolab-0.17.0 → fotolab-0.18.1}/docs/source/LICENSE.md +0 -0
  23. {fotolab-0.17.0 → fotolab-0.18.1}/docs/source/README.md +0 -0
  24. {fotolab-0.17.0 → fotolab-0.18.1}/docs/source/_static/logo.jpg +0 -0
  25. {fotolab-0.17.0 → fotolab-0.18.1}/docs/source/conf.py +0 -0
  26. {fotolab-0.17.0 → fotolab-0.18.1}/docs/source/index.rst +0 -0
  27. {fotolab-0.17.0 → fotolab-0.18.1}/fotolab/__main__.py +0 -0
  28. {fotolab-0.17.0 → fotolab-0.18.1}/fotolab/animate.py +0 -0
  29. {fotolab-0.17.0 → fotolab-0.18.1}/fotolab/border.py +0 -0
  30. {fotolab-0.17.0 → fotolab-0.18.1}/fotolab/contrast.py +0 -0
  31. {fotolab-0.17.0 → fotolab-0.18.1}/fotolab/montage.py +0 -0
  32. {fotolab-0.17.0 → fotolab-0.18.1}/fotolab/resize.py +0 -0
  33. {fotolab-0.17.0 → fotolab-0.18.1}/fotolab/rotate.py +0 -0
  34. {fotolab-0.17.0 → fotolab-0.18.1}/fotolab/sharpen.py +0 -0
  35. {fotolab-0.17.0 → fotolab-0.18.1}/noxfile.py +0 -0
  36. {fotolab-0.17.0 → fotolab-0.18.1}/pyproject.toml +0 -0
  37. {fotolab-0.17.0 → fotolab-0.18.1}/tests/__init__.py +0 -0
  38. {fotolab-0.17.0 → fotolab-0.18.1}/tests/conftest.py +0 -0
  39. {fotolab-0.17.0 → fotolab-0.18.1}/tests/test_env.py +0 -0
  40. {fotolab-0.17.0 → fotolab-0.18.1}/tests/test_help_flag.py +0 -0
  41. {fotolab-0.17.0 → fotolab-0.18.1}/tests/test_quiet_flag.py +0 -0
@@ -15,7 +15,7 @@ repos:
15
15
  - id: trailing-whitespace
16
16
 
17
17
  - repo: https://github.com/abravalheri/validate-pyproject
18
- rev: v0.19
18
+ rev: v0.20.2
19
19
  hooks:
20
20
  - id: validate-pyproject
21
21
  name: validate-pyproject
@@ -2,4 +2,4 @@
2
2
  3.9.20
3
3
  3.10.15
4
4
  3.11.10
5
- 3.12.6
5
+ 3.12.7
@@ -7,6 +7,18 @@ and this project adheres to [0-based versioning](https://0ver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## v0.18.1 (2024-10-06)
11
+
12
+ - Bump deps and Python version in `pyenv`
13
+ - Use `prog` field in `argparse` when show program version
14
+ - Set watermark text default to lowercase
15
+
16
+ ## v0.18.0 (2024-09-29)
17
+
18
+ - Add `--camera` flag to `watermark` subcommand to use camera's metadata as text
19
+ - Add `--lowercase` flag to `watermark` subcommand to use lowercase the watermark text
20
+ - Bump deps and `pre-commit` hooks
21
+
10
22
  ## v0.17.0 (2024-09-22)
11
23
 
12
24
  - Bump Python versions for `pyenv`
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fotolab
3
- Version: 0.17.0
3
+ Version: 0.18.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>
@@ -328,7 +328,7 @@ fotolab watermark -h
328
328
  usage: fotolab watermark [-h] [-t WATERMARK_TEXT]
329
329
  [-p {top-left,top-right,bottom-left,bottom-right}]
330
330
  [-pd PADDING] [-fs FONT_SIZE] [-fc FONT_COLOR]
331
- [-ow OUTLINE_WIDTH] [-oc OUTLINE_COLOR]
331
+ [-ow OUTLINE_WIDTH] [-oc OUTLINE_COLOR] [--camera]
332
332
  IMAGE_FILENAMES [IMAGE_FILENAMES ...]
333
333
 
334
334
  positional arguments:
@@ -355,6 +355,7 @@ options:
355
355
  -oc OUTLINE_COLOR, --outline-color OUTLINE_COLOR
356
356
  set the outline color of the watermark text (default:
357
357
  'black')
358
+ --camera use camera metadata as watermark
358
359
  ```
359
360
 
360
361
  <!--help-watermark !-->
@@ -953,12 +953,12 @@
953
953
  },
954
954
  "tomli": {
955
955
  "hashes": [
956
- "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
957
- "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
956
+ "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38",
957
+ "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"
958
958
  ],
959
959
  "index": "pypi",
960
- "markers": "python_version >= '3.7'",
961
- "version": "==2.0.1"
960
+ "markers": "python_version >= '3.8'",
961
+ "version": "==2.0.2"
962
962
  },
963
963
  "urllib3": {
964
964
  "hashes": [
@@ -970,11 +970,11 @@
970
970
  },
971
971
  "virtualenv": {
972
972
  "hashes": [
973
- "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6",
974
- "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4"
973
+ "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48",
974
+ "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2"
975
975
  ],
976
976
  "markers": "python_version >= '3.7'",
977
- "version": "==20.26.5"
977
+ "version": "==20.26.6"
978
978
  },
979
979
  "zipp": {
980
980
  "hashes": [
@@ -306,7 +306,7 @@ fotolab watermark -h
306
306
  usage: fotolab watermark [-h] [-t WATERMARK_TEXT]
307
307
  [-p {top-left,top-right,bottom-left,bottom-right}]
308
308
  [-pd PADDING] [-fs FONT_SIZE] [-fc FONT_COLOR]
309
- [-ow OUTLINE_WIDTH] [-oc OUTLINE_COLOR]
309
+ [-ow OUTLINE_WIDTH] [-oc OUTLINE_COLOR] [--camera]
310
310
  IMAGE_FILENAMES [IMAGE_FILENAMES ...]
311
311
 
312
312
  positional arguments:
@@ -333,6 +333,7 @@ options:
333
333
  -oc OUTLINE_COLOR, --outline-color OUTLINE_COLOR
334
334
  set the outline color of the watermark text (default:
335
335
  'black')
336
+ --camera use camera metadata as watermark
336
337
  ```
337
338
 
338
339
  <!--help-watermark !-->
@@ -21,7 +21,7 @@ import subprocess
21
21
  import sys
22
22
  from pathlib import Path
23
23
 
24
- __version__ = "0.17.0"
24
+ __version__ = "0.18.1"
25
25
 
26
26
  log = logging.getLogger(__name__)
27
27
 
@@ -67,6 +67,8 @@ def run(args: argparse.Namespace) -> None:
67
67
  "outline_width": 2,
68
68
  "outline_color": "black",
69
69
  "padding": 15,
70
+ "camera": False,
71
+ "lowercase": False,
70
72
  }
71
73
  combined_args = argparse.Namespace(**vars(args), **extra_args)
72
74
  combined_args.overwrite = True
@@ -132,7 +132,7 @@ def build_parser() -> argparse.ArgumentParser:
132
132
  "-V",
133
133
  "--version",
134
134
  action="version",
135
- version=f"fotolab {__version__}",
135
+ version=f"%(prog)s {__version__}",
136
136
  )
137
137
 
138
138
  subparsers = parser.add_subparsers(help="sub-command help")
@@ -34,7 +34,7 @@ def build_subparser(subparsers) -> None:
34
34
  env_parser.set_defaults(func=run)
35
35
 
36
36
 
37
- def run(args: argparse.Namespace) -> None:
37
+ def run(_args: argparse.Namespace) -> None:
38
38
  """Run env subcommand.
39
39
 
40
40
  Args:
@@ -43,8 +43,6 @@ def run(args: argparse.Namespace) -> None:
43
43
  Returns:
44
44
  None
45
45
  """
46
- log.debug(args)
47
-
48
46
  sys_version = sys.version.replace("\n", "")
49
47
  print(
50
48
  f"fotolab: {__version__}",
@@ -58,16 +58,22 @@ def run(args: argparse.Namespace) -> None:
58
58
  """
59
59
  log.debug(args)
60
60
 
61
- image = Image.open(args.image_filename)
61
+ exif_tags = extract_exif_tags(args.image.filename)
62
+ tag_name_width = max(map(len, exif_tags))
63
+ for tag_name, tag_value in exif_tags.items():
64
+ print(f"{tag_name:<{tag_name_width}}: {tag_value}")
65
+
66
+
67
+ def extract_exif_tags(image_filename: str, sort: bool = False) -> dict:
68
+ """Extract Exif metadata from image."""
69
+ image = Image.open(image_filename)
62
70
  exif = image._getexif()
63
71
  info = {ExifTags.TAGS.get(tag_id): exif.get(tag_id) for tag_id in exif}
64
72
 
65
73
  filtered_info = {
66
74
  key: value for key, value in info.items() if key is not None
67
75
  }
68
- if args.sort:
76
+ if sort:
69
77
  filtered_info = dict(sorted(filtered_info.items()))
70
78
 
71
- tag_name_width = max(map(len, filtered_info))
72
- for tag_name, tag_value in filtered_info.items():
73
- print(f"{tag_name:<{tag_name_width}}: {tag_value}")
79
+ return filtered_info
@@ -22,6 +22,7 @@ import math
22
22
  from PIL import Image, ImageColor, ImageDraw, ImageFont
23
23
 
24
24
  from fotolab import save_image
25
+ from fotolab.info import extract_exif_tags
25
26
 
26
27
  log = logging.getLogger(__name__)
27
28
 
@@ -126,12 +127,29 @@ def build_subparser(subparsers) -> None:
126
127
  metavar="OUTLINE_COLOR",
127
128
  )
128
129
 
130
+ watermark_parser.add_argument(
131
+ "--camera",
132
+ default=False,
133
+ action="store_true",
134
+ dest="camera",
135
+ help="use camera metadata as watermark",
136
+ )
137
+
138
+ watermark_parser.add_argument(
139
+ "-l",
140
+ "--lowercase",
141
+ default=True,
142
+ action="store_true",
143
+ dest="lowercase",
144
+ help="lowercase the watermark text",
145
+ )
146
+
129
147
 
130
148
  def run(args: argparse.Namespace) -> None:
131
149
  """Run watermark subcommand.
132
150
 
133
151
  Args:
134
- config (argparse.Namespace): Config from command line arguments
152
+ args (argparse.Namespace): Config from command line arguments
135
153
 
136
154
  Returns:
137
155
  None
@@ -147,8 +165,15 @@ def run(args: argparse.Namespace) -> None:
147
165
  font = ImageFont.load_default(calc_font_size(original_image, args))
148
166
  log.debug("default font: %s", " ".join(font.getname()))
149
167
 
168
+ text = args.text
169
+ if args.camera and camera_metadata(image_filename):
170
+ text = camera_metadata(image_filename)
171
+
172
+ if args.lowercase:
173
+ text = text.lower()
174
+
150
175
  (left, top, right, bottom) = draw.textbbox(
151
- xy=(0, 0), text=args.text, font=font
176
+ xy=(0, 0), text=text, font=font
152
177
  )
153
178
  text_width = right - left
154
179
  text_height = bottom - top
@@ -162,7 +187,7 @@ def run(args: argparse.Namespace) -> None:
162
187
 
163
188
  draw.text(
164
189
  (position_x, position_y),
165
- args.text,
190
+ text,
166
191
  font=font,
167
192
  fill=(*ImageColor.getrgb(args.font_color), 128),
168
193
  stroke_width=calc_font_outline_width(original_image, args),
@@ -172,6 +197,13 @@ def run(args: argparse.Namespace) -> None:
172
197
  save_image(args, watermarked_image, image_filename, "watermark")
173
198
 
174
199
 
200
+ def camera_metadata(image_filename):
201
+ """Extract camera and model metadata."""
202
+ exif_tags = extract_exif_tags(image_filename)
203
+ metadata = f'{exif_tags["Make"]} {exif_tags["Model"]}'
204
+ return metadata.strip()
205
+
206
+
175
207
  def calc_font_size(image, args) -> int:
176
208
  """Calculate the font size based on the width of the image."""
177
209
  width, _height = image.size
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