fotolab 0.9.0__tar.gz → 0.10.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 (38) hide show
  1. {fotolab-0.9.0 → fotolab-0.10.0}/.pre-commit-config.yaml +1 -1
  2. {fotolab-0.9.0 → fotolab-0.10.0}/CHANGELOG.md +28 -0
  3. {fotolab-0.9.0 → fotolab-0.10.0}/PKG-INFO +20 -4
  4. {fotolab-0.9.0 → fotolab-0.10.0}/Pipfile.lock +122 -98
  5. {fotolab-0.9.0 → fotolab-0.10.0}/README.md +19 -3
  6. {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/__init__.py +31 -1
  7. {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/auto.py +1 -0
  8. {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/border.py +3 -13
  9. {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/cli.py +2 -0
  10. {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/contrast.py +3 -14
  11. {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/env.py +3 -1
  12. fotolab-0.10.0/fotolab/info.py +58 -0
  13. {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/resize.py +4 -14
  14. {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/sharpen.py +3 -14
  15. {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/watermark.py +3 -14
  16. {fotolab-0.9.0 → fotolab-0.10.0}/.coveragerc +0 -0
  17. {fotolab-0.9.0 → fotolab-0.10.0}/.gitignore +0 -0
  18. {fotolab-0.9.0 → fotolab-0.10.0}/.python-version +0 -0
  19. {fotolab-0.9.0 → fotolab-0.10.0}/CONTRIBUTING.md +0 -0
  20. {fotolab-0.9.0 → fotolab-0.10.0}/LICENSE.md +0 -0
  21. {fotolab-0.9.0 → fotolab-0.10.0}/Pipfile +0 -0
  22. {fotolab-0.9.0 → fotolab-0.10.0}/docs/Makefile +0 -0
  23. {fotolab-0.9.0 → fotolab-0.10.0}/docs/make.bat +0 -0
  24. {fotolab-0.9.0 → fotolab-0.10.0}/docs/source/CHANGELOG.md +0 -0
  25. {fotolab-0.9.0 → fotolab-0.10.0}/docs/source/CONTRIBUTING.md +0 -0
  26. {fotolab-0.9.0 → fotolab-0.10.0}/docs/source/LICENSE.md +0 -0
  27. {fotolab-0.9.0 → fotolab-0.10.0}/docs/source/README.md +0 -0
  28. {fotolab-0.9.0 → fotolab-0.10.0}/docs/source/_static/logo.jpg +0 -0
  29. {fotolab-0.9.0 → fotolab-0.10.0}/docs/source/conf.py +0 -0
  30. {fotolab-0.9.0 → fotolab-0.10.0}/docs/source/index.rst +0 -0
  31. {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/__main__.py +0 -0
  32. {fotolab-0.9.0 → fotolab-0.10.0}/pyproject.toml +0 -0
  33. {fotolab-0.9.0 → fotolab-0.10.0}/tests/__init__.py +0 -0
  34. {fotolab-0.9.0 → fotolab-0.10.0}/tests/conftest.py +0 -0
  35. {fotolab-0.9.0 → fotolab-0.10.0}/tests/test_env.py +0 -0
  36. {fotolab-0.9.0 → fotolab-0.10.0}/tests/test_help_flag.py +0 -0
  37. {fotolab-0.9.0 → fotolab-0.10.0}/tests/test_quiet_flag.py +0 -0
  38. {fotolab-0.9.0 → fotolab-0.10.0}/tox.ini +0 -0
@@ -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,34 @@ and this project adheres to [0-based versioning](https://0ver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## v0.10.0 (2024-03-31)
11
+
12
+ ### Added
13
+
14
+ - Add `info` subcommand to show EXIF data from image
15
+
16
+ ### Changed
17
+
18
+ - Bump deps
19
+ - Update help message in README
20
+ - Ignore `pylint` W0212 rule
21
+
22
+ ### Fix
23
+
24
+ - Fix incorrect action in log when running `auto` subcommand
25
+ - Update incorrect comment for `save_image` helper function
26
+
27
+ ## v0.9.1 (2024-03-24)
28
+
29
+ ### Changed
30
+
31
+ - Implement open image using the default program
32
+ - Refactor all subcommand to use `save_image` global helper function
33
+
34
+ ### Fixed
35
+
36
+ - Fix newline in `sys.version` output in Python 3.8
37
+
10
38
  ## v0.9.0 (2024-03-17)
11
39
 
12
40
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fotolab
3
- Version: 0.9.0
3
+ Version: 0.10.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
@@ -135,6 +135,22 @@ optional arguments:
135
135
  -h, --help show this help message and exit
136
136
  ```
137
137
 
138
+ ### fotolab info
139
+
140
+ ```console
141
+ fotolab info -h
142
+ ```
143
+
144
+ ```console
145
+ usage: fotolab info [-h] IMAGE_FILENAME
146
+
147
+ positional arguments:
148
+ IMAGE_FILENAME set the image filename
149
+
150
+ optional arguments:
151
+ -h, --help show this help message and exit
152
+ ```
153
+
138
154
  ### fotolab sharpen
139
155
 
140
156
  ```console
@@ -114,61 +114,61 @@
114
114
  "toml"
115
115
  ],
116
116
  "hashes": [
117
- "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa",
118
- "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003",
119
- "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f",
120
- "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c",
121
- "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e",
122
- "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0",
123
- "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9",
124
- "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52",
125
- "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e",
126
- "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454",
127
- "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0",
128
- "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079",
129
- "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352",
130
- "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f",
131
- "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30",
132
- "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe",
133
- "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113",
134
- "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765",
135
- "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc",
136
- "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e",
137
- "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501",
138
- "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7",
139
- "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2",
140
- "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f",
141
- "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4",
142
- "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524",
143
- "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c",
144
- "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51",
145
- "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840",
146
- "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6",
147
- "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee",
148
- "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e",
149
- "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45",
150
- "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba",
151
- "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d",
152
- "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3",
153
- "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10",
154
- "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e",
155
- "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb",
156
- "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9",
157
- "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a",
158
- "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47",
159
- "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1",
160
- "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3",
161
- "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914",
162
- "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328",
163
- "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6",
164
- "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d",
165
- "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0",
166
- "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94",
167
- "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc",
168
- "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"
117
+ "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c",
118
+ "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63",
119
+ "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7",
120
+ "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f",
121
+ "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8",
122
+ "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf",
123
+ "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0",
124
+ "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384",
125
+ "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76",
126
+ "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7",
127
+ "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d",
128
+ "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70",
129
+ "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f",
130
+ "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818",
131
+ "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b",
132
+ "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d",
133
+ "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec",
134
+ "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083",
135
+ "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2",
136
+ "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9",
137
+ "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd",
138
+ "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade",
139
+ "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e",
140
+ "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a",
141
+ "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227",
142
+ "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87",
143
+ "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c",
144
+ "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e",
145
+ "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c",
146
+ "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e",
147
+ "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd",
148
+ "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec",
149
+ "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562",
150
+ "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8",
151
+ "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677",
152
+ "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357",
153
+ "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c",
154
+ "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd",
155
+ "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49",
156
+ "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286",
157
+ "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1",
158
+ "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf",
159
+ "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51",
160
+ "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409",
161
+ "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384",
162
+ "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e",
163
+ "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978",
164
+ "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57",
165
+ "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e",
166
+ "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2",
167
+ "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48",
168
+ "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"
169
169
  ],
170
170
  "markers": "python_version >= '3.8'",
171
- "version": "==7.4.3"
171
+ "version": "==7.4.4"
172
172
  },
173
173
  "distlib": {
174
174
  "hashes": [
@@ -196,11 +196,11 @@
196
196
  },
197
197
  "filelock": {
198
198
  "hashes": [
199
- "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e",
200
- "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"
199
+ "sha256:5ffa845303983e7a0b7ae17636509bc97997d58afeafa72fb141a17b152284cb",
200
+ "sha256:a79895a25bbefdf55d1a2a0a80968f7dbb28edcd6d4234a0afb3f37ecde4b546"
201
201
  ],
202
202
  "markers": "python_version >= '3.8'",
203
- "version": "==3.13.1"
203
+ "version": "==3.13.3"
204
204
  },
205
205
  "flake8": {
206
206
  "hashes": [
@@ -268,12 +268,12 @@
268
268
  },
269
269
  "importlib-metadata": {
270
270
  "hashes": [
271
- "sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792",
272
- "sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100"
271
+ "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570",
272
+ "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"
273
273
  ],
274
274
  "index": "pypi",
275
275
  "markers": "python_version >= '3.8'",
276
- "version": "==7.0.2"
276
+ "version": "==7.1.0"
277
277
  },
278
278
  "iniconfig": {
279
279
  "hashes": [
@@ -400,12 +400,12 @@
400
400
  },
401
401
  "pre-commit": {
402
402
  "hashes": [
403
- "sha256:ba637c2d7a670c10daedc059f5c49b5bd0aadbccfcd7ec15592cf9665117532c",
404
- "sha256:c3ef34f463045c88658c5b99f38c1e297abdcc0ff13f98d3370055fbbfabc67e"
403
+ "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32",
404
+ "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"
405
405
  ],
406
406
  "index": "pypi",
407
- "markers": "python_version >= '3.9'",
408
- "version": "==3.6.2"
407
+ "markers": "python_version >= '3.8'",
408
+ "version": "==3.5.0"
409
409
  },
410
410
  "py": {
411
411
  "hashes": [
@@ -450,12 +450,12 @@
450
450
  },
451
451
  "pytest-cov": {
452
452
  "hashes": [
453
- "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6",
454
- "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"
453
+ "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652",
454
+ "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"
455
455
  ],
456
456
  "index": "pypi",
457
- "markers": "python_version >= '3.7'",
458
- "version": "==4.1.0"
457
+ "markers": "python_version >= '3.8'",
458
+ "version": "==5.0.0"
459
459
  },
460
460
  "pytest-randomly": {
461
461
  "hashes": [
@@ -598,21 +598,21 @@
598
598
  },
599
599
  "zipp": {
600
600
  "hashes": [
601
- "sha256:c1bb803ed69d2cce2373152797064f7e79bc43f0a3748eb494096a867e0ebf79",
602
- "sha256:df8d042b02765029a09b157efd8e820451045890acc30f8e37dd2f94a060221f"
601
+ "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b",
602
+ "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"
603
603
  ],
604
604
  "markers": "python_version >= '3.8'",
605
- "version": "==3.18.0"
605
+ "version": "==3.18.1"
606
606
  }
607
607
  },
608
608
  "doc": {
609
609
  "alabaster": {
610
610
  "hashes": [
611
- "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65",
612
- "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"
611
+ "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3",
612
+ "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"
613
613
  ],
614
- "markers": "python_version >= '3.9'",
615
- "version": "==0.7.16"
614
+ "markers": "python_version >= '3.6'",
615
+ "version": "==0.7.13"
616
616
  },
617
617
  "babel": {
618
618
  "hashes": [
@@ -750,6 +750,14 @@
750
750
  "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
751
751
  "version": "==1.4.1"
752
752
  },
753
+ "importlib-metadata": {
754
+ "hashes": [
755
+ "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570",
756
+ "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"
757
+ ],
758
+ "markers": "python_version < '3.10'",
759
+ "version": "==7.1.0"
760
+ },
753
761
  "jinja2": {
754
762
  "hashes": [
755
763
  "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa",
@@ -873,6 +881,14 @@
873
881
  "markers": "python_version >= '3.7'",
874
882
  "version": "==2.17.2"
875
883
  },
884
+ "pytz": {
885
+ "hashes": [
886
+ "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812",
887
+ "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"
888
+ ],
889
+ "markers": "python_version < '3.9'",
890
+ "version": "==2024.1"
891
+ },
876
892
  "pyyaml": {
877
893
  "hashes": [
878
894
  "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5",
@@ -947,12 +963,12 @@
947
963
  },
948
964
  "sphinx": {
949
965
  "hashes": [
950
- "sha256:1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560",
951
- "sha256:9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5"
966
+ "sha256:780f4d32f1d7d1126576e0e5ecc19dc32ab76cd24e950228dcf7b1f6d3d9e22f",
967
+ "sha256:d170a81825b2fcacb6dfd5a0d7f578a053e45d3f2b153fecc948c37344eb4cbe"
952
968
  ],
953
969
  "index": "pypi",
954
- "markers": "python_version >= '3.9'",
955
- "version": "==7.2.6"
970
+ "markers": "python_version >= '3.8'",
971
+ "version": "==7.1.2"
956
972
  },
957
973
  "sphinx-autodoc-typehints": {
958
974
  "hashes": [
@@ -974,27 +990,27 @@
974
990
  },
975
991
  "sphinxcontrib-applehelp": {
976
992
  "hashes": [
977
- "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619",
978
- "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"
993
+ "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228",
994
+ "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"
979
995
  ],
980
- "markers": "python_version >= '3.9'",
981
- "version": "==1.0.8"
996
+ "markers": "python_version >= '3.8'",
997
+ "version": "==1.0.4"
982
998
  },
983
999
  "sphinxcontrib-devhelp": {
984
1000
  "hashes": [
985
- "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f",
986
- "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"
1001
+ "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e",
1002
+ "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"
987
1003
  ],
988
- "markers": "python_version >= '3.9'",
989
- "version": "==1.0.6"
1004
+ "markers": "python_version >= '3.5'",
1005
+ "version": "==1.0.2"
990
1006
  },
991
1007
  "sphinxcontrib-htmlhelp": {
992
1008
  "hashes": [
993
- "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015",
994
- "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"
1009
+ "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff",
1010
+ "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"
995
1011
  ],
996
- "markers": "python_version >= '3.9'",
997
- "version": "==2.0.5"
1012
+ "markers": "python_version >= '3.8'",
1013
+ "version": "==2.0.1"
998
1014
  },
999
1015
  "sphinxcontrib-jsmath": {
1000
1016
  "hashes": [
@@ -1006,19 +1022,19 @@
1006
1022
  },
1007
1023
  "sphinxcontrib-qthelp": {
1008
1024
  "hashes": [
1009
- "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6",
1010
- "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"
1025
+ "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72",
1026
+ "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"
1011
1027
  ],
1012
- "markers": "python_version >= '3.9'",
1013
- "version": "==1.0.7"
1028
+ "markers": "python_version >= '3.5'",
1029
+ "version": "==1.0.3"
1014
1030
  },
1015
1031
  "sphinxcontrib-serializinghtml": {
1016
1032
  "hashes": [
1017
- "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7",
1018
- "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"
1033
+ "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd",
1034
+ "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"
1019
1035
  ],
1020
- "markers": "python_version >= '3.9'",
1021
- "version": "==1.1.10"
1036
+ "markers": "python_version >= '3.5'",
1037
+ "version": "==1.1.5"
1022
1038
  },
1023
1039
  "urllib3": {
1024
1040
  "hashes": [
@@ -1027,6 +1043,14 @@
1027
1043
  ],
1028
1044
  "markers": "python_version >= '3.8'",
1029
1045
  "version": "==2.2.1"
1046
+ },
1047
+ "zipp": {
1048
+ "hashes": [
1049
+ "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b",
1050
+ "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"
1051
+ ],
1052
+ "markers": "python_version >= '3.8'",
1053
+ "version": "==3.18.1"
1030
1054
  }
1031
1055
  }
1032
1056
  }
@@ -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
@@ -113,6 +113,22 @@ optional arguments:
113
113
  -h, --help show this help message and exit
114
114
  ```
115
115
 
116
+ ### fotolab info
117
+
118
+ ```console
119
+ fotolab info -h
120
+ ```
121
+
122
+ ```console
123
+ usage: fotolab info [-h] IMAGE_FILENAME
124
+
125
+ positional arguments:
126
+ IMAGE_FILENAME set the image filename
127
+
128
+ optional arguments:
129
+ -h, --help show this help message and exit
130
+ ```
131
+
116
132
  ### fotolab sharpen
117
133
 
118
134
  ```console
@@ -19,12 +19,42 @@ import logging
19
19
  import os
20
20
  import subprocess
21
21
  import sys
22
+ from pathlib import Path
22
23
 
23
- __version__ = "0.9.0"
24
+ __version__ = "0.10.0"
24
25
 
25
26
  log = logging.getLogger(__name__)
26
27
 
27
28
 
29
+ def save_image(args, new_image, subcommand):
30
+ """Save image after image operation.
31
+
32
+ Args:
33
+ args (argparse.Namespace): Config from command line arguments
34
+ new_image(PIL.Image.Image): Modified image
35
+ subcommand(str): Subcommand used to call this function
36
+
37
+ Returns:
38
+ None
39
+ """
40
+ image_file = Path(args.image_filename)
41
+
42
+ if args.overwrite:
43
+ new_filename = image_file.with_name(image_file.name)
44
+ else:
45
+ new_filename = Path(
46
+ args.output_dir,
47
+ image_file.with_name(f"{subcommand}_{image_file.name}"),
48
+ )
49
+ new_filename.parent.mkdir(parents=True, exist_ok=True)
50
+
51
+ log.info("%s image: %s", subcommand, new_filename)
52
+ new_image.save(new_filename)
53
+
54
+ if args.open:
55
+ _open_image(new_filename)
56
+
57
+
28
58
  def _open_image(filename):
29
59
  """Open generated image using default program."""
30
60
  if sys.platform == "linux":
@@ -68,6 +68,7 @@ def run(args: argparse.Namespace) -> None:
68
68
  }
69
69
  combined_args = argparse.Namespace(**vars(args), **extra_args)
70
70
  combined_args.overwrite = True
71
+ combined_args.open = False
71
72
  log.debug(args)
72
73
  log.debug(combined_args)
73
74
 
@@ -17,10 +17,11 @@
17
17
 
18
18
  import argparse
19
19
  import logging
20
- from pathlib import Path
21
20
 
22
21
  from PIL import Image, ImageColor, ImageOps
23
22
 
23
+ from fotolab import save_image
24
+
24
25
  log = logging.getLogger(__name__)
25
26
 
26
27
 
@@ -110,8 +111,6 @@ def run(args: argparse.Namespace) -> None:
110
111
  """
111
112
  log.debug(args)
112
113
 
113
- image_file = Path(args.image_filename)
114
-
115
114
  original_image = Image.open(args.image_filename)
116
115
 
117
116
  if (
@@ -135,13 +134,4 @@ def run(args: argparse.Namespace) -> None:
135
134
  fill=ImageColor.getrgb(args.color),
136
135
  )
137
136
 
138
- if args.overwrite:
139
- new_filename = image_file.with_name(image_file.name)
140
- else:
141
- new_filename = Path(
142
- args.output_dir, image_file.with_name(f"border_{image_file.name}")
143
- )
144
- new_filename.parent.mkdir(parents=True, exist_ok=True)
145
-
146
- log.info("creating image: %s", new_filename)
147
- bordered_image.save(new_filename)
137
+ save_image(args, bordered_image, "border")
@@ -29,6 +29,7 @@ import fotolab.auto
29
29
  import fotolab.border
30
30
  import fotolab.contrast
31
31
  import fotolab.env
32
+ import fotolab.info
32
33
  import fotolab.resize
33
34
  import fotolab.sharpen
34
35
  import fotolab.watermark
@@ -122,6 +123,7 @@ def build_parser() -> argparse.ArgumentParser:
122
123
  fotolab.auto.build_subparser(subparsers)
123
124
  fotolab.border.build_subparser(subparsers)
124
125
  fotolab.contrast.build_subparser(subparsers)
126
+ fotolab.info.build_subparser(subparsers)
125
127
  fotolab.resize.build_subparser(subparsers)
126
128
  fotolab.sharpen.build_subparser(subparsers)
127
129
  fotolab.watermark.build_subparser(subparsers)
@@ -17,10 +17,11 @@
17
17
 
18
18
  import argparse
19
19
  import logging
20
- from pathlib import Path
21
20
 
22
21
  from PIL import Image, ImageOps
23
22
 
23
+ from fotolab import save_image
24
+
24
25
  log = logging.getLogger(__name__)
25
26
 
26
27
 
@@ -66,19 +67,7 @@ def run(args: argparse.Namespace) -> None:
66
67
  """
67
68
  log.debug(args)
68
69
 
69
- image_file = Path(args.image_filename)
70
-
71
70
  original_image = Image.open(args.image_filename)
72
71
  contrast_image = ImageOps.autocontrast(original_image, cutoff=args.cutoff)
73
72
 
74
- if args.overwrite:
75
- new_filename = image_file.with_name(image_file.name)
76
- else:
77
- new_filename = Path(
78
- args.output_dir,
79
- image_file.with_name(f"contrast_{image_file.name}"),
80
- )
81
- new_filename.parent.mkdir(parents=True, exist_ok=True)
82
-
83
- log.info("contrasting image: %s", new_filename)
84
- contrast_image.save(new_filename)
73
+ save_image(args, contrast_image, "contrast")
@@ -45,6 +45,8 @@ def run(args: argparse.Namespace) -> None:
45
45
  """
46
46
  log.debug(args)
47
47
 
48
+ sys_version = sys.version.replace("\n", "")
49
+
48
50
  print(f"fotolab: {__version__}")
49
- print(f"python: {sys.version}")
51
+ print(f"python: {sys_version}")
50
52
  print(f"platform: {platform.platform()}")
@@ -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}")
@@ -17,10 +17,11 @@
17
17
 
18
18
  import argparse
19
19
  import logging
20
- from pathlib import Path
21
20
 
22
21
  from PIL import Image
23
22
 
23
+ from fotolab import save_image
24
+
24
25
  log = logging.getLogger(__name__)
25
26
 
26
27
 
@@ -63,28 +64,17 @@ def run(args: argparse.Namespace) -> None:
63
64
  """Run resize subcommand.
64
65
 
65
66
  Args:
66
- config (argparse.Namespace): Config from command line arguments
67
+ args (argparse.Namespace): Config from command line arguments
67
68
 
68
69
  Returns:
69
70
  None
70
71
  """
71
72
  log.debug(args)
72
73
 
73
- image_file = Path(args.image_filename)
74
74
  original_image = Image.open(args.image_filename)
75
75
  resized_image = original_image.copy()
76
76
  resized_image = resized_image.resize(
77
77
  (args.width, args.height), Image.Resampling.LANCZOS
78
78
  )
79
79
 
80
- if args.overwrite:
81
- new_filename = image_file.with_name(image_file.name)
82
- else:
83
- new_filename = Path(
84
- args.output_dir,
85
- image_file.with_name(f"resize_{image_file.name}"),
86
- )
87
- new_filename.parent.mkdir(parents=True, exist_ok=True)
88
-
89
- log.info("resizing image: %s", new_filename)
90
- resized_image.save(new_filename)
80
+ save_image(args, resized_image, "resize")
@@ -17,10 +17,11 @@
17
17
 
18
18
  import argparse
19
19
  import logging
20
- from pathlib import Path
21
20
 
22
21
  from PIL import Image, ImageFilter
23
22
 
23
+ from fotolab import save_image
24
+
24
25
  log = logging.getLogger(__name__)
25
26
 
26
27
 
@@ -86,22 +87,10 @@ def run(args: argparse.Namespace) -> None:
86
87
  """
87
88
  log.debug(args)
88
89
 
89
- image_file = Path(args.image_filename)
90
-
91
90
  original_image = Image.open(args.image_filename)
92
91
  sharpen_image = original_image.filter(
93
92
  ImageFilter.UnsharpMask(
94
93
  args.radius, percent=args.percent, threshold=args.threshold
95
94
  )
96
95
  )
97
-
98
- if args.overwrite:
99
- new_filename = image_file.with_name(image_file.name)
100
- else:
101
- new_filename = Path(
102
- args.output_dir, image_file.with_name(f"sharpen_{image_file.name}")
103
- )
104
- new_filename.parent.mkdir(parents=True, exist_ok=True)
105
-
106
- log.info("sharpening image: %s", new_filename)
107
- sharpen_image.save(new_filename)
96
+ save_image(args, sharpen_image, "sharpen")
@@ -17,10 +17,11 @@
17
17
 
18
18
  import argparse
19
19
  import logging
20
- from pathlib import Path
21
20
 
22
21
  from PIL import Image, ImageColor, ImageDraw, ImageFont
23
22
 
23
+ from fotolab import save_image
24
+
24
25
  log = logging.getLogger(__name__)
25
26
 
26
27
  POSITIONS = ["top-left", "top-right", "bottom-left", "bottom-right"]
@@ -119,8 +120,6 @@ def run(args: argparse.Namespace) -> None:
119
120
  """
120
121
  log.debug(args)
121
122
 
122
- image_file = Path(args.image_filename)
123
-
124
123
  original_image = Image.open(args.image_filename)
125
124
  watermarked_image = original_image.copy()
126
125
 
@@ -146,17 +145,7 @@ def run(args: argparse.Namespace) -> None:
146
145
  stroke_fill=ImageColor.getrgb(args.outline_color),
147
146
  )
148
147
 
149
- if args.overwrite:
150
- new_filename = image_file.with_name(image_file.name)
151
- else:
152
- new_filename = Path(
153
- args.output_dir,
154
- image_file.with_name(f"watermark_{image_file.name}"),
155
- )
156
- new_filename.parent.mkdir(parents=True, exist_ok=True)
157
-
158
- log.info("watermarking image: %s", new_filename)
159
- watermarked_image.save(new_filename)
148
+ save_image(args, watermarked_image, "watermark")
160
149
 
161
150
 
162
151
  def calculate_position(image, text_width, text_height, position) -> tuple:
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