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.
- {fotolab-0.9.0 → fotolab-0.10.0}/.pre-commit-config.yaml +1 -1
- {fotolab-0.9.0 → fotolab-0.10.0}/CHANGELOG.md +28 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/PKG-INFO +20 -4
- {fotolab-0.9.0 → fotolab-0.10.0}/Pipfile.lock +122 -98
- {fotolab-0.9.0 → fotolab-0.10.0}/README.md +19 -3
- {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/__init__.py +31 -1
- {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/auto.py +1 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/border.py +3 -13
- {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/cli.py +2 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/contrast.py +3 -14
- {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/env.py +3 -1
- fotolab-0.10.0/fotolab/info.py +58 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/resize.py +4 -14
- {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/sharpen.py +3 -14
- {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/watermark.py +3 -14
- {fotolab-0.9.0 → fotolab-0.10.0}/.coveragerc +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/.gitignore +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/.python-version +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/CONTRIBUTING.md +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/LICENSE.md +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/Pipfile +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/docs/Makefile +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/docs/make.bat +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/docs/source/CHANGELOG.md +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/docs/source/CONTRIBUTING.md +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/docs/source/LICENSE.md +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/docs/source/README.md +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/docs/source/_static/logo.jpg +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/docs/source/conf.py +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/docs/source/index.rst +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/fotolab/__main__.py +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/pyproject.toml +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/tests/__init__.py +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/tests/conftest.py +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/tests/test_env.py +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/tests/test_help_flag.py +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/tests/test_quiet_flag.py +0 -0
- {fotolab-0.9.0 → fotolab-0.10.0}/tox.ini +0 -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.
|
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:
|
118
|
-
"sha256:
|
119
|
-
"sha256:
|
120
|
-
"sha256:
|
121
|
-
"sha256:
|
122
|
-
"sha256:
|
123
|
-
"sha256:
|
124
|
-
"sha256:
|
125
|
-
"sha256:
|
126
|
-
"sha256:
|
127
|
-
"sha256:
|
128
|
-
"sha256:
|
129
|
-
"sha256:
|
130
|
-
"sha256:
|
131
|
-
"sha256:
|
132
|
-
"sha256:
|
133
|
-
"sha256:
|
134
|
-
"sha256:
|
135
|
-
"sha256:
|
136
|
-
"sha256:
|
137
|
-
"sha256:
|
138
|
-
"sha256:
|
139
|
-
"sha256:
|
140
|
-
"sha256:
|
141
|
-
"sha256:
|
142
|
-
"sha256:
|
143
|
-
"sha256:
|
144
|
-
"sha256:
|
145
|
-
"sha256:
|
146
|
-
"sha256:
|
147
|
-
"sha256:
|
148
|
-
"sha256:
|
149
|
-
"sha256:
|
150
|
-
"sha256:
|
151
|
-
"sha256:
|
152
|
-
"sha256:
|
153
|
-
"sha256:
|
154
|
-
"sha256:
|
155
|
-
"sha256:
|
156
|
-
"sha256:
|
157
|
-
"sha256:
|
158
|
-
"sha256:
|
159
|
-
"sha256:
|
160
|
-
"sha256:
|
161
|
-
"sha256:
|
162
|
-
"sha256:
|
163
|
-
"sha256:
|
164
|
-
"sha256:
|
165
|
-
"sha256:
|
166
|
-
"sha256:
|
167
|
-
"sha256:
|
168
|
-
"sha256:
|
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.
|
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:
|
200
|
-
"sha256:
|
199
|
+
"sha256:5ffa845303983e7a0b7ae17636509bc97997d58afeafa72fb141a17b152284cb",
|
200
|
+
"sha256:a79895a25bbefdf55d1a2a0a80968f7dbb28edcd6d4234a0afb3f37ecde4b546"
|
201
201
|
],
|
202
202
|
"markers": "python_version >= '3.8'",
|
203
|
-
"version": "==3.13.
|
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:
|
272
|
-
"sha256:
|
271
|
+
"sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570",
|
272
|
+
"sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"
|
273
273
|
],
|
274
274
|
"index": "pypi",
|
275
275
|
"markers": "python_version >= '3.8'",
|
276
|
-
"version": "==7.0
|
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:
|
404
|
-
"sha256:
|
403
|
+
"sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32",
|
404
|
+
"sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"
|
405
405
|
],
|
406
406
|
"index": "pypi",
|
407
|
-
"markers": "python_version >= '3.
|
408
|
-
"version": "==3.
|
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:
|
454
|
-
"sha256:
|
453
|
+
"sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652",
|
454
|
+
"sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"
|
455
455
|
],
|
456
456
|
"index": "pypi",
|
457
|
-
"markers": "python_version >= '3.
|
458
|
-
"version": "==
|
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:
|
602
|
-
"sha256:
|
601
|
+
"sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b",
|
602
|
+
"sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"
|
603
603
|
],
|
604
604
|
"markers": "python_version >= '3.8'",
|
605
|
-
"version": "==3.18.
|
605
|
+
"version": "==3.18.1"
|
606
606
|
}
|
607
607
|
},
|
608
608
|
"doc": {
|
609
609
|
"alabaster": {
|
610
610
|
"hashes": [
|
611
|
-
"sha256:
|
612
|
-
"sha256:
|
611
|
+
"sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3",
|
612
|
+
"sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"
|
613
613
|
],
|
614
|
-
"markers": "python_version >= '3.
|
615
|
-
"version": "==0.7.
|
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:
|
951
|
-
"sha256:
|
966
|
+
"sha256:780f4d32f1d7d1126576e0e5ecc19dc32ab76cd24e950228dcf7b1f6d3d9e22f",
|
967
|
+
"sha256:d170a81825b2fcacb6dfd5a0d7f578a053e45d3f2b153fecc948c37344eb4cbe"
|
952
968
|
],
|
953
969
|
"index": "pypi",
|
954
|
-
"markers": "python_version >= '3.
|
955
|
-
"version": "==7.2
|
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:
|
978
|
-
"sha256:
|
993
|
+
"sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228",
|
994
|
+
"sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"
|
979
995
|
],
|
980
|
-
"markers": "python_version >= '3.
|
981
|
-
"version": "==1.0.
|
996
|
+
"markers": "python_version >= '3.8'",
|
997
|
+
"version": "==1.0.4"
|
982
998
|
},
|
983
999
|
"sphinxcontrib-devhelp": {
|
984
1000
|
"hashes": [
|
985
|
-
"sha256:
|
986
|
-
"sha256:
|
1001
|
+
"sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e",
|
1002
|
+
"sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"
|
987
1003
|
],
|
988
|
-
"markers": "python_version >= '3.
|
989
|
-
"version": "==1.0.
|
1004
|
+
"markers": "python_version >= '3.5'",
|
1005
|
+
"version": "==1.0.2"
|
990
1006
|
},
|
991
1007
|
"sphinxcontrib-htmlhelp": {
|
992
1008
|
"hashes": [
|
993
|
-
"sha256:
|
994
|
-
"sha256:
|
1009
|
+
"sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff",
|
1010
|
+
"sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"
|
995
1011
|
],
|
996
|
-
"markers": "python_version >= '3.
|
997
|
-
"version": "==2.0.
|
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:
|
1010
|
-
"sha256:
|
1025
|
+
"sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72",
|
1026
|
+
"sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"
|
1011
1027
|
],
|
1012
|
-
"markers": "python_version >= '3.
|
1013
|
-
"version": "==1.0.
|
1028
|
+
"markers": "python_version >= '3.5'",
|
1029
|
+
"version": "==1.0.3"
|
1014
1030
|
},
|
1015
1031
|
"sphinxcontrib-serializinghtml": {
|
1016
1032
|
"hashes": [
|
1017
|
-
"sha256:
|
1018
|
-
"sha256:
|
1033
|
+
"sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd",
|
1034
|
+
"sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"
|
1019
1035
|
],
|
1020
|
-
"markers": "python_version >= '3.
|
1021
|
-
"version": "==1.1.
|
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.
|
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":
|
@@ -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
|
-
|
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
|
-
|
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: {
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|