fotolab 0.14.0__tar.gz → 0.16.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.14.0 → fotolab-0.16.0}/.pre-commit-config.yaml +1 -1
  2. fotolab-0.16.0/.python-version +5 -0
  3. {fotolab-0.14.0 → fotolab-0.16.0}/CHANGELOG.md +25 -0
  4. {fotolab-0.14.0 → fotolab-0.16.0}/PKG-INFO +23 -8
  5. {fotolab-0.14.0 → fotolab-0.16.0}/Pipfile +1 -0
  6. {fotolab-0.14.0 → fotolab-0.16.0}/Pipfile.lock +109 -84
  7. {fotolab-0.14.0 → fotolab-0.16.0}/README.md +22 -7
  8. {fotolab-0.14.0 → fotolab-0.16.0}/fotolab/__init__.py +3 -3
  9. {fotolab-0.14.0 → fotolab-0.16.0}/fotolab/cli.py +14 -0
  10. {fotolab-0.14.0 → fotolab-0.16.0}/fotolab/resize.py +39 -5
  11. fotolab-0.16.0/fotolab/rotate.py +61 -0
  12. {fotolab-0.14.0 → fotolab-0.16.0}/fotolab/sharpen.py +3 -3
  13. {fotolab-0.14.0 → fotolab-0.16.0}/fotolab/watermark.py +46 -7
  14. fotolab-0.16.0/noxfile.py +24 -0
  15. fotolab-0.14.0/.python-version +0 -5
  16. {fotolab-0.14.0 → fotolab-0.16.0}/.coveragerc +0 -0
  17. {fotolab-0.14.0 → fotolab-0.16.0}/.gitignore +0 -0
  18. {fotolab-0.14.0 → fotolab-0.16.0}/CONTRIBUTING.md +0 -0
  19. {fotolab-0.14.0 → fotolab-0.16.0}/LICENSE.md +0 -0
  20. {fotolab-0.14.0 → fotolab-0.16.0}/docs/Makefile +0 -0
  21. {fotolab-0.14.0 → fotolab-0.16.0}/docs/make.bat +0 -0
  22. {fotolab-0.14.0 → fotolab-0.16.0}/docs/source/CHANGELOG.md +0 -0
  23. {fotolab-0.14.0 → fotolab-0.16.0}/docs/source/CONTRIBUTING.md +0 -0
  24. {fotolab-0.14.0 → fotolab-0.16.0}/docs/source/LICENSE.md +0 -0
  25. {fotolab-0.14.0 → fotolab-0.16.0}/docs/source/README.md +0 -0
  26. {fotolab-0.14.0 → fotolab-0.16.0}/docs/source/_static/logo.jpg +0 -0
  27. {fotolab-0.14.0 → fotolab-0.16.0}/docs/source/conf.py +0 -0
  28. {fotolab-0.14.0 → fotolab-0.16.0}/docs/source/index.rst +0 -0
  29. {fotolab-0.14.0 → fotolab-0.16.0}/fotolab/__main__.py +0 -0
  30. {fotolab-0.14.0 → fotolab-0.16.0}/fotolab/animate.py +0 -0
  31. {fotolab-0.14.0 → fotolab-0.16.0}/fotolab/auto.py +0 -0
  32. {fotolab-0.14.0 → fotolab-0.16.0}/fotolab/border.py +0 -0
  33. {fotolab-0.14.0 → fotolab-0.16.0}/fotolab/contrast.py +0 -0
  34. {fotolab-0.14.0 → fotolab-0.16.0}/fotolab/env.py +0 -0
  35. {fotolab-0.14.0 → fotolab-0.16.0}/fotolab/info.py +0 -0
  36. {fotolab-0.14.0 → fotolab-0.16.0}/fotolab/montage.py +0 -0
  37. {fotolab-0.14.0 → fotolab-0.16.0}/pyproject.toml +0 -0
  38. {fotolab-0.14.0 → fotolab-0.16.0}/tests/__init__.py +0 -0
  39. {fotolab-0.14.0 → fotolab-0.16.0}/tests/conftest.py +0 -0
  40. {fotolab-0.14.0 → fotolab-0.16.0}/tests/test_env.py +0 -0
  41. {fotolab-0.14.0 → fotolab-0.16.0}/tests/test_help_flag.py +0 -0
  42. {fotolab-0.14.0 → fotolab-0.16.0}/tests/test_quiet_flag.py +0 -0
  43. {fotolab-0.14.0 → fotolab-0.16.0}/tox.ini +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.16
18
+ rev: v0.17
19
19
  hooks:
20
20
  - id: validate-pyproject
21
21
  name: validate-pyproject
@@ -0,0 +1,5 @@
1
+ 3.8.19
2
+ 3.9.19
3
+ 3.10.14
4
+ 3.11.9
5
+ 3.12.3
@@ -7,6 +7,31 @@ and this project adheres to [0-based versioning](https://0ver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## v0.16.0 (2024-05-12)
11
+
12
+ ### Added
13
+
14
+ - Add `rotate` subcommand to rotate an image
15
+ - Add initial `lint` session for `nox`
16
+
17
+ ### Changed
18
+
19
+ - Bump deps, `pre-commit` hooks, and Python versions in `pyenv`
20
+
21
+ ## v0.15.0 (2024-05-05)
22
+
23
+ ### Added
24
+
25
+ - Add `-v` or `--verbose` flag to toggle the debug logs of PIL
26
+
27
+ ### Changed
28
+
29
+ - Bump deps and `pre-commit` hooks
30
+ - Resize image to the right aspect ratio based on image's dimension
31
+ - Resize watermark font based on the image's dimension
32
+ - Remove empty lines in help message
33
+ - Show absolute image path in logs
34
+
10
35
  ## v0.14.0 (2024-04-28)
11
36
 
12
37
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fotolab
3
- Version: 0.14.0
3
+ Version: 0.16.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
- usage: fotolab [-h] [-o] [-op] [-od OUTPUT_DIR] [-q] [-d] [-V]
62
- {animate,auto,border,contrast,info,resize,montage,sharpen,watermark,env}
60
+ usage: fotolab [-h] [-o] [-op] [-od OUTPUT_DIR] [-q] [-v] [-d] [-V]
61
+ {animate,auto,border,contrast,info,resize,rotate,montage,sharpen,watermark,env}
63
62
  ...
64
63
 
65
64
  A console program to manipulate photos.
@@ -69,7 +68,7 @@ A console program to manipulate photos.
69
68
  issues: https://github.com/kianmeng/fotolab/issues
70
69
 
71
70
  positional arguments:
72
- {animate,auto,border,contrast,info,resize,montage,sharpen,watermark,env}
71
+ {animate,auto,border,contrast,info,resize,rotate,montage,sharpen,watermark,env}
73
72
  sub-command help
74
73
  animate animate an image
75
74
  auto auto adjust (resize, contrast, and watermark) a photo
@@ -77,6 +76,7 @@ positional arguments:
77
76
  contrast contrast an image
78
77
  info info an image
79
78
  resize resize an image
79
+ rotate rotate an image
80
80
  montage montage a list of image
81
81
  sharpen sharpen an image
82
82
  watermark watermark an image
@@ -89,6 +89,7 @@ optional arguments:
89
89
  -od OUTPUT_DIR, --output-dir OUTPUT_DIR
90
90
  set default output folder (default: 'output')
91
91
  -q, --quiet suppress all logging
92
+ -v, --verbose show verbosity of debugging log, use -vv, -vvv for more details
92
93
  -d, --debug show debugging log and stacktrace
93
94
  -V, --version show program's version number and exit
94
95
  ```
@@ -100,7 +101,6 @@ fotolab animate -h
100
101
  ```
101
102
 
102
103
  ```console
103
-
104
104
  usage: fotolab animate [-h] [-f FORMAT] [-d DURATION] [-l LOOP]
105
105
  IMAGE_FILENAMES [IMAGE_FILENAMES ...]
106
106
 
@@ -197,6 +197,22 @@ optional arguments:
197
197
  -h, --help show this help message and exit
198
198
  ```
199
199
 
200
+ ### fotolab rotate
201
+
202
+ ```console
203
+ fotolab rotate -h
204
+ ```
205
+
206
+ ```console
207
+ usage: fotolab rotate [-h] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
208
+
209
+ positional arguments:
210
+ IMAGE_FILENAMES set the image filenames
211
+
212
+ optional arguments:
213
+ -h, --help show this help message and exit
214
+ ```
215
+
200
216
  ### fotolab montage
201
217
 
202
218
  ```console
@@ -204,7 +220,6 @@ fotolab montage -h
204
220
  ```
205
221
 
206
222
  ```console
207
-
208
223
  usage: fotolab montage [-h] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
209
224
 
210
225
  positional arguments:
@@ -221,7 +236,7 @@ fotolab resize -h
221
236
  ```
222
237
 
223
238
  ```console
224
- usage: fotolab resize [-h] [-wh WIDTH] [-ht HEIGHT]
239
+ usage: fotolab resize [-h] [-wh WIDTH | -ht HEIGHT]
225
240
  IMAGE_FILENAMES [IMAGE_FILENAMES ...]
226
241
 
227
242
  positional arguments:
@@ -23,6 +23,7 @@ scripttest = "*"
23
23
  tomli = "*"
24
24
  tox = "<4"
25
25
  tox-pyenv = "*"
26
+ nox = "*"
26
27
 
27
28
  [requires]
28
29
  python_version = "3.8"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "_meta": {
3
3
  "hash": {
4
- "sha256": "771b8d253ef37cf5b47ebdfb6ec968ac0c2287ac70f9b4590691debfa8c42af0"
4
+ "sha256": "2802f82088933ceafb9dbd46875d8cdfd808d188201db87fb804aeb1746f1095"
5
5
  },
6
6
  "pipfile-spec": 6,
7
7
  "requires": {
@@ -94,6 +94,14 @@
94
94
  }
95
95
  },
96
96
  "develop": {
97
+ "argcomplete": {
98
+ "hashes": [
99
+ "sha256:c168c3723482c031df3c207d4ba8fa702717ccb9fc0bfe4117166c1f537b4a54",
100
+ "sha256:fd03ff4a5b9e6580569d34b273f741e85cd9e072f3feeeee3eba4891c70eda62"
101
+ ],
102
+ "markers": "python_version >= '3.8'",
103
+ "version": "==3.3.0"
104
+ },
97
105
  "astor": {
98
106
  "hashes": [
99
107
  "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5",
@@ -110,66 +118,74 @@
110
118
  "markers": "python_version >= '3.8'",
111
119
  "version": "==3.4.0"
112
120
  },
121
+ "colorlog": {
122
+ "hashes": [
123
+ "sha256:3e3e079a41feb5a1b64f978b5ea4f46040a94f11f0e8bbb8261e3dbbeca64d44",
124
+ "sha256:4dcbb62368e2800cb3c5abd348da7e53f6c362dda502ec27c560b2e58a66bd33"
125
+ ],
126
+ "markers": "python_version >= '3.6'",
127
+ "version": "==6.8.2"
128
+ },
113
129
  "coverage": {
114
130
  "extras": [
115
131
  "toml"
116
132
  ],
117
133
  "hashes": [
118
- "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c",
119
- "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63",
120
- "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7",
121
- "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f",
122
- "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8",
123
- "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf",
124
- "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0",
125
- "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384",
126
- "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76",
127
- "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7",
128
- "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d",
129
- "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70",
130
- "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f",
131
- "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818",
132
- "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b",
133
- "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d",
134
- "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec",
135
- "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083",
136
- "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2",
137
- "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9",
138
- "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd",
139
- "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade",
140
- "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e",
141
- "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a",
142
- "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227",
143
- "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87",
144
- "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c",
145
- "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e",
146
- "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c",
147
- "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e",
148
- "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd",
149
- "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec",
150
- "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562",
151
- "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8",
152
- "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677",
153
- "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357",
154
- "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c",
155
- "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd",
156
- "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49",
157
- "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286",
158
- "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1",
159
- "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf",
160
- "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51",
161
- "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409",
162
- "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384",
163
- "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e",
164
- "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978",
165
- "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57",
166
- "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e",
167
- "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2",
168
- "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48",
169
- "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"
134
+ "sha256:0646599e9b139988b63704d704af8e8df7fa4cbc4a1f33df69d97f36cb0a38de",
135
+ "sha256:0cdcbc320b14c3e5877ee79e649677cb7d89ef588852e9583e6b24c2e5072661",
136
+ "sha256:0d0a0f5e06881ecedfe6f3dd2f56dcb057b6dbeb3327fd32d4b12854df36bf26",
137
+ "sha256:1434e088b41594baa71188a17533083eabf5609e8e72f16ce8c186001e6b8c41",
138
+ "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d",
139
+ "sha256:1cc0fe9b0b3a8364093c53b0b4c0c2dd4bb23acbec4c9240b5f284095ccf7981",
140
+ "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2",
141
+ "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34",
142
+ "sha256:39afcd3d4339329c5f58de48a52f6e4e50f6578dd6099961cf22228feb25f38f",
143
+ "sha256:4a7b0ceee8147444347da6a66be737c9d78f3353b0681715b668b72e79203e4a",
144
+ "sha256:4a9ca3f2fae0088c3c71d743d85404cec8df9be818a005ea065495bedc33da35",
145
+ "sha256:4bf0655ab60d754491004a5efd7f9cccefcc1081a74c9ef2da4735d6ee4a6223",
146
+ "sha256:4cc37def103a2725bc672f84bd939a6fe4522310503207aae4d56351644682f1",
147
+ "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746",
148
+ "sha256:5037f8fcc2a95b1f0e80585bd9d1ec31068a9bcb157d9750a172836e98bc7a90",
149
+ "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c",
150
+ "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca",
151
+ "sha256:57e0204b5b745594e5bc14b9b50006da722827f0b8c776949f1135677e88d0b8",
152
+ "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596",
153
+ "sha256:5c3721c2c9e4c4953a41a26c14f4cef64330392a6d2d675c8b1db3b645e31f0e",
154
+ "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd",
155
+ "sha256:5fd215c0c7d7aab005221608a3c2b46f58c0285a819565887ee0b718c052aa4e",
156
+ "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3",
157
+ "sha256:61c4bf1ba021817de12b813338c9be9f0ad5b1e781b9b340a6d29fc13e7c1b5e",
158
+ "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312",
159
+ "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7",
160
+ "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572",
161
+ "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428",
162
+ "sha256:79afb6197e2f7f60c4824dd4b2d4c2ec5801ceb6ba9ce5d2c3080e5660d51a4f",
163
+ "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07",
164
+ "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e",
165
+ "sha256:8fe7502616b67b234482c3ce276ff26f39ffe88adca2acf0261df4b8454668b4",
166
+ "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136",
167
+ "sha256:9735317685ba6ec7e3754798c8871c2f49aa5e687cc794a0b1d284b2389d1bd5",
168
+ "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8",
169
+ "sha256:9e78295f4144f9dacfed4f92935fbe1780021247c2fabf73a819b17f0ccfff8d",
170
+ "sha256:b016ea6b959d3b9556cb401c55a37547135a587db0115635a443b2ce8f1c7228",
171
+ "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206",
172
+ "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa",
173
+ "sha256:c0884920835a033b78d1c73b6d3bbcda8161a900f38a488829a83982925f6c2e",
174
+ "sha256:c3e757949f268364b96ca894b4c342b41dc6f8f8b66c37878aacef5930db61be",
175
+ "sha256:ca498687ca46a62ae590253fba634a1fe9836bc56f626852fb2720f334c9e4e5",
176
+ "sha256:d1d0d98d95dd18fe29dc66808e1accf59f037d5716f86a501fc0256455219668",
177
+ "sha256:d21918e9ef11edf36764b93101e2ae8cc82aa5efdc7c5a4e9c6c35a48496d601",
178
+ "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057",
179
+ "sha256:db66fc317a046556a96b453a58eced5024af4582a8dbdc0c23ca4dbc0d5b3146",
180
+ "sha256:dde0070c40ea8bb3641e811c1cfbf18e265d024deff6de52c5950677a8fb1e0f",
181
+ "sha256:df4e745a81c110e7446b1cc8131bf986157770fa405fe90e15e850aaf7619bc8",
182
+ "sha256:e2213def81a50519d7cc56ed643c9e93e0247f5bbe0d1247d15fa520814a7cd7",
183
+ "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987",
184
+ "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19",
185
+ "sha256:fc0b4d8bfeabd25ea75e94632f5b6e047eef8adaed0c2161ada1e922e7f7cece"
170
186
  ],
171
187
  "markers": "python_version >= '3.8'",
172
- "version": "==7.4.4"
188
+ "version": "==7.5.1"
173
189
  },
174
190
  "distlib": {
175
191
  "hashes": [
@@ -197,11 +213,11 @@
197
213
  },
198
214
  "filelock": {
199
215
  "hashes": [
200
- "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f",
201
- "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"
216
+ "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f",
217
+ "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"
202
218
  ],
203
219
  "markers": "python_version >= '3.8'",
204
- "version": "==3.13.4"
220
+ "version": "==3.14.0"
205
221
  },
206
222
  "flake8": {
207
223
  "hashes": [
@@ -300,6 +316,15 @@
300
316
  "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'",
301
317
  "version": "==1.8.0"
302
318
  },
319
+ "nox": {
320
+ "hashes": [
321
+ "sha256:6492236efa15a460ecb98e7b67562a28b70da006ab0be164e8821177577c0565",
322
+ "sha256:ecf6700199cdfa9e5ea0a41ff5e6ef4641d09508eda6edb89d9987864115817f"
323
+ ],
324
+ "index": "pypi",
325
+ "markers": "python_version >= '3.7'",
326
+ "version": "==2024.4.15"
327
+ },
303
328
  "packaging": {
304
329
  "hashes": [
305
330
  "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5",
@@ -386,11 +411,11 @@
386
411
  },
387
412
  "platformdirs": {
388
413
  "hashes": [
389
- "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068",
390
- "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"
414
+ "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf",
415
+ "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"
391
416
  ],
392
417
  "markers": "python_version >= '3.8'",
393
- "version": "==4.2.0"
418
+ "version": "==4.2.1"
394
419
  },
395
420
  "pluggy": {
396
421
  "hashes": [
@@ -443,12 +468,12 @@
443
468
  },
444
469
  "pytest": {
445
470
  "hashes": [
446
- "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7",
447
- "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"
471
+ "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233",
472
+ "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f"
448
473
  ],
449
474
  "index": "pypi",
450
475
  "markers": "python_version >= '3.8'",
451
- "version": "==8.1.1"
476
+ "version": "==8.2.0"
452
477
  },
453
478
  "pytest-cov": {
454
479
  "hashes": [
@@ -470,12 +495,12 @@
470
495
  },
471
496
  "pytest-xdist": {
472
497
  "hashes": [
473
- "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a",
474
- "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24"
498
+ "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7",
499
+ "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"
475
500
  ],
476
501
  "index": "pypi",
477
- "markers": "python_version >= '3.7'",
478
- "version": "==3.5.0"
502
+ "markers": "python_version >= '3.8'",
503
+ "version": "==3.6.1"
479
504
  },
480
505
  "pyyaml": {
481
506
  "hashes": [
@@ -592,11 +617,11 @@
592
617
  },
593
618
  "virtualenv": {
594
619
  "hashes": [
595
- "sha256:7bb554bbdfeaacc3349fa614ea5bff6ac300fc7c335e9facf3a3bcfc703f45be",
596
- "sha256:8aac4332f2ea6ef519c648d0bc48a5b1d324994753519919bddbb1aff25a104e"
620
+ "sha256:604bfdceaeece392802e6ae48e69cec49168b9c5f4a44e483963f9242eb0e78b",
621
+ "sha256:7aa9982a728ae5892558bff6a2839c00b9ed145523ece2274fad6f414690ae75"
597
622
  ],
598
623
  "markers": "python_version >= '3.7'",
599
- "version": "==20.25.3"
624
+ "version": "==20.26.1"
600
625
  },
601
626
  "zipp": {
602
627
  "hashes": [
@@ -618,11 +643,11 @@
618
643
  },
619
644
  "babel": {
620
645
  "hashes": [
621
- "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363",
622
- "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"
646
+ "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb",
647
+ "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"
623
648
  ],
624
- "markers": "python_version >= '3.7'",
625
- "version": "==2.14.0"
649
+ "markers": "python_version >= '3.8'",
650
+ "version": "==2.15.0"
626
651
  },
627
652
  "certifi": {
628
653
  "hashes": [
@@ -762,11 +787,11 @@
762
787
  },
763
788
  "jinja2": {
764
789
  "hashes": [
765
- "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa",
766
- "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"
790
+ "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369",
791
+ "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"
767
792
  ],
768
793
  "markers": "python_version >= '3.7'",
769
- "version": "==3.1.3"
794
+ "version": "==3.1.4"
770
795
  },
771
796
  "markdown-it-py": {
772
797
  "hashes": [
@@ -860,12 +885,12 @@
860
885
  },
861
886
  "myst-parser": {
862
887
  "hashes": [
863
- "sha256:0b4ae0b33a45800a748260cb40348c37089a8a456c35120609240bd1b32f9255",
864
- "sha256:8ee926557b8e4c2940a1e62c5720e1667cfaf8480b94b1b9c77dc38e31d104aa"
888
+ "sha256:6457aaa33a5d474aca678b8ead9b3dc298e89c68e67012e73146ea6fd54babf1",
889
+ "sha256:88f0cb406cb363b077d176b51c476f62d60604d68a8dcdf4832e080441301a87"
865
890
  ],
866
891
  "index": "pypi",
867
892
  "markers": "python_version >= '3.8'",
868
- "version": "==3.0.0"
893
+ "version": "==3.0.1"
869
894
  },
870
895
  "packaging": {
871
896
  "hashes": [
@@ -877,11 +902,11 @@
877
902
  },
878
903
  "pygments": {
879
904
  "hashes": [
880
- "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c",
881
- "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"
905
+ "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199",
906
+ "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"
882
907
  ],
883
- "markers": "python_version >= '3.7'",
884
- "version": "==2.17.2"
908
+ "markers": "python_version >= '3.8'",
909
+ "version": "==2.18.0"
885
910
  },
886
911
  "pytz": {
887
912
  "hashes": [
@@ -35,9 +35,8 @@ fotolab -h
35
35
  ```
36
36
 
37
37
  ```console
38
-
39
- usage: fotolab [-h] [-o] [-op] [-od OUTPUT_DIR] [-q] [-d] [-V]
40
- {animate,auto,border,contrast,info,resize,montage,sharpen,watermark,env}
38
+ usage: fotolab [-h] [-o] [-op] [-od OUTPUT_DIR] [-q] [-v] [-d] [-V]
39
+ {animate,auto,border,contrast,info,resize,rotate,montage,sharpen,watermark,env}
41
40
  ...
42
41
 
43
42
  A console program to manipulate photos.
@@ -47,7 +46,7 @@ A console program to manipulate photos.
47
46
  issues: https://github.com/kianmeng/fotolab/issues
48
47
 
49
48
  positional arguments:
50
- {animate,auto,border,contrast,info,resize,montage,sharpen,watermark,env}
49
+ {animate,auto,border,contrast,info,resize,rotate,montage,sharpen,watermark,env}
51
50
  sub-command help
52
51
  animate animate an image
53
52
  auto auto adjust (resize, contrast, and watermark) a photo
@@ -55,6 +54,7 @@ positional arguments:
55
54
  contrast contrast an image
56
55
  info info an image
57
56
  resize resize an image
57
+ rotate rotate an image
58
58
  montage montage a list of image
59
59
  sharpen sharpen an image
60
60
  watermark watermark an image
@@ -67,6 +67,7 @@ optional arguments:
67
67
  -od OUTPUT_DIR, --output-dir OUTPUT_DIR
68
68
  set default output folder (default: 'output')
69
69
  -q, --quiet suppress all logging
70
+ -v, --verbose show verbosity of debugging log, use -vv, -vvv for more details
70
71
  -d, --debug show debugging log and stacktrace
71
72
  -V, --version show program's version number and exit
72
73
  ```
@@ -78,7 +79,6 @@ fotolab animate -h
78
79
  ```
79
80
 
80
81
  ```console
81
-
82
82
  usage: fotolab animate [-h] [-f FORMAT] [-d DURATION] [-l LOOP]
83
83
  IMAGE_FILENAMES [IMAGE_FILENAMES ...]
84
84
 
@@ -175,6 +175,22 @@ optional arguments:
175
175
  -h, --help show this help message and exit
176
176
  ```
177
177
 
178
+ ### fotolab rotate
179
+
180
+ ```console
181
+ fotolab rotate -h
182
+ ```
183
+
184
+ ```console
185
+ usage: fotolab rotate [-h] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
186
+
187
+ positional arguments:
188
+ IMAGE_FILENAMES set the image filenames
189
+
190
+ optional arguments:
191
+ -h, --help show this help message and exit
192
+ ```
193
+
178
194
  ### fotolab montage
179
195
 
180
196
  ```console
@@ -182,7 +198,6 @@ fotolab montage -h
182
198
  ```
183
199
 
184
200
  ```console
185
-
186
201
  usage: fotolab montage [-h] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
187
202
 
188
203
  positional arguments:
@@ -199,7 +214,7 @@ fotolab resize -h
199
214
  ```
200
215
 
201
216
  ```console
202
- usage: fotolab resize [-h] [-wh WIDTH] [-ht HEIGHT]
217
+ usage: fotolab resize [-h] [-wh WIDTH | -ht HEIGHT]
203
218
  IMAGE_FILENAMES [IMAGE_FILENAMES ...]
204
219
 
205
220
  positional arguments:
@@ -21,7 +21,7 @@ import subprocess
21
21
  import sys
22
22
  from pathlib import Path
23
23
 
24
- __version__ = "0.14.0"
24
+ __version__ = "0.16.0"
25
25
 
26
26
  log = logging.getLogger(__name__)
27
27
 
@@ -48,7 +48,7 @@ def save_image(args, new_image, output_filename, subcommand):
48
48
  )
49
49
  new_filename.parent.mkdir(parents=True, exist_ok=True)
50
50
 
51
- log.info("%s image: %s", subcommand, new_filename)
51
+ log.info("%s image: %s", subcommand, new_filename.resolve())
52
52
  new_image.save(new_filename)
53
53
 
54
54
  if args.open:
@@ -64,4 +64,4 @@ def _open_image(filename):
64
64
  elif sys.platform == "windows":
65
65
  os.startfile(filename)
66
66
 
67
- log.info("open image: %s using default program.", filename.resolve())
67
+ log.info("open image: %s", filename.resolve())
@@ -33,6 +33,7 @@ import fotolab.env
33
33
  import fotolab.info
34
34
  import fotolab.montage
35
35
  import fotolab.resize
36
+ import fotolab.rotate
36
37
  import fotolab.sharpen
37
38
  import fotolab.watermark
38
39
  from fotolab import __version__
@@ -42,6 +43,9 @@ log = logging.getLogger(__name__)
42
43
 
43
44
  def setup_logging(args: argparse.Namespace) -> None:
44
45
  """Set up logging by level."""
46
+ if args.verbose == 0:
47
+ logging.getLogger("PIL").setLevel(logging.ERROR)
48
+
45
49
  if args.quiet:
46
50
  logging.disable(logging.NOTSET)
47
51
  else:
@@ -105,6 +109,15 @@ def build_parser() -> argparse.ArgumentParser:
105
109
  help="suppress all logging",
106
110
  )
107
111
 
112
+ parser.add_argument(
113
+ "-v",
114
+ "--verbose",
115
+ default=0,
116
+ action="count",
117
+ dest="verbose",
118
+ help="show verbosity of debugging log, use -vv, -vvv for more details",
119
+ )
120
+
108
121
  parser.add_argument(
109
122
  "-d",
110
123
  "--debug",
@@ -128,6 +141,7 @@ def build_parser() -> argparse.ArgumentParser:
128
141
  fotolab.contrast.build_subparser(subparsers)
129
142
  fotolab.info.build_subparser(subparsers)
130
143
  fotolab.resize.build_subparser(subparsers)
144
+ fotolab.rotate.build_subparser(subparsers)
131
145
  fotolab.montage.build_subparser(subparsers)
132
146
  fotolab.sharpen.build_subparser(subparsers)
133
147
  fotolab.watermark.build_subparser(subparsers)
@@ -17,6 +17,7 @@
17
17
 
18
18
  import argparse
19
19
  import logging
20
+ import math
20
21
 
21
22
  from PIL import Image
22
23
 
@@ -24,6 +25,9 @@ from fotolab import save_image
24
25
 
25
26
  log = logging.getLogger(__name__)
26
27
 
28
+ DEFAULT_WIDTH = 600
29
+ DEFAULT_HEIGHT = 277
30
+
27
31
 
28
32
  def build_subparser(subparsers) -> None:
29
33
  """Build the subparser."""
@@ -40,23 +44,25 @@ def build_subparser(subparsers) -> None:
40
44
  metavar="IMAGE_FILENAMES",
41
45
  )
42
46
 
43
- resize_parser.add_argument(
47
+ group = resize_parser.add_mutually_exclusive_group(required=False)
48
+
49
+ group.add_argument(
44
50
  "-wh",
45
51
  "--width",
46
52
  dest="width",
47
53
  help="set the width of the image (default: '%(default)s')",
48
54
  type=int,
49
- default="600",
55
+ default=DEFAULT_WIDTH,
50
56
  metavar="WIDTH",
51
57
  )
52
58
 
53
- resize_parser.add_argument(
59
+ group.add_argument(
54
60
  "-ht",
55
61
  "--height",
56
62
  dest="height",
57
63
  help="set the height of the image (default: '%(default)s')",
58
64
  type=int,
59
- default="277",
65
+ default=DEFAULT_HEIGHT,
60
66
  metavar="HEIGHT",
61
67
  )
62
68
 
@@ -74,9 +80,37 @@ def run(args: argparse.Namespace) -> None:
74
80
 
75
81
  for image_filename in args.image_filenames:
76
82
  original_image = Image.open(image_filename)
83
+
84
+ new_width, new_height = _calc_new_image_dimension(original_image, args)
77
85
  resized_image = original_image.copy()
78
86
  resized_image = resized_image.resize(
79
- (args.width, args.height), Image.Resampling.LANCZOS
87
+ (new_width, new_height), Image.Resampling.LANCZOS
80
88
  )
81
89
 
82
90
  save_image(args, resized_image, image_filename, "resize")
91
+
92
+
93
+ def _calc_new_image_dimension(image, args) -> tuple:
94
+ new_width = args.width
95
+ new_height = args.height
96
+
97
+ old_width, old_height = image.size
98
+ log.debug("old image dimension: %d x %d", old_width, old_height)
99
+
100
+ if args.width != DEFAULT_WIDTH:
101
+ aspect_ratio = old_height / old_width
102
+ log.debug("aspect ratio: %f", aspect_ratio)
103
+
104
+ new_height = math.ceil(args.width * aspect_ratio)
105
+ log.debug("new height: %d", new_height)
106
+
107
+ if args.height != DEFAULT_HEIGHT:
108
+ aspect_ratio = old_width / old_height
109
+ log.debug("aspect ratio: %f", aspect_ratio)
110
+
111
+ new_width = math.floor(args.height * aspect_ratio)
112
+ log.debug("new width: %d", new_width)
113
+
114
+ log.debug("new image dimension: %d x %d", new_width, new_height)
115
+
116
+ return (new_width, new_height)
@@ -0,0 +1,61 @@
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
+ """Rotate 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
+ rotate_parser = subparsers.add_parser("rotate", help="rotate an image")
31
+
32
+ rotate_parser.set_defaults(func=run)
33
+
34
+ rotate_parser.add_argument(
35
+ dest="image_filenames",
36
+ help="set the image filenames",
37
+ nargs="+",
38
+ type=str,
39
+ default=None,
40
+ metavar="IMAGE_FILENAMES",
41
+ )
42
+
43
+
44
+ def run(args: argparse.Namespace) -> None:
45
+ """Run rotate subcommand.
46
+
47
+ Args:
48
+ config (argparse.Namespace): Config from command line arguments
49
+
50
+ Returns:
51
+ None
52
+ """
53
+ log.debug(args)
54
+
55
+ for image_filename in args.image_filenames:
56
+ original_image = Image.open(image_filename)
57
+ rotated_image = original_image.rotate(
58
+ 180,
59
+ expand=True,
60
+ )
61
+ save_image(args, rotated_image, image_filename, "rotate")
@@ -44,7 +44,7 @@ def build_subparser(subparsers) -> None:
44
44
  "-r",
45
45
  "--radius",
46
46
  dest="radius",
47
- type=str,
47
+ type=int,
48
48
  default=1,
49
49
  help="set the radius or size of edges (default: '%(default)s')",
50
50
  metavar="RADIUS",
@@ -54,7 +54,7 @@ def build_subparser(subparsers) -> None:
54
54
  "-p",
55
55
  "--percent",
56
56
  dest="percent",
57
- type=str,
57
+ type=int,
58
58
  default=100,
59
59
  help=(
60
60
  "set the amount of overall strength of sharpening effect "
@@ -67,7 +67,7 @@ def build_subparser(subparsers) -> None:
67
67
  "-t",
68
68
  "--threshold",
69
69
  dest="threshold",
70
- type=str,
70
+ type=int,
71
71
  default=3,
72
72
  help=(
73
73
  "set the minimum brightness changed to be sharpened "
@@ -17,6 +17,7 @@
17
17
 
18
18
  import argparse
19
19
  import logging
20
+ import math
20
21
 
21
22
  from PIL import Image, ImageColor, ImageDraw, ImageFont
22
23
 
@@ -24,6 +25,9 @@ from fotolab import save_image
24
25
 
25
26
  log = logging.getLogger(__name__)
26
27
 
28
+ FONT_SIZE_ASPECT_RATIO = 12 / 600
29
+ FONT_PADDING_ASPECT_RATIO = 15 / 600
30
+ FONT_OUTLINE_WIDTH_ASPECT_RATIO = 2 / 600
27
31
  POSITIONS = ["top-left", "top-right", "bottom-left", "bottom-right"]
28
32
 
29
33
 
@@ -140,19 +144,21 @@ def run(args: argparse.Namespace) -> None:
140
144
 
141
145
  draw = ImageDraw.Draw(watermarked_image)
142
146
 
143
- font = ImageFont.truetype("arial.ttf", args.font_size)
147
+ font = ImageFont.truetype(
148
+ "arial.ttf", calc_font_size(original_image, args)
149
+ )
144
150
 
145
151
  (left, top, right, bottom) = draw.textbbox(
146
152
  xy=(0, 0), text=args.text, font=font
147
153
  )
148
154
  text_width = right - left
149
155
  text_height = bottom - top
150
- (position_x, position_y) = calculate_position(
156
+ (position_x, position_y) = calc_position(
151
157
  watermarked_image,
152
158
  text_width,
153
159
  text_height,
154
160
  args.position,
155
- args.padding,
161
+ calc_padding(original_image, args),
156
162
  )
157
163
 
158
164
  draw.text(
@@ -160,16 +166,49 @@ def run(args: argparse.Namespace) -> None:
160
166
  args.text,
161
167
  font=font,
162
168
  fill=(*ImageColor.getrgb(args.font_color), 128),
163
- stroke_width=args.outline_width,
169
+ stroke_width=calc_font_outline_width(original_image, args),
164
170
  stroke_fill=(*ImageColor.getrgb(args.outline_color), 128),
165
171
  )
166
172
 
167
173
  save_image(args, watermarked_image, image_filename, "watermark")
168
174
 
169
175
 
170
- def calculate_position(
171
- image, text_width, text_height, position, padding
172
- ) -> tuple:
176
+ def calc_font_size(image, args) -> int:
177
+ """Calculate the font size based on the width of the image."""
178
+ width, _height = image.size
179
+ new_font_size = args.font_size
180
+ if width > 600:
181
+ new_font_size = math.floor(FONT_SIZE_ASPECT_RATIO * width)
182
+
183
+ log.debug("new font size: %d", new_font_size)
184
+ return new_font_size
185
+
186
+
187
+ def calc_font_outline_width(image, args) -> int:
188
+ """Calculate the font padding based on the width of the image."""
189
+ width, _height = image.size
190
+ new_font_outline_width = args.outline_width
191
+ if width > 600:
192
+ new_font_outline_width = math.floor(
193
+ FONT_OUTLINE_WIDTH_ASPECT_RATIO * width
194
+ )
195
+
196
+ log.debug("new font outline width: %d", new_font_outline_width)
197
+ return new_font_outline_width
198
+
199
+
200
+ def calc_padding(image, args) -> int:
201
+ """Calculate the font padding based on the width of the image."""
202
+ width, _height = image.size
203
+ new_padding = args.padding
204
+ if width > 600:
205
+ new_padding = math.floor(FONT_PADDING_ASPECT_RATIO * width)
206
+
207
+ log.debug("new padding: %d", new_padding)
208
+ return new_padding
209
+
210
+
211
+ def calc_position(image, text_width, text_height, position, padding) -> tuple:
173
212
  """Calculate the boundary coordinates of the watermark text."""
174
213
  if position == "top-left":
175
214
  position_x = 0 + padding
@@ -0,0 +1,24 @@
1
+ # Copyright (c) 2022,2023,2024 Kian-Meng Ang
2
+
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Generals Public License for more details.
12
+
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ """Nox configuration."""
17
+ import nox
18
+
19
+
20
+ @nox.session()
21
+ def lint(session: nox.Session) -> None:
22
+ """Runs pre-commit linter."""
23
+ session.install("pre-commit")
24
+ session.run("pre-commit", "run", "--all-files", *session.posargs)
@@ -1,5 +0,0 @@
1
- 3.8.18
2
- 3.9.18
3
- 3.10.13
4
- 3.11.8
5
- 3.12.2
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