fotolab 0.9.1__tar.gz → 0.12.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 (39) hide show
  1. {fotolab-0.9.1 → fotolab-0.12.0}/.pre-commit-config.yaml +3 -3
  2. {fotolab-0.9.1 → fotolab-0.12.0}/CHANGELOG.md +44 -0
  3. {fotolab-0.9.1 → fotolab-0.12.0}/PKG-INFO +63 -7
  4. {fotolab-0.9.1 → fotolab-0.12.0}/Pipfile.lock +161 -159
  5. {fotolab-0.9.1 → fotolab-0.12.0}/README.md +62 -6
  6. {fotolab-0.9.1 → fotolab-0.12.0}/fotolab/__init__.py +10 -7
  7. {fotolab-0.9.1 → fotolab-0.12.0}/fotolab/auto.py +1 -0
  8. {fotolab-0.9.1 → fotolab-0.12.0}/fotolab/border.py +1 -1
  9. {fotolab-0.9.1 → fotolab-0.12.0}/fotolab/cli.py +5 -1
  10. {fotolab-0.9.1 → fotolab-0.12.0}/fotolab/contrast.py +1 -1
  11. fotolab-0.12.0/fotolab/info.py +58 -0
  12. fotolab-0.12.0/fotolab/montage.py +70 -0
  13. {fotolab-0.9.1 → fotolab-0.12.0}/fotolab/resize.py +1 -1
  14. {fotolab-0.9.1 → fotolab-0.12.0}/fotolab/sharpen.py +1 -1
  15. {fotolab-0.9.1 → fotolab-0.12.0}/fotolab/watermark.py +20 -6
  16. {fotolab-0.9.1 → fotolab-0.12.0}/.coveragerc +0 -0
  17. {fotolab-0.9.1 → fotolab-0.12.0}/.gitignore +0 -0
  18. {fotolab-0.9.1 → fotolab-0.12.0}/.python-version +0 -0
  19. {fotolab-0.9.1 → fotolab-0.12.0}/CONTRIBUTING.md +0 -0
  20. {fotolab-0.9.1 → fotolab-0.12.0}/LICENSE.md +0 -0
  21. {fotolab-0.9.1 → fotolab-0.12.0}/Pipfile +0 -0
  22. {fotolab-0.9.1 → fotolab-0.12.0}/docs/Makefile +0 -0
  23. {fotolab-0.9.1 → fotolab-0.12.0}/docs/make.bat +0 -0
  24. {fotolab-0.9.1 → fotolab-0.12.0}/docs/source/CHANGELOG.md +0 -0
  25. {fotolab-0.9.1 → fotolab-0.12.0}/docs/source/CONTRIBUTING.md +0 -0
  26. {fotolab-0.9.1 → fotolab-0.12.0}/docs/source/LICENSE.md +0 -0
  27. {fotolab-0.9.1 → fotolab-0.12.0}/docs/source/README.md +0 -0
  28. {fotolab-0.9.1 → fotolab-0.12.0}/docs/source/_static/logo.jpg +0 -0
  29. {fotolab-0.9.1 → fotolab-0.12.0}/docs/source/conf.py +0 -0
  30. {fotolab-0.9.1 → fotolab-0.12.0}/docs/source/index.rst +0 -0
  31. {fotolab-0.9.1 → fotolab-0.12.0}/fotolab/__main__.py +0 -0
  32. {fotolab-0.9.1 → fotolab-0.12.0}/fotolab/env.py +0 -0
  33. {fotolab-0.9.1 → fotolab-0.12.0}/pyproject.toml +0 -0
  34. {fotolab-0.9.1 → fotolab-0.12.0}/tests/__init__.py +0 -0
  35. {fotolab-0.9.1 → fotolab-0.12.0}/tests/conftest.py +0 -0
  36. {fotolab-0.9.1 → fotolab-0.12.0}/tests/test_env.py +0 -0
  37. {fotolab-0.9.1 → fotolab-0.12.0}/tests/test_help_flag.py +0 -0
  38. {fotolab-0.9.1 → fotolab-0.12.0}/tests/test_quiet_flag.py +0 -0
  39. {fotolab-0.9.1 → fotolab-0.12.0}/tox.ini +0 -0
@@ -2,7 +2,7 @@
2
2
  # See https://pre-commit.com/hooks.html for more hooks
3
3
  repos:
4
4
  - repo: https://github.com/pre-commit/pre-commit-hooks
5
- rev: v4.5.0
5
+ rev: v4.6.0
6
6
  hooks:
7
7
  - id: check-case-conflict
8
8
  - id: check-merge-conflict
@@ -45,7 +45,7 @@ repos:
45
45
  - --py=312
46
46
 
47
47
  - repo: https://github.com/psf/black
48
- rev: 24.3.0
48
+ rev: 24.4.0
49
49
  hooks:
50
50
  - id: black
51
51
  language_version: python3.12
@@ -104,7 +104,7 @@ repos:
104
104
  - fotolab
105
105
  - tests
106
106
  - --unsafe-load-any-extension=y
107
- - --disable=R0801
107
+ - --disable=R0801,W0212
108
108
 
109
109
  - repo: https://github.com/pre-commit/mirrors-mypy
110
110
  rev: v1.9.0
@@ -7,6 +7,50 @@ and this project adheres to [0-based versioning](https://0ver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## v0.12.0 (2024-04-14)
11
+
12
+ ### Added
13
+
14
+ - Add `-pd` or `--padding` option to set the padding of watermark text block
15
+
16
+ ### Changed
17
+
18
+ - Bump deps, `pre-commit` hooks, and Python versions for `pyenv`
19
+
20
+ ### Fixed
21
+
22
+ - Fix `auto` subcommand missing `padding` field
23
+ - Fix missing closing parenthesis in help message
24
+ - Update missing subcommand help message in README
25
+
26
+ ## v0.11.0 (2024-04-07)
27
+
28
+ ### Added
29
+
30
+ - Add `montage` subcommand to join multiple images
31
+
32
+ ### Changed
33
+
34
+ - Bump deps
35
+ - Update missing subcommand help message in README
36
+
37
+ ## v0.10.0 (2024-03-31)
38
+
39
+ ### Added
40
+
41
+ - Add `info` subcommand to show EXIF data from image
42
+
43
+ ### Changed
44
+
45
+ - Bump deps
46
+ - Update help message in README
47
+ - Ignore `pylint` W0212 rule
48
+
49
+ ### Fix
50
+
51
+ - Fix incorrect action in log when running `auto` subcommand
52
+ - Update incorrect comment for `save_image` helper function
53
+
10
54
  ## v0.9.1 (2024-03-24)
11
55
 
12
56
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fotolab
3
- Version: 0.9.1
3
+ Version: 0.12.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>
@@ -57,9 +57,8 @@ fotolab -h
57
57
  ```
58
58
 
59
59
  ```console
60
-
61
60
  usage: fotolab [-h] [-o] [-op] [-od OUTPUT_DIR] [-q] [-d] [-V]
62
- {auto,border,contrast,resize,sharpen,watermark,env} ...
61
+ {auto,border,contrast,info,resize,sharpen,watermark,env} ...
63
62
 
64
63
  A console program to manipulate photos.
65
64
 
@@ -68,11 +67,12 @@ A console program to manipulate photos.
68
67
  issues: https://github.com/kianmeng/fotolab/issues
69
68
 
70
69
  positional arguments:
71
- {auto,border,contrast,resize,sharpen,watermark,env}
70
+ {auto,border,contrast,info,resize,sharpen,watermark,env}
72
71
  sub-command help
73
72
  auto auto adjust (resize, contrast, and watermark) a photo
74
73
  border add border to image
75
74
  contrast contrast an image
75
+ info info an image
76
76
  resize resize an image
77
77
  sharpen sharpen an image
78
78
  watermark watermark an image
@@ -89,6 +89,22 @@ optional arguments:
89
89
  -V, --version show program's version number and exit
90
90
  ```
91
91
 
92
+ ### fotolab auto
93
+
94
+ ```console
95
+ fotolab auto -h
96
+ ```
97
+
98
+ ```console
99
+ usage: fotolab auto [-h] IMAGE_FILENAME
100
+
101
+ positional arguments:
102
+ IMAGE_FILENAME set the image filename
103
+
104
+ optional arguments:
105
+ -h, --help show this help message and exit
106
+ ```
107
+
92
108
  ### fotolab border
93
109
 
94
110
  ```console
@@ -135,6 +151,43 @@ optional arguments:
135
151
  -h, --help show this help message and exit
136
152
  ```
137
153
 
154
+ ### fotolab info
155
+
156
+ ```console
157
+ fotolab info -h
158
+ ```
159
+
160
+ ```console
161
+ usage: fotolab info [-h] IMAGE_FILENAME
162
+
163
+ positional arguments:
164
+ IMAGE_FILENAME set the image filename
165
+
166
+ optional arguments:
167
+ -h, --help show this help message and exit
168
+ ```
169
+
170
+ ### fotolab resize
171
+
172
+ ```console
173
+ fotolab resize -h
174
+ ```
175
+
176
+ ```console
177
+
178
+ usage: fotolab resize [-h] [-wh WIDTH] [-ht HEIGHT] IMAGE_FILENAME
179
+
180
+ positional arguments:
181
+ IMAGE_FILENAME set the image filename
182
+
183
+ optional arguments:
184
+ -h, --help show this help message and exit
185
+ -wh WIDTH, --width WIDTH
186
+ set the width of the image (default: '600')
187
+ -ht HEIGHT, --height HEIGHT
188
+ set the height of the image (default: '277')
189
+ ```
190
+
138
191
  ### fotolab sharpen
139
192
 
140
193
  ```console
@@ -160,20 +213,23 @@ fotolab watermark -h
160
213
  ```console
161
214
  usage: fotolab watermark [-h] [-t WATERMARK_TEXT]
162
215
  [-p {top-left,top-right,bottom-left,bottom-right}]
163
- [-fs FONT_SIZE] [-fc FONT_COLOR] [-ow OUTLINE_WIDTH]
164
- [-oc OUTLINE_COLOR]
216
+ [-pd PADDING] [-fs FONT_SIZE] [-fc FONT_COLOR]
217
+ [-ow OUTLINE_WIDTH] [-oc OUTLINE_COLOR]
165
218
  IMAGE_FILENAME
166
219
 
167
220
  positional arguments:
168
221
  IMAGE_FILENAME set the image filename
169
222
 
170
- options:
223
+ optional arguments:
171
224
  -h, --help show this help message and exit
172
225
  -t WATERMARK_TEXT, --text WATERMARK_TEXT
173
226
  set the watermark text (default: 'kianmeng.org')
174
227
  -p {top-left,top-right,bottom-left,bottom-right}, --position {top-left,top-right,bottom-left,bottom-right}
175
228
  set position of the watermark text (default: 'bottom-
176
229
  left')
230
+ -pd PADDING, --padding PADDING
231
+ set the padding of the watermark text relative to the
232
+ image (default: '15')
177
233
  -fs FONT_SIZE, --font-size FONT_SIZE
178
234
  set the font size of watermark text (default: '12')
179
235
  -fc FONT_COLOR, --font-color FONT_COLOR
@@ -18,78 +18,79 @@
18
18
  "default": {
19
19
  "pillow": {
20
20
  "hashes": [
21
- "sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8",
22
- "sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39",
23
- "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac",
24
- "sha256:0fb3e7fc88a14eacd303e90481ad983fd5b69c761e9e6ef94c983f91025da869",
25
- "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e",
26
- "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04",
27
- "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9",
28
- "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e",
29
- "sha256:170aeb00224ab3dc54230c797f8404507240dd868cf52066f66a41b33169bdbe",
30
- "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef",
31
- "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56",
32
- "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa",
33
- "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f",
34
- "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f",
35
- "sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e",
36
- "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a",
37
- "sha256:322bdf3c9b556e9ffb18f93462e5f749d3444ce081290352c6070d014c93feb2",
38
- "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2",
39
- "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5",
40
- "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a",
41
- "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2",
42
- "sha256:3ff074fc97dd4e80543a3e91f69d58889baf2002b6be64347ea8cf5533188213",
43
- "sha256:47c0995fc4e7f79b5cfcab1fc437ff2890b770440f7696a3ba065ee0fd496563",
44
- "sha256:49d9ba1ed0ef3e061088cd1e7538a0759aab559e2e0a80a36f9fd9d8c0c21591",
45
- "sha256:51f1a1bffc50e2e9492e87d8e09a17c5eea8409cda8d3f277eb6edc82813c17c",
46
- "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2",
47
- "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb",
48
- "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757",
49
- "sha256:69ffdd6120a4737710a9eee73e1d2e37db89b620f702754b8f6e62594471dee0",
50
- "sha256:6fad5ff2f13d69b7e74ce5b4ecd12cc0ec530fcee76356cac6742785ff71c452",
51
- "sha256:7049e301399273a0136ff39b84c3678e314f2158f50f517bc50285fb5ec847ad",
52
- "sha256:70c61d4c475835a19b3a5aa42492409878bbca7438554a1f89d20d58a7c75c01",
53
- "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f",
54
- "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5",
55
- "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61",
56
- "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e",
57
- "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b",
58
- "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068",
59
- "sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9",
60
- "sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588",
61
- "sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483",
62
- "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f",
63
- "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67",
64
- "sha256:9d189550615b4948f45252d7f005e53c2040cea1af5b60d6f79491a6e147eef7",
65
- "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311",
66
- "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6",
67
- "sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72",
68
- "sha256:b1b3020d90c2d8e1dae29cf3ce54f8094f7938460fb5ce8bc5c01450b01fbaf6",
69
- "sha256:b4b6b1e20608493548b1f32bce8cca185bf0480983890403d3b8753e44077129",
70
- "sha256:b6f491cdf80ae540738859d9766783e3b3c8e5bd37f5dfa0b76abdecc5081f13",
71
- "sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67",
72
- "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c",
73
- "sha256:babf5acfede515f176833ed6028754cbcd0d206f7f614ea3447d67c33be12516",
74
- "sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e",
75
- "sha256:c4225f5220f46b2fde568c74fca27ae9771536c2e29d7c04f4fb62c83275ac4e",
76
- "sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364",
77
- "sha256:c6dafac9e0f2b3c78df97e79af707cdc5ef8e88208d686a4847bab8266870023",
78
- "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1",
79
- "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04",
80
- "sha256:cb4c38abeef13c61d6916f264d4845fab99d7b711be96c326b84df9e3e0ff62d",
81
- "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a",
82
- "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7",
83
- "sha256:d8ecd059fdaf60c1963c58ceb8997b32e9dc1b911f5da5307aab614f1ce5c2fb",
84
- "sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4",
85
- "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e",
86
- "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1",
87
- "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48",
88
- "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868"
21
+ "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c",
22
+ "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2",
23
+ "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb",
24
+ "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d",
25
+ "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa",
26
+ "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3",
27
+ "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1",
28
+ "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a",
29
+ "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd",
30
+ "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8",
31
+ "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999",
32
+ "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599",
33
+ "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936",
34
+ "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375",
35
+ "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d",
36
+ "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b",
37
+ "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60",
38
+ "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572",
39
+ "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3",
40
+ "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced",
41
+ "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f",
42
+ "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b",
43
+ "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19",
44
+ "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f",
45
+ "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d",
46
+ "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383",
47
+ "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795",
48
+ "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355",
49
+ "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57",
50
+ "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09",
51
+ "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b",
52
+ "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462",
53
+ "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf",
54
+ "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f",
55
+ "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a",
56
+ "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad",
57
+ "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9",
58
+ "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d",
59
+ "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45",
60
+ "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994",
61
+ "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d",
62
+ "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338",
63
+ "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463",
64
+ "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451",
65
+ "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591",
66
+ "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c",
67
+ "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd",
68
+ "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32",
69
+ "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9",
70
+ "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf",
71
+ "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5",
72
+ "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828",
73
+ "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3",
74
+ "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5",
75
+ "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2",
76
+ "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b",
77
+ "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2",
78
+ "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475",
79
+ "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3",
80
+ "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb",
81
+ "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef",
82
+ "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015",
83
+ "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002",
84
+ "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170",
85
+ "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84",
86
+ "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57",
87
+ "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f",
88
+ "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27",
89
+ "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"
89
90
  ],
90
91
  "index": "pypi",
91
92
  "markers": "python_version >= '3.8'",
92
- "version": "==10.2.0"
93
+ "version": "==10.3.0"
93
94
  }
94
95
  },
95
96
  "develop": {
@@ -188,19 +189,19 @@
188
189
  },
189
190
  "execnet": {
190
191
  "hashes": [
191
- "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41",
192
- "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"
192
+ "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc",
193
+ "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"
193
194
  ],
194
- "markers": "python_version >= '3.7'",
195
- "version": "==2.0.2"
195
+ "markers": "python_version >= '3.8'",
196
+ "version": "==2.1.1"
196
197
  },
197
198
  "filelock": {
198
199
  "hashes": [
199
- "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e",
200
- "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"
200
+ "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f",
201
+ "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"
201
202
  ],
202
203
  "markers": "python_version >= '3.8'",
203
- "version": "==3.13.1"
204
+ "version": "==3.13.4"
204
205
  },
205
206
  "flake8": {
206
207
  "hashes": [
@@ -238,12 +239,12 @@
238
239
  },
239
240
  "flake8-pytest-style": {
240
241
  "hashes": [
241
- "sha256:b924197c99b951315949920b0e5547f34900b1844348432e67a44ab191582109",
242
- "sha256:f5d2aa3219163a052dd92226589d45fab8ea027a3269922f0c4029f548ea5cd1"
242
+ "sha256:919c328cacd4bc4f873ea61ab4db0d8f2c32e0db09a3c73ab46b1de497556464",
243
+ "sha256:abcb9f56f277954014b749e5a0937fae215be01a21852e9d05e7600c3de6aae5"
243
244
  ],
244
245
  "index": "pypi",
245
- "markers": "python_full_version >= '3.7.2' and python_full_version < '4.0.0'",
246
- "version": "==1.7.2"
246
+ "markers": "python_full_version >= '3.8.1' and python_full_version < '4.0.0'",
247
+ "version": "==2.0.0"
247
248
  },
248
249
  "flake8-simplify": {
249
250
  "hashes": [
@@ -309,78 +310,79 @@
309
310
  },
310
311
  "pillow": {
311
312
  "hashes": [
312
- "sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8",
313
- "sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39",
314
- "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac",
315
- "sha256:0fb3e7fc88a14eacd303e90481ad983fd5b69c761e9e6ef94c983f91025da869",
316
- "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e",
317
- "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04",
318
- "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9",
319
- "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e",
320
- "sha256:170aeb00224ab3dc54230c797f8404507240dd868cf52066f66a41b33169bdbe",
321
- "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef",
322
- "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56",
323
- "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa",
324
- "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f",
325
- "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f",
326
- "sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e",
327
- "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a",
328
- "sha256:322bdf3c9b556e9ffb18f93462e5f749d3444ce081290352c6070d014c93feb2",
329
- "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2",
330
- "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5",
331
- "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a",
332
- "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2",
333
- "sha256:3ff074fc97dd4e80543a3e91f69d58889baf2002b6be64347ea8cf5533188213",
334
- "sha256:47c0995fc4e7f79b5cfcab1fc437ff2890b770440f7696a3ba065ee0fd496563",
335
- "sha256:49d9ba1ed0ef3e061088cd1e7538a0759aab559e2e0a80a36f9fd9d8c0c21591",
336
- "sha256:51f1a1bffc50e2e9492e87d8e09a17c5eea8409cda8d3f277eb6edc82813c17c",
337
- "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2",
338
- "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb",
339
- "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757",
340
- "sha256:69ffdd6120a4737710a9eee73e1d2e37db89b620f702754b8f6e62594471dee0",
341
- "sha256:6fad5ff2f13d69b7e74ce5b4ecd12cc0ec530fcee76356cac6742785ff71c452",
342
- "sha256:7049e301399273a0136ff39b84c3678e314f2158f50f517bc50285fb5ec847ad",
343
- "sha256:70c61d4c475835a19b3a5aa42492409878bbca7438554a1f89d20d58a7c75c01",
344
- "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f",
345
- "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5",
346
- "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61",
347
- "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e",
348
- "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b",
349
- "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068",
350
- "sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9",
351
- "sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588",
352
- "sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483",
353
- "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f",
354
- "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67",
355
- "sha256:9d189550615b4948f45252d7f005e53c2040cea1af5b60d6f79491a6e147eef7",
356
- "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311",
357
- "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6",
358
- "sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72",
359
- "sha256:b1b3020d90c2d8e1dae29cf3ce54f8094f7938460fb5ce8bc5c01450b01fbaf6",
360
- "sha256:b4b6b1e20608493548b1f32bce8cca185bf0480983890403d3b8753e44077129",
361
- "sha256:b6f491cdf80ae540738859d9766783e3b3c8e5bd37f5dfa0b76abdecc5081f13",
362
- "sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67",
363
- "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c",
364
- "sha256:babf5acfede515f176833ed6028754cbcd0d206f7f614ea3447d67c33be12516",
365
- "sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e",
366
- "sha256:c4225f5220f46b2fde568c74fca27ae9771536c2e29d7c04f4fb62c83275ac4e",
367
- "sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364",
368
- "sha256:c6dafac9e0f2b3c78df97e79af707cdc5ef8e88208d686a4847bab8266870023",
369
- "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1",
370
- "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04",
371
- "sha256:cb4c38abeef13c61d6916f264d4845fab99d7b711be96c326b84df9e3e0ff62d",
372
- "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a",
373
- "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7",
374
- "sha256:d8ecd059fdaf60c1963c58ceb8997b32e9dc1b911f5da5307aab614f1ce5c2fb",
375
- "sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4",
376
- "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e",
377
- "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1",
378
- "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48",
379
- "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868"
313
+ "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c",
314
+ "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2",
315
+ "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb",
316
+ "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d",
317
+ "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa",
318
+ "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3",
319
+ "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1",
320
+ "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a",
321
+ "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd",
322
+ "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8",
323
+ "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999",
324
+ "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599",
325
+ "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936",
326
+ "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375",
327
+ "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d",
328
+ "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b",
329
+ "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60",
330
+ "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572",
331
+ "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3",
332
+ "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced",
333
+ "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f",
334
+ "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b",
335
+ "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19",
336
+ "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f",
337
+ "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d",
338
+ "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383",
339
+ "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795",
340
+ "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355",
341
+ "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57",
342
+ "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09",
343
+ "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b",
344
+ "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462",
345
+ "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf",
346
+ "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f",
347
+ "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a",
348
+ "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad",
349
+ "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9",
350
+ "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d",
351
+ "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45",
352
+ "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994",
353
+ "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d",
354
+ "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338",
355
+ "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463",
356
+ "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451",
357
+ "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591",
358
+ "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c",
359
+ "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd",
360
+ "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32",
361
+ "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9",
362
+ "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf",
363
+ "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5",
364
+ "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828",
365
+ "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3",
366
+ "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5",
367
+ "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2",
368
+ "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b",
369
+ "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2",
370
+ "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475",
371
+ "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3",
372
+ "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb",
373
+ "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef",
374
+ "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015",
375
+ "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002",
376
+ "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170",
377
+ "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84",
378
+ "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57",
379
+ "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f",
380
+ "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27",
381
+ "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"
380
382
  ],
381
383
  "index": "pypi",
382
384
  "markers": "python_version >= '3.8'",
383
- "version": "==10.2.0"
385
+ "version": "==10.3.0"
384
386
  },
385
387
  "platformdirs": {
386
388
  "hashes": [
@@ -450,12 +452,12 @@
450
452
  },
451
453
  "pytest-cov": {
452
454
  "hashes": [
453
- "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6",
454
- "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"
455
+ "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652",
456
+ "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"
455
457
  ],
456
458
  "index": "pypi",
457
- "markers": "python_version >= '3.7'",
458
- "version": "==4.1.0"
459
+ "markers": "python_version >= '3.8'",
460
+ "version": "==5.0.0"
459
461
  },
460
462
  "pytest-randomly": {
461
463
  "hashes": [
@@ -736,11 +738,11 @@
736
738
  },
737
739
  "idna": {
738
740
  "hashes": [
739
- "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca",
740
- "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"
741
+ "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc",
742
+ "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"
741
743
  ],
742
744
  "markers": "python_version >= '3.5'",
743
- "version": "==3.6"
745
+ "version": "==3.7"
744
746
  },
745
747
  "imagesize": {
746
748
  "hashes": [
@@ -972,12 +974,12 @@
972
974
  },
973
975
  "sphinx-autodoc-typehints": {
974
976
  "hashes": [
975
- "sha256:12c0e161f6fe191c2cdfd8fa3caea271f5387d9fbc67ebcd6f4f1f24ce880993",
976
- "sha256:7f2cdac2e70fd9787926b6e9e541cd4ded1e838d2b46fda2a1bb0a75ec5b7f3a"
977
+ "sha256:60ed1e3b2c970acc0aa6e877be42d48029a9faec7378a17838716cacd8c10b12",
978
+ "sha256:f73ae89b43a799e587e39266672c1075b2ef783aeb382d3ebed77c38a3fc0149"
977
979
  ],
978
980
  "index": "pypi",
979
981
  "markers": "python_version >= '3.8'",
980
- "version": "==2.0.0"
982
+ "version": "==2.0.1"
981
983
  },
982
984
  "sphinx-copybutton": {
983
985
  "hashes": [
@@ -35,9 +35,8 @@ fotolab -h
35
35
  ```
36
36
 
37
37
  ```console
38
-
39
38
  usage: fotolab [-h] [-o] [-op] [-od OUTPUT_DIR] [-q] [-d] [-V]
40
- {auto,border,contrast,resize,sharpen,watermark,env} ...
39
+ {auto,border,contrast,info,resize,sharpen,watermark,env} ...
41
40
 
42
41
  A console program to manipulate photos.
43
42
 
@@ -46,11 +45,12 @@ A console program to manipulate photos.
46
45
  issues: https://github.com/kianmeng/fotolab/issues
47
46
 
48
47
  positional arguments:
49
- {auto,border,contrast,resize,sharpen,watermark,env}
48
+ {auto,border,contrast,info,resize,sharpen,watermark,env}
50
49
  sub-command help
51
50
  auto auto adjust (resize, contrast, and watermark) a photo
52
51
  border add border to image
53
52
  contrast contrast an image
53
+ info info an image
54
54
  resize resize an image
55
55
  sharpen sharpen an image
56
56
  watermark watermark an image
@@ -67,6 +67,22 @@ optional arguments:
67
67
  -V, --version show program's version number and exit
68
68
  ```
69
69
 
70
+ ### fotolab auto
71
+
72
+ ```console
73
+ fotolab auto -h
74
+ ```
75
+
76
+ ```console
77
+ usage: fotolab auto [-h] IMAGE_FILENAME
78
+
79
+ positional arguments:
80
+ IMAGE_FILENAME set the image filename
81
+
82
+ optional arguments:
83
+ -h, --help show this help message and exit
84
+ ```
85
+
70
86
  ### fotolab border
71
87
 
72
88
  ```console
@@ -113,6 +129,43 @@ optional arguments:
113
129
  -h, --help show this help message and exit
114
130
  ```
115
131
 
132
+ ### fotolab info
133
+
134
+ ```console
135
+ fotolab info -h
136
+ ```
137
+
138
+ ```console
139
+ usage: fotolab info [-h] IMAGE_FILENAME
140
+
141
+ positional arguments:
142
+ IMAGE_FILENAME set the image filename
143
+
144
+ optional arguments:
145
+ -h, --help show this help message and exit
146
+ ```
147
+
148
+ ### fotolab resize
149
+
150
+ ```console
151
+ fotolab resize -h
152
+ ```
153
+
154
+ ```console
155
+
156
+ usage: fotolab resize [-h] [-wh WIDTH] [-ht HEIGHT] IMAGE_FILENAME
157
+
158
+ positional arguments:
159
+ IMAGE_FILENAME set the image filename
160
+
161
+ optional arguments:
162
+ -h, --help show this help message and exit
163
+ -wh WIDTH, --width WIDTH
164
+ set the width of the image (default: '600')
165
+ -ht HEIGHT, --height HEIGHT
166
+ set the height of the image (default: '277')
167
+ ```
168
+
116
169
  ### fotolab sharpen
117
170
 
118
171
  ```console
@@ -138,20 +191,23 @@ fotolab watermark -h
138
191
  ```console
139
192
  usage: fotolab watermark [-h] [-t WATERMARK_TEXT]
140
193
  [-p {top-left,top-right,bottom-left,bottom-right}]
141
- [-fs FONT_SIZE] [-fc FONT_COLOR] [-ow OUTLINE_WIDTH]
142
- [-oc OUTLINE_COLOR]
194
+ [-pd PADDING] [-fs FONT_SIZE] [-fc FONT_COLOR]
195
+ [-ow OUTLINE_WIDTH] [-oc OUTLINE_COLOR]
143
196
  IMAGE_FILENAME
144
197
 
145
198
  positional arguments:
146
199
  IMAGE_FILENAME set the image filename
147
200
 
148
- options:
201
+ optional arguments:
149
202
  -h, --help show this help message and exit
150
203
  -t WATERMARK_TEXT, --text WATERMARK_TEXT
151
204
  set the watermark text (default: 'kianmeng.org')
152
205
  -p {top-left,top-right,bottom-left,bottom-right}, --position {top-left,top-right,bottom-left,bottom-right}
153
206
  set position of the watermark text (default: 'bottom-
154
207
  left')
208
+ -pd PADDING, --padding PADDING
209
+ set the padding of the watermark text relative to the
210
+ image (default: '15')
155
211
  -fs FONT_SIZE, --font-size FONT_SIZE
156
212
  set the font size of watermark text (default: '12')
157
213
  -fc FONT_COLOR, --font-color FONT_COLOR
@@ -21,34 +21,37 @@ import subprocess
21
21
  import sys
22
22
  from pathlib import Path
23
23
 
24
- __version__ = "0.9.1"
24
+ __version__ = "0.12.0"
25
25
 
26
26
  log = logging.getLogger(__name__)
27
27
 
28
28
 
29
- def save_image(args, new_image):
30
- """Run resize subcommand.
29
+ def save_image(args, new_image, subcommand):
30
+ """Save image after image operation.
31
31
 
32
32
  Args:
33
33
  args (argparse.Namespace): Config from command line arguments
34
34
  new_image(PIL.Image.Image): Modified image
35
+ subcommand(str): Subcommand used to call this function
35
36
 
36
37
  Returns:
37
38
  None
38
39
  """
39
- action = args.func.__module__.split(".")[-1]
40
- image_file = Path(args.image_filename)
40
+ if "image_filenames" in args:
41
+ image_file = Path(args.image_filenames[0].name)
42
+ else:
43
+ image_file = Path(args.image_filename)
41
44
 
42
45
  if args.overwrite:
43
46
  new_filename = image_file.with_name(image_file.name)
44
47
  else:
45
48
  new_filename = Path(
46
49
  args.output_dir,
47
- image_file.with_name(f"{action}_{image_file.name}"),
50
+ image_file.with_name(f"{subcommand}_{image_file.name}"),
48
51
  )
49
52
  new_filename.parent.mkdir(parents=True, exist_ok=True)
50
53
 
51
- log.info("%s image: %s", action, new_filename)
54
+ log.info("%s image: %s", subcommand, new_filename)
52
55
  new_image.save(new_filename)
53
56
 
54
57
  if args.open:
@@ -65,6 +65,7 @@ def run(args: argparse.Namespace) -> None:
65
65
  "font_color": "white",
66
66
  "outline_width": 2,
67
67
  "outline_color": "black",
68
+ "padding": 15,
68
69
  }
69
70
  combined_args = argparse.Namespace(**vars(args), **extra_args)
70
71
  combined_args.overwrite = True
@@ -134,4 +134,4 @@ def run(args: argparse.Namespace) -> None:
134
134
  fill=ImageColor.getrgb(args.color),
135
135
  )
136
136
 
137
- save_image(args, bordered_image)
137
+ save_image(args, bordered_image, "border")
@@ -29,6 +29,8 @@ import fotolab.auto
29
29
  import fotolab.border
30
30
  import fotolab.contrast
31
31
  import fotolab.env
32
+ import fotolab.info
33
+ import fotolab.montage
32
34
  import fotolab.resize
33
35
  import fotolab.sharpen
34
36
  import fotolab.watermark
@@ -82,7 +84,7 @@ def build_parser() -> argparse.ArgumentParser:
82
84
  default=False,
83
85
  action="store_true",
84
86
  dest="open",
85
- help="open the image using default program (default: '%(default)s'",
87
+ help="open the image using default program (default: '%(default)s')",
86
88
  )
87
89
 
88
90
  parser.add_argument(
@@ -122,7 +124,9 @@ def build_parser() -> argparse.ArgumentParser:
122
124
  fotolab.auto.build_subparser(subparsers)
123
125
  fotolab.border.build_subparser(subparsers)
124
126
  fotolab.contrast.build_subparser(subparsers)
127
+ fotolab.info.build_subparser(subparsers)
125
128
  fotolab.resize.build_subparser(subparsers)
129
+ fotolab.montage.build_subparser(subparsers)
126
130
  fotolab.sharpen.build_subparser(subparsers)
127
131
  fotolab.watermark.build_subparser(subparsers)
128
132
  fotolab.env.build_subparser(subparsers)
@@ -70,4 +70,4 @@ def run(args: argparse.Namespace) -> None:
70
70
  original_image = Image.open(args.image_filename)
71
71
  contrast_image = ImageOps.autocontrast(original_image, cutoff=args.cutoff)
72
72
 
73
- save_image(args, contrast_image)
73
+ save_image(args, contrast_image, "contrast")
@@ -0,0 +1,58 @@
1
+ # Copyright (C) 2024 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
+ """Info subcommand."""
17
+
18
+ import argparse
19
+ import logging
20
+
21
+ from PIL import ExifTags, Image
22
+
23
+ log = logging.getLogger(__name__)
24
+
25
+
26
+ def build_subparser(subparsers) -> None:
27
+ """Build the subparser."""
28
+ info_parser = subparsers.add_parser("info", help="info an image")
29
+
30
+ info_parser.set_defaults(func=run)
31
+
32
+ info_parser.add_argument(
33
+ dest="image_filename",
34
+ help="set the image filename",
35
+ type=str,
36
+ default=None,
37
+ metavar="IMAGE_FILENAME",
38
+ )
39
+
40
+
41
+ def run(args: argparse.Namespace) -> None:
42
+ """Run info subcommand.
43
+
44
+ Args:
45
+ config (argparse.Namespace): Config from command line arguments
46
+
47
+ Returns:
48
+ None
49
+ """
50
+ log.debug(args)
51
+
52
+ image = Image.open(args.image_filename)
53
+ exif = image._getexif()
54
+ info = {ExifTags.TAGS.get(tag_id): exif.get(tag_id) for tag_id in exif}
55
+
56
+ tag_name_width = max(map(len, info))
57
+ for tag_name, tag_value in info.items():
58
+ print(f"{tag_name:<{tag_name_width}}: {tag_value}")
@@ -0,0 +1,70 @@
1
+ # Copyright (C) 2024 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
+ """Montage subcommand."""
17
+
18
+ import argparse
19
+ import logging
20
+
21
+ from PIL import Image
22
+
23
+ from fotolab import save_image
24
+
25
+ log = logging.getLogger(__name__)
26
+
27
+
28
+ def build_subparser(subparsers) -> None:
29
+ """Build the subparser."""
30
+ montage_parser = subparsers.add_parser(
31
+ "montage", help="montage a list of image"
32
+ )
33
+
34
+ montage_parser.set_defaults(func=run)
35
+
36
+ montage_parser.add_argument(
37
+ dest="image_filenames",
38
+ help="set the image filenames",
39
+ nargs="+",
40
+ type=argparse.FileType("r"),
41
+ default=None,
42
+ metavar="IMAGE_FILENAMES",
43
+ )
44
+
45
+
46
+ def run(args: argparse.Namespace) -> None:
47
+ """Run montage subcommand.
48
+
49
+ Args:
50
+ config (argparse.Namespace): Config from command line arguments
51
+
52
+ Returns:
53
+ None
54
+ """
55
+ log.debug(args)
56
+ images = []
57
+ for image_filename in args.image_filenames:
58
+ images.append(Image.open(image_filename.name))
59
+
60
+ total_width = sum(img.width for img in images)
61
+ total_height = max(img.height for img in images)
62
+
63
+ montaged_image = Image.new("RGB", (total_width, total_height))
64
+
65
+ x_offset = 0
66
+ for img in images:
67
+ montaged_image.paste(img, (x_offset, 0))
68
+ x_offset += img.width
69
+
70
+ save_image(args, montaged_image, "montage")
@@ -77,4 +77,4 @@ def run(args: argparse.Namespace) -> None:
77
77
  (args.width, args.height), Image.Resampling.LANCZOS
78
78
  )
79
79
 
80
- save_image(args, resized_image)
80
+ save_image(args, resized_image, "resize")
@@ -93,4 +93,4 @@ def run(args: argparse.Namespace) -> None:
93
93
  args.radius, percent=args.percent, threshold=args.threshold
94
94
  )
95
95
  )
96
- save_image(args, sharpen_image)
96
+ save_image(args, sharpen_image, "sharpen")
@@ -62,6 +62,19 @@ def build_subparser(subparsers) -> None:
62
62
  default="bottom-left",
63
63
  )
64
64
 
65
+ watermark_parser.add_argument(
66
+ "-pd",
67
+ "--padding",
68
+ dest="padding",
69
+ type=int,
70
+ default=15,
71
+ help=(
72
+ "set the padding of the watermark text relative to the image "
73
+ "(default: '%(default)s')"
74
+ ),
75
+ metavar="PADDING",
76
+ )
77
+
65
78
  watermark_parser.add_argument(
66
79
  "-fs",
67
80
  "--font-size",
@@ -133,24 +146,25 @@ def run(args: argparse.Namespace) -> None:
133
146
  text_width = right - left
134
147
  text_height = bottom - top
135
148
  (position_x, position_y) = calculate_position(
136
- watermarked_image, text_width, text_height, args.position
149
+ watermarked_image, text_width, text_height, args.position, args.padding
137
150
  )
138
151
 
139
152
  draw.text(
140
153
  (position_x, position_y),
141
154
  args.text,
142
155
  font=font,
143
- fill=ImageColor.getrgb(args.font_color),
156
+ fill=(*ImageColor.getrgb(args.font_color), 128),
144
157
  stroke_width=args.outline_width,
145
- stroke_fill=ImageColor.getrgb(args.outline_color),
158
+ stroke_fill=(*ImageColor.getrgb(args.outline_color), 128),
146
159
  )
147
160
 
148
- save_image(args, watermarked_image)
161
+ save_image(args, watermarked_image, "watermark")
149
162
 
150
163
 
151
- def calculate_position(image, text_width, text_height, position) -> tuple:
164
+ def calculate_position(
165
+ image, text_width, text_height, position, padding
166
+ ) -> tuple:
152
167
  """Calculate the boundary coordinates of the watermark text."""
153
- padding = 10
154
168
  if position == "top-left":
155
169
  position_x = 0 + padding
156
170
  position_y = 0 + padding
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