fotolab 0.28.5__tar.gz → 0.28.6__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.28.5 → fotolab-0.28.6}/CHANGELOG.md +7 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/PKG-INFO +1 -1
- {fotolab-0.28.5 → fotolab-0.28.6}/Pipfile.lock +177 -154
- {fotolab-0.28.5 → fotolab-0.28.6}/fotolab/__init__.py +5 -5
- {fotolab-0.28.5 → fotolab-0.28.6}/fotolab/cli.py +12 -12
- {fotolab-0.28.5 → fotolab-0.28.6}/fotolab/subcommands/watermark.py +44 -29
- {fotolab-0.28.5 → fotolab-0.28.6}/.coveragerc +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/.gitignore +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/.pre-commit-config.yaml +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/.python-version +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/CONTRIBUTING.md +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/LICENSE.md +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/Pipfile +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/README.md +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/docs/Makefile +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/docs/make.bat +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/docs/source/CHANGELOG.md +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/docs/source/CONTRIBUTING.md +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/docs/source/LICENSE.md +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/docs/source/README.md +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/docs/source/_static/logo.jpg +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/docs/source/conf.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/docs/source/index.rst +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/fotolab/__main__.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/fotolab/subcommands/__init__.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/fotolab/subcommands/animate.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/fotolab/subcommands/auto.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/fotolab/subcommands/border.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/fotolab/subcommands/contrast.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/fotolab/subcommands/env.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/fotolab/subcommands/halftone.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/fotolab/subcommands/info.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/fotolab/subcommands/montage.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/fotolab/subcommands/resize.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/fotolab/subcommands/rotate.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/fotolab/subcommands/sharpen.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/generate +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/noxfile.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/pyproject.toml +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/tests/__init__.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/tests/conftest.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/tests/test_env_subcommand.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/tests/test_help_flag.py +0 -0
- {fotolab-0.28.5 → fotolab-0.28.6}/tests/test_quiet_flag.py +0 -0
@@ -7,6 +7,13 @@ and this project adheres to [0-based versioning](https://0ver.org/).
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
## v0.28.6 (2025-04-20)
|
11
|
+
|
12
|
+
- Add missing typehints for `watermark` subcommand
|
13
|
+
- Bump deps
|
14
|
+
- Fix incorrect license header
|
15
|
+
- Fix open image on Windows using `os.startfile` instead of `open` command
|
16
|
+
|
10
17
|
## v0.28.5 (2025-04-13)
|
11
18
|
|
12
19
|
- Group build and publish package commands together
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"_meta": {
|
3
3
|
"hash": {
|
4
|
-
"sha256": "
|
4
|
+
"sha256": "0ae04620e58fea48a693d99901d3c9f3a1992033a0a3b8eac3306e6ca34f65bb"
|
5
5
|
},
|
6
6
|
"pipfile-spec": 6,
|
7
7
|
"requires": {
|
@@ -16,83 +16,97 @@
|
|
16
16
|
]
|
17
17
|
},
|
18
18
|
"default": {
|
19
|
+
"fotolab": {
|
20
|
+
"file": ".",
|
21
|
+
"markers": "python_version >= '3.9'"
|
22
|
+
},
|
19
23
|
"pillow": {
|
20
24
|
"hashes": [
|
21
|
-
"sha256:
|
22
|
-
"sha256:
|
23
|
-
"sha256:
|
24
|
-
"sha256:
|
25
|
-
"sha256:
|
26
|
-
"sha256:
|
27
|
-
"sha256:
|
28
|
-
"sha256:
|
29
|
-
"sha256:
|
30
|
-
"sha256:
|
31
|
-
"sha256:
|
32
|
-
"sha256:
|
33
|
-
"sha256:
|
34
|
-
"sha256:
|
35
|
-
"sha256:
|
36
|
-
"sha256:
|
37
|
-
"sha256:
|
38
|
-
"sha256:
|
39
|
-
"sha256:
|
40
|
-
"sha256:
|
41
|
-
"sha256:
|
42
|
-
"sha256:
|
43
|
-
"sha256:
|
44
|
-
"sha256:
|
45
|
-
"sha256:
|
46
|
-
"sha256:
|
47
|
-
"sha256:
|
48
|
-
"sha256:
|
49
|
-
"sha256:
|
50
|
-
"sha256:
|
51
|
-
"sha256:
|
52
|
-
"sha256:
|
53
|
-
"sha256:
|
54
|
-
"sha256:
|
55
|
-
"sha256:
|
56
|
-
"sha256:
|
57
|
-
"sha256:
|
58
|
-
"sha256:
|
59
|
-
"sha256:
|
60
|
-
"sha256:
|
61
|
-
"sha256:
|
62
|
-
"sha256:
|
63
|
-
"sha256:
|
64
|
-
"sha256:
|
65
|
-
"sha256:
|
66
|
-
"sha256:
|
67
|
-
"sha256:
|
68
|
-
"sha256:
|
69
|
-
"sha256:
|
70
|
-
"sha256:
|
71
|
-
"sha256:
|
72
|
-
"sha256:
|
73
|
-
"sha256:
|
74
|
-
"sha256:
|
75
|
-
"sha256:
|
76
|
-
"sha256:
|
77
|
-
"sha256:
|
78
|
-
"sha256:
|
79
|
-
"sha256:
|
80
|
-
"sha256:
|
81
|
-
"sha256:
|
82
|
-
"sha256:
|
83
|
-
"sha256:
|
84
|
-
"sha256:
|
85
|
-
"sha256:
|
86
|
-
"sha256:
|
87
|
-
"sha256:
|
88
|
-
"sha256:
|
89
|
-
"sha256:
|
90
|
-
"sha256:
|
91
|
-
"sha256:
|
25
|
+
"sha256:014ca0050c85003620526b0ac1ac53f56fc93af128f7546623cc8e31875ab928",
|
26
|
+
"sha256:036e53f4170e270ddb8797d4c590e6dd14d28e15c7da375c18978045f7e6c37b",
|
27
|
+
"sha256:062b7a42d672c45a70fa1f8b43d1d38ff76b63421cbbe7f88146b39e8a558d91",
|
28
|
+
"sha256:0c3e6d0f59171dfa2e25d7116217543310908dfa2770aa64b8f87605f8cacc97",
|
29
|
+
"sha256:0c7b29dbd4281923a2bfe562acb734cee96bbb129e96e6972d315ed9f232bef4",
|
30
|
+
"sha256:0f5c7eda47bf8e3c8a283762cab94e496ba977a420868cb819159980b6709193",
|
31
|
+
"sha256:127bf6ac4a5b58b3d32fc8289656f77f80567d65660bc46f72c0d77e6600cc95",
|
32
|
+
"sha256:14e33b28bf17c7a38eede290f77db7c664e4eb01f7869e37fa98a5aa95978941",
|
33
|
+
"sha256:14f73f7c291279bd65fda51ee87affd7c1e097709f7fdd0188957a16c264601f",
|
34
|
+
"sha256:191955c55d8a712fab8934a42bfefbf99dd0b5875078240943f913bb66d46d9f",
|
35
|
+
"sha256:1d535df14716e7f8776b9e7fee118576d65572b4aad3ed639be9e4fa88a1cad3",
|
36
|
+
"sha256:208653868d5c9ecc2b327f9b9ef34e0e42a4cdd172c2988fd81d62d2bc9bc044",
|
37
|
+
"sha256:21e1470ac9e5739ff880c211fc3af01e3ae505859392bf65458c224d0bf283eb",
|
38
|
+
"sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681",
|
39
|
+
"sha256:25a5f306095c6780c52e6bbb6109624b95c5b18e40aab1c3041da3e9e0cd3e2d",
|
40
|
+
"sha256:2728567e249cdd939f6cc3d1f049595c66e4187f3c34078cbc0a7d21c47482d2",
|
41
|
+
"sha256:2b490402c96f907a166615e9a5afacf2519e28295f157ec3a2bb9bd57de638cb",
|
42
|
+
"sha256:312c77b7f07ab2139924d2639860e084ec2a13e72af54d4f08ac843a5fc9c79d",
|
43
|
+
"sha256:31df6e2d3d8fc99f993fd253e97fae451a8db2e7207acf97859732273e108406",
|
44
|
+
"sha256:35ca289f712ccfc699508c4658a1d14652e8033e9b69839edf83cbdd0ba39e70",
|
45
|
+
"sha256:3692b68c87096ac6308296d96354eddd25f98740c9d2ab54e1549d6c8aea9d79",
|
46
|
+
"sha256:36d6b82164c39ce5482f649b437382c0fb2395eabc1e2b1702a6deb8ad647d6e",
|
47
|
+
"sha256:39ad2e0f424394e3aebc40168845fee52df1394a4673a6ee512d840d14ab3013",
|
48
|
+
"sha256:3e645b020f3209a0181a418bffe7b4a93171eef6c4ef6cc20980b30bebf17b7d",
|
49
|
+
"sha256:3fe735ced9a607fee4f481423a9c36701a39719252a9bb251679635f99d0f7d2",
|
50
|
+
"sha256:4b835d89c08a6c2ee7781b8dd0a30209a8012b5f09c0a665b65b0eb3560b6f36",
|
51
|
+
"sha256:4d375eb838755f2528ac8cbc926c3e31cc49ca4ad0cf79cff48b20e30634a4a7",
|
52
|
+
"sha256:4eb92eca2711ef8be42fd3f67533765d9fd043b8c80db204f16c8ea62ee1a751",
|
53
|
+
"sha256:5119225c622403afb4b44bad4c1ca6c1f98eed79db8d3bc6e4e160fc6339d66c",
|
54
|
+
"sha256:562d11134c97a62fe3af29581f083033179f7ff435f78392565a1ad2d1c2c45c",
|
55
|
+
"sha256:598174aef4589af795f66f9caab87ba4ff860ce08cd5bb447c6fc553ffee603c",
|
56
|
+
"sha256:63b5dff3a68f371ea06025a1a6966c9a1e1ee452fc8020c2cd0ea41b83e9037b",
|
57
|
+
"sha256:6ebce70c3f486acf7591a3d73431fa504a4e18a9b97ff27f5f47b7368e4b9dd1",
|
58
|
+
"sha256:738db0e0941ca0376804d4de6a782c005245264edaa253ffce24e5a15cbdc7bd",
|
59
|
+
"sha256:7491cf8a79b8eb867d419648fff2f83cb0b3891c8b36da92cc7f1931d46108c8",
|
60
|
+
"sha256:74ee3d7ecb3f3c05459ba95eed5efa28d6092d751ce9bf20e3e253a4e497e691",
|
61
|
+
"sha256:750f96efe0597382660d8b53e90dd1dd44568a8edb51cb7f9d5d918b80d4de14",
|
62
|
+
"sha256:78092232a4ab376a35d68c4e6d5e00dfd73454bd12b230420025fbe178ee3b0b",
|
63
|
+
"sha256:78afba22027b4accef10dbd5eed84425930ba41b3ea0a86fa8d20baaf19d807f",
|
64
|
+
"sha256:7bdb5e09068332578214cadd9c05e3d64d99e0e87591be22a324bdbc18925be0",
|
65
|
+
"sha256:80f1df8dbe9572b4b7abdfa17eb5d78dd620b1d55d9e25f834efdbee872d3aed",
|
66
|
+
"sha256:85d27ea4c889342f7e35f6d56e7e1cb345632ad592e8c51b693d7b7556043ce0",
|
67
|
+
"sha256:8b02d8f9cb83c52578a0b4beadba92e37d83a4ef11570a8688bbf43f4ca50909",
|
68
|
+
"sha256:8ce2e8411c7aaef53e6bb29fe98f28cd4fbd9a1d9be2eeea434331aac0536b22",
|
69
|
+
"sha256:8f4f3724c068be008c08257207210c138d5f3731af6c155a81c2b09a9eb3a788",
|
70
|
+
"sha256:9622e3b6c1d8b551b6e6f21873bdcc55762b4b2126633014cea1803368a9aa16",
|
71
|
+
"sha256:9b7b0d4fd2635f54ad82785d56bc0d94f147096493a79985d0ab57aedd563156",
|
72
|
+
"sha256:9bc7ae48b8057a611e5fe9f853baa88093b9a76303937449397899385da06fad",
|
73
|
+
"sha256:9db98ab6565c69082ec9b0d4e40dd9f6181dab0dd236d26f7a50b8b9bfbd5076",
|
74
|
+
"sha256:9ee66787e095127116d91dea2143db65c7bb1e232f617aa5957c0d9d2a3f23a7",
|
75
|
+
"sha256:a0a6709b47019dff32e678bc12c63008311b82b9327613f534e496dacaefb71e",
|
76
|
+
"sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6",
|
77
|
+
"sha256:aa442755e31c64037aa7c1cb186e0b369f8416c567381852c63444dd666fb772",
|
78
|
+
"sha256:ad275964d52e2243430472fc5d2c2334b4fc3ff9c16cb0a19254e25efa03a155",
|
79
|
+
"sha256:b0e130705d568e2f43a17bcbe74d90958e8a16263868a12c3e0d9c8162690830",
|
80
|
+
"sha256:b10428b3416d4f9c61f94b494681280be7686bda15898a3a9e08eb66a6d92d67",
|
81
|
+
"sha256:b2dbea1012ccb784a65349f57bbc93730b96e85b42e9bf7b01ef40443db720b4",
|
82
|
+
"sha256:b4ba4be812c7a40280629e55ae0b14a0aafa150dd6451297562e1764808bbe61",
|
83
|
+
"sha256:b93a07e76d13bff9444f1a029e0af2964e654bfc2e2c2d46bfd080df5ad5f3d8",
|
84
|
+
"sha256:bf2c33d6791c598142f00c9c4c7d47f6476731c31081331664eb26d6ab583e01",
|
85
|
+
"sha256:c27476257b2fdcd7872d54cfd119b3a9ce4610fb85c8e32b70b42e3680a29a1e",
|
86
|
+
"sha256:c8bd62331e5032bc396a93609982a9ab6b411c05078a52f5fe3cc59234a3abd1",
|
87
|
+
"sha256:c97209e85b5be259994eb5b69ff50c5d20cca0f458ef9abd835e262d9d88b39d",
|
88
|
+
"sha256:cc1c3bc53befb6096b84165956e886b1729634a799e9d6329a0c512ab651e579",
|
89
|
+
"sha256:cc5d875d56e49f112b6def6813c4e3d3036d269c008bf8aef72cd08d20ca6df6",
|
90
|
+
"sha256:d189ba1bebfbc0c0e529159631ec72bb9e9bc041f01ec6d3233d6d82eb823bc1",
|
91
|
+
"sha256:d4e5c5edee874dce4f653dbe59db7c73a600119fbea8d31f53423586ee2aafd7",
|
92
|
+
"sha256:d57a75d53922fc20c165016a20d9c44f73305e67c351bbc60d1adaf662e74047",
|
93
|
+
"sha256:da3104c57bbd72948d75f6a9389e6727d2ab6333c3617f0a89d72d4940aa0443",
|
94
|
+
"sha256:dd6b20b93b3ccc9c1b597999209e4bc5cf2853f9ee66e3fc9a400a78733ffc9a",
|
95
|
+
"sha256:e0409af9f829f87a2dfb7e259f78f317a5351f2045158be321fd135973fff7bf",
|
96
|
+
"sha256:e0b55f27f584ed623221cfe995c912c61606be8513bfa0e07d2c674b4516d9dd",
|
97
|
+
"sha256:e616e7154c37669fc1dfc14584f11e284e05d1c650e1c0f972f281c4ccc53193",
|
98
|
+
"sha256:e6def7eed9e7fa90fde255afaf08060dc4b343bbe524a8f69bdd2a2f0018f600",
|
99
|
+
"sha256:ea926cfbc3957090becbcbbb65ad177161a2ff2ad578b5a6ec9bb1e1cd78753c",
|
100
|
+
"sha256:f0d3348c95b766f54b76116d53d4cb171b52992a1027e7ca50c81b43b9d9e363",
|
101
|
+
"sha256:f6b0c664ccb879109ee3ca702a9272d877f4fcd21e5eb63c26422fd6e415365e",
|
102
|
+
"sha256:f781dcb0bc9929adc77bad571b8621ecb1e4cdef86e940fe2e5b5ee24fd33b35",
|
103
|
+
"sha256:f91ebf30830a48c825590aede79376cb40f110b387c17ee9bd59932c961044f9",
|
104
|
+
"sha256:fdec757fea0b793056419bca3e9932eb2b0ceec90ef4813ea4c1e072c389eb28",
|
105
|
+
"sha256:fe15238d3798788d00716637b3d4e7bb6bde18b26e5d08335a96e88564a36b6b"
|
92
106
|
],
|
93
107
|
"index": "pypi",
|
94
108
|
"markers": "python_version >= '3.9'",
|
95
|
-
"version": "==11.1
|
109
|
+
"version": "==11.2.1"
|
96
110
|
}
|
97
111
|
},
|
98
112
|
"develop": {
|
@@ -448,9 +462,8 @@
|
|
448
462
|
"version": "==3.12.0"
|
449
463
|
},
|
450
464
|
"fotolab": {
|
451
|
-
"
|
452
|
-
"markers": "python_version >= '3.9'"
|
453
|
-
"path": "."
|
465
|
+
"file": ".",
|
466
|
+
"markers": "python_version >= '3.9'"
|
454
467
|
},
|
455
468
|
"identify": {
|
456
469
|
"hashes": [
|
@@ -636,81 +649,91 @@
|
|
636
649
|
},
|
637
650
|
"pillow": {
|
638
651
|
"hashes": [
|
639
|
-
"sha256:
|
640
|
-
"sha256:
|
641
|
-
"sha256:
|
642
|
-
"sha256:
|
643
|
-
"sha256:
|
644
|
-
"sha256:
|
645
|
-
"sha256:
|
646
|
-
"sha256:
|
647
|
-
"sha256:
|
648
|
-
"sha256:
|
649
|
-
"sha256:
|
650
|
-
"sha256:
|
651
|
-
"sha256:
|
652
|
-
"sha256:
|
653
|
-
"sha256:
|
654
|
-
"sha256:
|
655
|
-
"sha256:
|
656
|
-
"sha256:
|
657
|
-
"sha256:
|
658
|
-
"sha256:
|
659
|
-
"sha256:
|
660
|
-
"sha256:
|
661
|
-
"sha256:
|
662
|
-
"sha256:
|
663
|
-
"sha256:
|
664
|
-
"sha256:
|
665
|
-
"sha256:
|
666
|
-
"sha256:
|
667
|
-
"sha256:
|
668
|
-
"sha256:
|
669
|
-
"sha256:
|
670
|
-
"sha256:
|
671
|
-
"sha256:
|
672
|
-
"sha256:
|
673
|
-
"sha256:
|
674
|
-
"sha256:
|
675
|
-
"sha256:
|
676
|
-
"sha256:
|
677
|
-
"sha256:
|
678
|
-
"sha256:
|
679
|
-
"sha256:
|
680
|
-
"sha256:
|
681
|
-
"sha256:
|
682
|
-
"sha256:
|
683
|
-
"sha256:
|
684
|
-
"sha256:
|
685
|
-
"sha256:
|
686
|
-
"sha256:
|
687
|
-
"sha256:
|
688
|
-
"sha256:
|
689
|
-
"sha256:
|
690
|
-
"sha256:
|
691
|
-
"sha256:
|
692
|
-
"sha256:
|
693
|
-
"sha256:
|
694
|
-
"sha256:
|
695
|
-
"sha256:
|
696
|
-
"sha256:
|
697
|
-
"sha256:
|
698
|
-
"sha256:
|
699
|
-
"sha256:
|
700
|
-
"sha256:
|
701
|
-
"sha256:
|
702
|
-
"sha256:
|
703
|
-
"sha256:
|
704
|
-
"sha256:
|
705
|
-
"sha256:
|
706
|
-
"sha256:
|
707
|
-
"sha256:
|
708
|
-
"sha256:
|
709
|
-
"sha256:
|
652
|
+
"sha256:014ca0050c85003620526b0ac1ac53f56fc93af128f7546623cc8e31875ab928",
|
653
|
+
"sha256:036e53f4170e270ddb8797d4c590e6dd14d28e15c7da375c18978045f7e6c37b",
|
654
|
+
"sha256:062b7a42d672c45a70fa1f8b43d1d38ff76b63421cbbe7f88146b39e8a558d91",
|
655
|
+
"sha256:0c3e6d0f59171dfa2e25d7116217543310908dfa2770aa64b8f87605f8cacc97",
|
656
|
+
"sha256:0c7b29dbd4281923a2bfe562acb734cee96bbb129e96e6972d315ed9f232bef4",
|
657
|
+
"sha256:0f5c7eda47bf8e3c8a283762cab94e496ba977a420868cb819159980b6709193",
|
658
|
+
"sha256:127bf6ac4a5b58b3d32fc8289656f77f80567d65660bc46f72c0d77e6600cc95",
|
659
|
+
"sha256:14e33b28bf17c7a38eede290f77db7c664e4eb01f7869e37fa98a5aa95978941",
|
660
|
+
"sha256:14f73f7c291279bd65fda51ee87affd7c1e097709f7fdd0188957a16c264601f",
|
661
|
+
"sha256:191955c55d8a712fab8934a42bfefbf99dd0b5875078240943f913bb66d46d9f",
|
662
|
+
"sha256:1d535df14716e7f8776b9e7fee118576d65572b4aad3ed639be9e4fa88a1cad3",
|
663
|
+
"sha256:208653868d5c9ecc2b327f9b9ef34e0e42a4cdd172c2988fd81d62d2bc9bc044",
|
664
|
+
"sha256:21e1470ac9e5739ff880c211fc3af01e3ae505859392bf65458c224d0bf283eb",
|
665
|
+
"sha256:225c832a13326e34f212d2072982bb1adb210e0cc0b153e688743018c94a2681",
|
666
|
+
"sha256:25a5f306095c6780c52e6bbb6109624b95c5b18e40aab1c3041da3e9e0cd3e2d",
|
667
|
+
"sha256:2728567e249cdd939f6cc3d1f049595c66e4187f3c34078cbc0a7d21c47482d2",
|
668
|
+
"sha256:2b490402c96f907a166615e9a5afacf2519e28295f157ec3a2bb9bd57de638cb",
|
669
|
+
"sha256:312c77b7f07ab2139924d2639860e084ec2a13e72af54d4f08ac843a5fc9c79d",
|
670
|
+
"sha256:31df6e2d3d8fc99f993fd253e97fae451a8db2e7207acf97859732273e108406",
|
671
|
+
"sha256:35ca289f712ccfc699508c4658a1d14652e8033e9b69839edf83cbdd0ba39e70",
|
672
|
+
"sha256:3692b68c87096ac6308296d96354eddd25f98740c9d2ab54e1549d6c8aea9d79",
|
673
|
+
"sha256:36d6b82164c39ce5482f649b437382c0fb2395eabc1e2b1702a6deb8ad647d6e",
|
674
|
+
"sha256:39ad2e0f424394e3aebc40168845fee52df1394a4673a6ee512d840d14ab3013",
|
675
|
+
"sha256:3e645b020f3209a0181a418bffe7b4a93171eef6c4ef6cc20980b30bebf17b7d",
|
676
|
+
"sha256:3fe735ced9a607fee4f481423a9c36701a39719252a9bb251679635f99d0f7d2",
|
677
|
+
"sha256:4b835d89c08a6c2ee7781b8dd0a30209a8012b5f09c0a665b65b0eb3560b6f36",
|
678
|
+
"sha256:4d375eb838755f2528ac8cbc926c3e31cc49ca4ad0cf79cff48b20e30634a4a7",
|
679
|
+
"sha256:4eb92eca2711ef8be42fd3f67533765d9fd043b8c80db204f16c8ea62ee1a751",
|
680
|
+
"sha256:5119225c622403afb4b44bad4c1ca6c1f98eed79db8d3bc6e4e160fc6339d66c",
|
681
|
+
"sha256:562d11134c97a62fe3af29581f083033179f7ff435f78392565a1ad2d1c2c45c",
|
682
|
+
"sha256:598174aef4589af795f66f9caab87ba4ff860ce08cd5bb447c6fc553ffee603c",
|
683
|
+
"sha256:63b5dff3a68f371ea06025a1a6966c9a1e1ee452fc8020c2cd0ea41b83e9037b",
|
684
|
+
"sha256:6ebce70c3f486acf7591a3d73431fa504a4e18a9b97ff27f5f47b7368e4b9dd1",
|
685
|
+
"sha256:738db0e0941ca0376804d4de6a782c005245264edaa253ffce24e5a15cbdc7bd",
|
686
|
+
"sha256:7491cf8a79b8eb867d419648fff2f83cb0b3891c8b36da92cc7f1931d46108c8",
|
687
|
+
"sha256:74ee3d7ecb3f3c05459ba95eed5efa28d6092d751ce9bf20e3e253a4e497e691",
|
688
|
+
"sha256:750f96efe0597382660d8b53e90dd1dd44568a8edb51cb7f9d5d918b80d4de14",
|
689
|
+
"sha256:78092232a4ab376a35d68c4e6d5e00dfd73454bd12b230420025fbe178ee3b0b",
|
690
|
+
"sha256:78afba22027b4accef10dbd5eed84425930ba41b3ea0a86fa8d20baaf19d807f",
|
691
|
+
"sha256:7bdb5e09068332578214cadd9c05e3d64d99e0e87591be22a324bdbc18925be0",
|
692
|
+
"sha256:80f1df8dbe9572b4b7abdfa17eb5d78dd620b1d55d9e25f834efdbee872d3aed",
|
693
|
+
"sha256:85d27ea4c889342f7e35f6d56e7e1cb345632ad592e8c51b693d7b7556043ce0",
|
694
|
+
"sha256:8b02d8f9cb83c52578a0b4beadba92e37d83a4ef11570a8688bbf43f4ca50909",
|
695
|
+
"sha256:8ce2e8411c7aaef53e6bb29fe98f28cd4fbd9a1d9be2eeea434331aac0536b22",
|
696
|
+
"sha256:8f4f3724c068be008c08257207210c138d5f3731af6c155a81c2b09a9eb3a788",
|
697
|
+
"sha256:9622e3b6c1d8b551b6e6f21873bdcc55762b4b2126633014cea1803368a9aa16",
|
698
|
+
"sha256:9b7b0d4fd2635f54ad82785d56bc0d94f147096493a79985d0ab57aedd563156",
|
699
|
+
"sha256:9bc7ae48b8057a611e5fe9f853baa88093b9a76303937449397899385da06fad",
|
700
|
+
"sha256:9db98ab6565c69082ec9b0d4e40dd9f6181dab0dd236d26f7a50b8b9bfbd5076",
|
701
|
+
"sha256:9ee66787e095127116d91dea2143db65c7bb1e232f617aa5957c0d9d2a3f23a7",
|
702
|
+
"sha256:a0a6709b47019dff32e678bc12c63008311b82b9327613f534e496dacaefb71e",
|
703
|
+
"sha256:a64dd61998416367b7ef979b73d3a85853ba9bec4c2925f74e588879a58716b6",
|
704
|
+
"sha256:aa442755e31c64037aa7c1cb186e0b369f8416c567381852c63444dd666fb772",
|
705
|
+
"sha256:ad275964d52e2243430472fc5d2c2334b4fc3ff9c16cb0a19254e25efa03a155",
|
706
|
+
"sha256:b0e130705d568e2f43a17bcbe74d90958e8a16263868a12c3e0d9c8162690830",
|
707
|
+
"sha256:b10428b3416d4f9c61f94b494681280be7686bda15898a3a9e08eb66a6d92d67",
|
708
|
+
"sha256:b2dbea1012ccb784a65349f57bbc93730b96e85b42e9bf7b01ef40443db720b4",
|
709
|
+
"sha256:b4ba4be812c7a40280629e55ae0b14a0aafa150dd6451297562e1764808bbe61",
|
710
|
+
"sha256:b93a07e76d13bff9444f1a029e0af2964e654bfc2e2c2d46bfd080df5ad5f3d8",
|
711
|
+
"sha256:bf2c33d6791c598142f00c9c4c7d47f6476731c31081331664eb26d6ab583e01",
|
712
|
+
"sha256:c27476257b2fdcd7872d54cfd119b3a9ce4610fb85c8e32b70b42e3680a29a1e",
|
713
|
+
"sha256:c8bd62331e5032bc396a93609982a9ab6b411c05078a52f5fe3cc59234a3abd1",
|
714
|
+
"sha256:c97209e85b5be259994eb5b69ff50c5d20cca0f458ef9abd835e262d9d88b39d",
|
715
|
+
"sha256:cc1c3bc53befb6096b84165956e886b1729634a799e9d6329a0c512ab651e579",
|
716
|
+
"sha256:cc5d875d56e49f112b6def6813c4e3d3036d269c008bf8aef72cd08d20ca6df6",
|
717
|
+
"sha256:d189ba1bebfbc0c0e529159631ec72bb9e9bc041f01ec6d3233d6d82eb823bc1",
|
718
|
+
"sha256:d4e5c5edee874dce4f653dbe59db7c73a600119fbea8d31f53423586ee2aafd7",
|
719
|
+
"sha256:d57a75d53922fc20c165016a20d9c44f73305e67c351bbc60d1adaf662e74047",
|
720
|
+
"sha256:da3104c57bbd72948d75f6a9389e6727d2ab6333c3617f0a89d72d4940aa0443",
|
721
|
+
"sha256:dd6b20b93b3ccc9c1b597999209e4bc5cf2853f9ee66e3fc9a400a78733ffc9a",
|
722
|
+
"sha256:e0409af9f829f87a2dfb7e259f78f317a5351f2045158be321fd135973fff7bf",
|
723
|
+
"sha256:e0b55f27f584ed623221cfe995c912c61606be8513bfa0e07d2c674b4516d9dd",
|
724
|
+
"sha256:e616e7154c37669fc1dfc14584f11e284e05d1c650e1c0f972f281c4ccc53193",
|
725
|
+
"sha256:e6def7eed9e7fa90fde255afaf08060dc4b343bbe524a8f69bdd2a2f0018f600",
|
726
|
+
"sha256:ea926cfbc3957090becbcbbb65ad177161a2ff2ad578b5a6ec9bb1e1cd78753c",
|
727
|
+
"sha256:f0d3348c95b766f54b76116d53d4cb171b52992a1027e7ca50c81b43b9d9e363",
|
728
|
+
"sha256:f6b0c664ccb879109ee3ca702a9272d877f4fcd21e5eb63c26422fd6e415365e",
|
729
|
+
"sha256:f781dcb0bc9929adc77bad571b8621ecb1e4cdef86e940fe2e5b5ee24fd33b35",
|
730
|
+
"sha256:f91ebf30830a48c825590aede79376cb40f110b387c17ee9bd59932c961044f9",
|
731
|
+
"sha256:fdec757fea0b793056419bca3e9932eb2b0ceec90ef4813ea4c1e072c389eb28",
|
732
|
+
"sha256:fe15238d3798788d00716637b3d4e7bb6bde18b26e5d08335a96e88564a36b6b"
|
710
733
|
],
|
711
734
|
"index": "pypi",
|
712
735
|
"markers": "python_version >= '3.9'",
|
713
|
-
"version": "==11.1
|
736
|
+
"version": "==11.2.1"
|
714
737
|
},
|
715
738
|
"pip": {
|
716
739
|
"hashes": [
|
@@ -882,11 +905,11 @@
|
|
882
905
|
},
|
883
906
|
"scripttest": {
|
884
907
|
"hashes": [
|
885
|
-
"sha256:
|
886
|
-
"sha256:
|
908
|
+
"sha256:a6067f1feadf4b2ed3e594b0b32e41589640e7fa3974765aa63d508520c0bfdc",
|
909
|
+
"sha256:ec818fd362d5ee0321e000ea43c0540020336771f5168bd591ddd5b37258ca06"
|
887
910
|
],
|
888
911
|
"index": "pypi",
|
889
|
-
"version": "==2.0"
|
912
|
+
"version": "==2.0.post1"
|
890
913
|
},
|
891
914
|
"snowballstemmer": {
|
892
915
|
"hashes": [
|
@@ -1019,11 +1042,11 @@
|
|
1019
1042
|
},
|
1020
1043
|
"urllib3": {
|
1021
1044
|
"hashes": [
|
1022
|
-
"sha256:
|
1023
|
-
"sha256:
|
1045
|
+
"sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466",
|
1046
|
+
"sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"
|
1024
1047
|
],
|
1025
1048
|
"markers": "python_version >= '3.9'",
|
1026
|
-
"version": "==2.
|
1049
|
+
"version": "==2.4.0"
|
1027
1050
|
},
|
1028
1051
|
"virtualenv": {
|
1029
1052
|
"hashes": [
|
@@ -17,13 +17,14 @@
|
|
17
17
|
|
18
18
|
import argparse
|
19
19
|
import logging
|
20
|
+
import os
|
20
21
|
import subprocess
|
21
22
|
import sys
|
22
23
|
from pathlib import Path
|
23
24
|
|
24
25
|
from PIL import Image
|
25
26
|
|
26
|
-
__version__ = "0.28.
|
27
|
+
__version__ = "0.28.6"
|
27
28
|
|
28
29
|
log = logging.getLogger(__name__)
|
29
30
|
|
@@ -98,10 +99,9 @@ def _open_image(filename):
|
|
98
99
|
subprocess.call(["xdg-open", filename])
|
99
100
|
elif sys.platform == "darwin":
|
100
101
|
subprocess.call(["open", filename])
|
101
|
-
elif sys.platform == "
|
102
|
-
|
103
|
-
proc.wait()
|
102
|
+
elif sys.platform == "win32":
|
103
|
+
os.startfile(filename)
|
104
104
|
log.info("open image: %s", filename.resolve())
|
105
105
|
|
106
|
-
except OSError as error:
|
106
|
+
except (OSError, FileNotFoundError) as error:
|
107
107
|
log.error("Error opening image: %s -> %s", filename, error)
|
@@ -1,17 +1,17 @@
|
|
1
1
|
# Copyright (C) 2024,2025 Kian-Meng Ang
|
2
2
|
|
3
|
-
# This program is free software: you can redistribute it and/or modify
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
|
8
|
-
# This program is distributed in the hope that it will be useful,
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
|
13
|
-
# You should have received a copy of the GNU General Public License
|
14
|
-
# along with this program.
|
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
15
|
|
16
16
|
"""A console program to manipulate photos.
|
17
17
|
|
@@ -19,23 +19,24 @@ import argparse
|
|
19
19
|
import logging
|
20
20
|
import math
|
21
21
|
from pathlib import Path
|
22
|
+
from typing import Tuple
|
22
23
|
|
23
24
|
from PIL import Image, ImageColor, ImageDraw, ImageFont, ImageSequence
|
24
25
|
|
25
26
|
from fotolab import save_image
|
26
27
|
from fotolab.subcommands.info import camera_metadata
|
27
28
|
|
28
|
-
log = logging.getLogger(__name__)
|
29
|
+
log: logging.Logger = logging.getLogger(__name__)
|
29
30
|
|
30
|
-
FONT_SIZE_ASPECT_RATIO = 12 / 600
|
31
|
-
FONT_PADDING_ASPECT_RATIO = 15 / 600
|
32
|
-
FONT_OUTLINE_WIDTH_ASPECT_RATIO = 2 / 600
|
33
|
-
POSITIONS = ["top-left", "top-right", "bottom-left", "bottom-right"]
|
31
|
+
FONT_SIZE_ASPECT_RATIO: float = 12 / 600
|
32
|
+
FONT_PADDING_ASPECT_RATIO: float = 15 / 600
|
33
|
+
FONT_OUTLINE_WIDTH_ASPECT_RATIO: float = 2 / 600
|
34
|
+
POSITIONS: list[str] = ["top-left", "top-right", "bottom-left", "bottom-right"]
|
34
35
|
|
35
36
|
|
36
|
-
def build_subparser(subparsers) -> None:
|
37
|
+
def build_subparser(subparsers: argparse._SubParsersAction) -> None:
|
37
38
|
"""Build the subparser."""
|
38
|
-
watermark_parser = subparsers.add_parser(
|
39
|
+
watermark_parser: argparse.ArgumentParser = subparsers.add_parser(
|
39
40
|
"watermark", help="watermark an image"
|
40
41
|
)
|
41
42
|
|
@@ -175,11 +176,13 @@ def run(args: argparse.Namespace) -> None:
|
|
175
176
|
log.debug(args)
|
176
177
|
|
177
178
|
for image_filename in args.image_filenames:
|
178
|
-
image = Image.open(image_filename)
|
179
|
+
image: Image.Image = Image.open(image_filename)
|
179
180
|
if image.format == "GIF":
|
180
181
|
watermark_gif_image(image, image_filename, args)
|
181
182
|
else:
|
182
|
-
watermarked_image = watermark_non_gif_image(
|
183
|
+
watermarked_image: Image.Image = watermark_non_gif_image(
|
184
|
+
image, args
|
185
|
+
)
|
183
186
|
save_image(args, watermarked_image, image_filename, "watermark")
|
184
187
|
|
185
188
|
|
@@ -196,15 +199,17 @@ def watermark_gif_image(
|
|
196
199
|
Returns:
|
197
200
|
None
|
198
201
|
"""
|
199
|
-
frames = []
|
202
|
+
frames: list[Image.Image] = []
|
200
203
|
for frame in ImageSequence.Iterator(original_image):
|
201
|
-
watermarked_frame = watermark_image(
|
204
|
+
watermarked_frame: Image.Image = watermark_image(
|
205
|
+
args, frame.convert("RGBA")
|
206
|
+
)
|
202
207
|
frames.append(watermarked_frame)
|
203
208
|
|
204
|
-
image_file = Path(output_filename)
|
209
|
+
image_file: Path = Path(output_filename)
|
205
210
|
|
206
211
|
if args.overwrite:
|
207
|
-
new_filename =
|
212
|
+
new_filename: Path = image_file.with_name(image_file.name)
|
208
213
|
else:
|
209
214
|
new_filename = Path(
|
210
215
|
args.output_dir,
|
@@ -243,16 +248,18 @@ def watermark_image(
|
|
243
248
|
args: argparse.Namespace, original_image: Image.Image
|
244
249
|
) -> Image.Image:
|
245
250
|
"""Watermark an image."""
|
246
|
-
watermarked_image = original_image.copy()
|
247
|
-
draw = ImageDraw.Draw(watermarked_image)
|
251
|
+
watermarked_image: Image.Image = original_image.copy()
|
252
|
+
draw: ImageDraw.ImageDraw = ImageDraw.Draw(watermarked_image)
|
248
253
|
|
249
|
-
font = ImageFont.load_default(
|
254
|
+
font: ImageFont.FreeTypeFont = ImageFont.load_default(
|
255
|
+
calc_font_size(original_image, args)
|
256
|
+
)
|
250
257
|
log.debug("default font: %s", " ".join(font.getname()))
|
251
258
|
|
252
|
-
text = prepare_text(args, original_image)
|
259
|
+
text: str = prepare_text(args, original_image)
|
253
260
|
(left, top, right, bottom) = draw.textbbox(xy=(0, 0), text=text, font=font)
|
254
|
-
text_width = right - left
|
255
|
-
text_height = bottom - top
|
261
|
+
text_width: int = right - left
|
262
|
+
text_height: int = bottom - top
|
256
263
|
(position_x, position_y) = calc_position(
|
257
264
|
watermarked_image,
|
258
265
|
text_width,
|
@@ -272,9 +279,9 @@ def watermark_image(
|
|
272
279
|
return watermarked_image
|
273
280
|
|
274
281
|
|
275
|
-
def prepare_text(args, image) -> str:
|
282
|
+
def prepare_text(args: argparse.Namespace, image: Image.Image) -> str:
|
276
283
|
"""Prepare the watermark text."""
|
277
|
-
text = args.text
|
284
|
+
text: str = args.text
|
278
285
|
if args.camera and camera_metadata(image):
|
279
286
|
text = camera_metadata(image)
|
280
287
|
|
@@ -284,10 +291,10 @@ def prepare_text(args, image) -> str:
|
|
284
291
|
return text
|
285
292
|
|
286
293
|
|
287
|
-
def calc_font_size(image, args) -> int:
|
294
|
+
def calc_font_size(image: Image.Image, args: argparse.Namespace) -> int:
|
288
295
|
"""Calculate the font size based on the width of the image."""
|
289
296
|
width, _height = image.size
|
290
|
-
new_font_size = args.font_size
|
297
|
+
new_font_size: int = args.font_size
|
291
298
|
if width > 600:
|
292
299
|
new_font_size = math.floor(FONT_SIZE_ASPECT_RATIO * width)
|
293
300
|
|
@@ -295,10 +302,12 @@ def calc_font_size(image, args) -> int:
|
|
295
302
|
return new_font_size
|
296
303
|
|
297
304
|
|
298
|
-
def calc_font_outline_width(
|
305
|
+
def calc_font_outline_width(
|
306
|
+
image: Image.Image, args: argparse.Namespace
|
307
|
+
) -> int:
|
299
308
|
"""Calculate the font padding based on the width of the image."""
|
300
309
|
width, _height = image.size
|
301
|
-
new_font_outline_width = args.outline_width
|
310
|
+
new_font_outline_width: int = args.outline_width
|
302
311
|
if width > 600:
|
303
312
|
new_font_outline_width = math.floor(
|
304
313
|
FONT_OUTLINE_WIDTH_ASPECT_RATIO * width
|
@@ -308,10 +317,10 @@ def calc_font_outline_width(image, args) -> int:
|
|
308
317
|
return new_font_outline_width
|
309
318
|
|
310
319
|
|
311
|
-
def calc_padding(image, args) -> int:
|
320
|
+
def calc_padding(image: Image.Image, args: argparse.Namespace) -> int:
|
312
321
|
"""Calculate the font padding based on the width of the image."""
|
313
322
|
width, _height = image.size
|
314
|
-
new_padding = args.padding
|
323
|
+
new_padding: int = args.padding
|
315
324
|
if width > 600:
|
316
325
|
new_padding = math.floor(FONT_PADDING_ASPECT_RATIO * width)
|
317
326
|
|
@@ -319,9 +328,15 @@ def calc_padding(image, args) -> int:
|
|
319
328
|
return new_padding
|
320
329
|
|
321
330
|
|
322
|
-
def calc_position(
|
331
|
+
def calc_position(
|
332
|
+
image: Image.Image,
|
333
|
+
text_width: int,
|
334
|
+
text_height: int,
|
335
|
+
position: str,
|
336
|
+
padding: int,
|
337
|
+
) -> Tuple[int, int]:
|
323
338
|
"""Calculate the boundary coordinates of the watermark text."""
|
324
|
-
positions = {
|
339
|
+
positions: dict[str, Tuple[int, int]] = {
|
325
340
|
"top-left": (padding, padding),
|
326
341
|
"top-right": (image.width - text_width - padding, padding),
|
327
342
|
"bottom-left": (padding, image.height - text_height - padding),
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
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
|