fotolab 0.13.0__py2.py3-none-any.whl → 0.16.3__py2.py3-none-any.whl

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/__init__.py CHANGED
@@ -21,7 +21,7 @@ import subprocess
21
21
  import sys
22
22
  from pathlib import Path
23
23
 
24
- __version__ = "0.13.0"
24
+ __version__ = "0.16.3"
25
25
 
26
26
  log = logging.getLogger(__name__)
27
27
 
@@ -48,7 +48,7 @@ def save_image(args, new_image, output_filename, subcommand):
48
48
  )
49
49
  new_filename.parent.mkdir(parents=True, exist_ok=True)
50
50
 
51
- log.info("%s image: %s", subcommand, new_filename)
51
+ log.info("%s image: %s", subcommand, new_filename.resolve())
52
52
  new_image.save(new_filename)
53
53
 
54
54
  if args.open:
@@ -64,4 +64,4 @@ def _open_image(filename):
64
64
  elif sys.platform == "windows":
65
65
  os.startfile(filename)
66
66
 
67
- log.info("open image: %s using default program.", filename.resolve())
67
+ log.info("open image: %s", filename.resolve())
fotolab/animate.py ADDED
@@ -0,0 +1,114 @@
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
+ """Animate subcommand."""
17
+
18
+ import argparse
19
+ import logging
20
+ from pathlib import Path
21
+
22
+ from PIL import Image
23
+
24
+ from fotolab import _open_image
25
+
26
+ log = logging.getLogger(__name__)
27
+
28
+
29
+ def build_subparser(subparsers) -> None:
30
+ """Build the subparser."""
31
+ animate_parser = subparsers.add_parser("animate", help="animate an image")
32
+
33
+ animate_parser.set_defaults(func=run)
34
+
35
+ animate_parser.add_argument(
36
+ dest="image_filenames",
37
+ help="set the image filenames",
38
+ nargs="+",
39
+ type=str,
40
+ default=None,
41
+ metavar="IMAGE_FILENAMES",
42
+ )
43
+
44
+ animate_parser.add_argument(
45
+ "-f",
46
+ "--format",
47
+ dest="format",
48
+ type=str,
49
+ choices=["gif", "webp"],
50
+ default="gif",
51
+ help="set the image format (default: '%(default)s')",
52
+ metavar="FORMAT",
53
+ )
54
+
55
+ animate_parser.add_argument(
56
+ "-d",
57
+ "--duration",
58
+ dest="duration",
59
+ type=int,
60
+ default=2500,
61
+ help="set the duration in milliseconds (default: '%(default)s')",
62
+ metavar="DURATION",
63
+ )
64
+
65
+ animate_parser.add_argument(
66
+ "-l",
67
+ "--loop",
68
+ dest="loop",
69
+ type=int,
70
+ default=0,
71
+ help="set the loop cycle (default: '%(default)s')",
72
+ metavar="LOOP",
73
+ )
74
+
75
+
76
+ def run(args: argparse.Namespace) -> None:
77
+ """Run animate subcommand.
78
+
79
+ Args:
80
+ config (argparse.Namespace): Config from command line arguments
81
+
82
+ Returns:
83
+ None
84
+ """
85
+ log.debug(args)
86
+
87
+ first_image = args.image_filenames[0]
88
+ animated_image = Image.open(first_image)
89
+
90
+ append_images = []
91
+ for image_filename in args.image_filenames[1:]:
92
+ append_images.append(Image.open(image_filename))
93
+
94
+ image_file = Path(first_image)
95
+ new_filename = Path(
96
+ args.output_dir,
97
+ image_file.with_name(f"animate_{image_file.stem}.{args.format}"),
98
+ )
99
+ new_filename.parent.mkdir(parents=True, exist_ok=True)
100
+
101
+ log.info("animate image: %s", new_filename)
102
+
103
+ animated_image.save(
104
+ new_filename,
105
+ format=args.format,
106
+ append_images=append_images,
107
+ save_all=True,
108
+ duration=args.duration,
109
+ loop=args.loop,
110
+ optimize=True,
111
+ )
112
+
113
+ if args.open:
114
+ _open_image(new_filename)
fotolab/cli.py CHANGED
@@ -25,6 +25,7 @@ import logging
25
25
  import sys
26
26
  from typing import Dict, Optional, Sequence
27
27
 
28
+ import fotolab.animate
28
29
  import fotolab.auto
29
30
  import fotolab.border
30
31
  import fotolab.contrast
@@ -32,6 +33,7 @@ import fotolab.env
32
33
  import fotolab.info
33
34
  import fotolab.montage
34
35
  import fotolab.resize
36
+ import fotolab.rotate
35
37
  import fotolab.sharpen
36
38
  import fotolab.watermark
37
39
  from fotolab import __version__
@@ -41,6 +43,9 @@ log = logging.getLogger(__name__)
41
43
 
42
44
  def setup_logging(args: argparse.Namespace) -> None:
43
45
  """Set up logging by level."""
46
+ if args.verbose == 0:
47
+ logging.getLogger("PIL").setLevel(logging.ERROR)
48
+
44
49
  if args.quiet:
45
50
  logging.disable(logging.NOTSET)
46
51
  else:
@@ -104,6 +109,15 @@ def build_parser() -> argparse.ArgumentParser:
104
109
  help="suppress all logging",
105
110
  )
106
111
 
112
+ parser.add_argument(
113
+ "-v",
114
+ "--verbose",
115
+ default=0,
116
+ action="count",
117
+ dest="verbose",
118
+ help="show verbosity of debugging log, use -vv, -vvv for more details",
119
+ )
120
+
107
121
  parser.add_argument(
108
122
  "-d",
109
123
  "--debug",
@@ -121,11 +135,13 @@ def build_parser() -> argparse.ArgumentParser:
121
135
  )
122
136
 
123
137
  subparsers = parser.add_subparsers(help="sub-command help")
138
+ fotolab.animate.build_subparser(subparsers)
124
139
  fotolab.auto.build_subparser(subparsers)
125
140
  fotolab.border.build_subparser(subparsers)
126
141
  fotolab.contrast.build_subparser(subparsers)
127
142
  fotolab.info.build_subparser(subparsers)
128
143
  fotolab.resize.build_subparser(subparsers)
144
+ fotolab.rotate.build_subparser(subparsers)
129
145
  fotolab.montage.build_subparser(subparsers)
130
146
  fotolab.sharpen.build_subparser(subparsers)
131
147
  fotolab.watermark.build_subparser(subparsers)
fotolab/resize.py CHANGED
@@ -17,6 +17,7 @@
17
17
 
18
18
  import argparse
19
19
  import logging
20
+ import math
20
21
 
21
22
  from PIL import Image
22
23
 
@@ -24,6 +25,9 @@ from fotolab import save_image
24
25
 
25
26
  log = logging.getLogger(__name__)
26
27
 
28
+ DEFAULT_WIDTH = 600
29
+ DEFAULT_HEIGHT = 277
30
+
27
31
 
28
32
  def build_subparser(subparsers) -> None:
29
33
  """Build the subparser."""
@@ -40,23 +44,25 @@ def build_subparser(subparsers) -> None:
40
44
  metavar="IMAGE_FILENAMES",
41
45
  )
42
46
 
43
- resize_parser.add_argument(
47
+ group = resize_parser.add_mutually_exclusive_group(required=False)
48
+
49
+ group.add_argument(
44
50
  "-wh",
45
51
  "--width",
46
52
  dest="width",
47
53
  help="set the width of the image (default: '%(default)s')",
48
54
  type=int,
49
- default="600",
55
+ default=DEFAULT_WIDTH,
50
56
  metavar="WIDTH",
51
57
  )
52
58
 
53
- resize_parser.add_argument(
59
+ group.add_argument(
54
60
  "-ht",
55
61
  "--height",
56
62
  dest="height",
57
63
  help="set the height of the image (default: '%(default)s')",
58
64
  type=int,
59
- default="277",
65
+ default=DEFAULT_HEIGHT,
60
66
  metavar="HEIGHT",
61
67
  )
62
68
 
@@ -74,9 +80,37 @@ def run(args: argparse.Namespace) -> None:
74
80
 
75
81
  for image_filename in args.image_filenames:
76
82
  original_image = Image.open(image_filename)
83
+
84
+ new_width, new_height = _calc_new_image_dimension(original_image, args)
77
85
  resized_image = original_image.copy()
78
86
  resized_image = resized_image.resize(
79
- (args.width, args.height), Image.Resampling.LANCZOS
87
+ (new_width, new_height), Image.Resampling.LANCZOS
80
88
  )
81
89
 
82
90
  save_image(args, resized_image, image_filename, "resize")
91
+
92
+
93
+ def _calc_new_image_dimension(image, args) -> tuple:
94
+ new_width = args.width
95
+ new_height = args.height
96
+
97
+ old_width, old_height = image.size
98
+ log.debug("old image dimension: %d x %d", old_width, old_height)
99
+
100
+ if args.width != DEFAULT_WIDTH:
101
+ aspect_ratio = old_height / old_width
102
+ log.debug("aspect ratio: %f", aspect_ratio)
103
+
104
+ new_height = math.ceil(args.width * aspect_ratio)
105
+ log.debug("new height: %d", new_height)
106
+
107
+ if args.height != DEFAULT_HEIGHT:
108
+ aspect_ratio = old_width / old_height
109
+ log.debug("aspect ratio: %f", aspect_ratio)
110
+
111
+ new_width = math.floor(args.height * aspect_ratio)
112
+ log.debug("new width: %d", new_width)
113
+
114
+ log.debug("new image dimension: %d x %d", new_width, new_height)
115
+
116
+ return (new_width, new_height)
fotolab/rotate.py ADDED
@@ -0,0 +1,61 @@
1
+ # Copyright (C) 2024 Kian-Meng Ang
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify it under
4
+ # the terms of the GNU Affero General Public License as published by the Free
5
+ # Software Foundation, either version 3 of the License, or (at your option) any
6
+ # later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful, but WITHOUT
9
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10
+ # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
11
+ # details.
12
+ #
13
+ # You should have received a copy of the GNU Affero General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ """Rotate subcommand."""
17
+
18
+ import argparse
19
+ import logging
20
+
21
+ from PIL import Image
22
+
23
+ from fotolab import save_image
24
+
25
+ log = logging.getLogger(__name__)
26
+
27
+
28
+ def build_subparser(subparsers) -> None:
29
+ """Build the subparser."""
30
+ rotate_parser = subparsers.add_parser("rotate", help="rotate an image")
31
+
32
+ rotate_parser.set_defaults(func=run)
33
+
34
+ rotate_parser.add_argument(
35
+ dest="image_filenames",
36
+ help="set the image filenames",
37
+ nargs="+",
38
+ type=str,
39
+ default=None,
40
+ metavar="IMAGE_FILENAMES",
41
+ )
42
+
43
+
44
+ def run(args: argparse.Namespace) -> None:
45
+ """Run rotate subcommand.
46
+
47
+ Args:
48
+ config (argparse.Namespace): Config from command line arguments
49
+
50
+ Returns:
51
+ None
52
+ """
53
+ log.debug(args)
54
+
55
+ for image_filename in args.image_filenames:
56
+ original_image = Image.open(image_filename)
57
+ rotated_image = original_image.rotate(
58
+ 180,
59
+ expand=True,
60
+ )
61
+ save_image(args, rotated_image, image_filename, "rotate")
fotolab/sharpen.py CHANGED
@@ -44,7 +44,7 @@ def build_subparser(subparsers) -> None:
44
44
  "-r",
45
45
  "--radius",
46
46
  dest="radius",
47
- type=str,
47
+ type=int,
48
48
  default=1,
49
49
  help="set the radius or size of edges (default: '%(default)s')",
50
50
  metavar="RADIUS",
@@ -54,7 +54,7 @@ def build_subparser(subparsers) -> None:
54
54
  "-p",
55
55
  "--percent",
56
56
  dest="percent",
57
- type=str,
57
+ type=int,
58
58
  default=100,
59
59
  help=(
60
60
  "set the amount of overall strength of sharpening effect "
@@ -67,7 +67,7 @@ def build_subparser(subparsers) -> None:
67
67
  "-t",
68
68
  "--threshold",
69
69
  dest="threshold",
70
- type=str,
70
+ type=int,
71
71
  default=3,
72
72
  help=(
73
73
  "set the minimum brightness changed to be sharpened "
fotolab/watermark.py CHANGED
@@ -17,6 +17,7 @@
17
17
 
18
18
  import argparse
19
19
  import logging
20
+ import math
20
21
 
21
22
  from PIL import Image, ImageColor, ImageDraw, ImageFont
22
23
 
@@ -24,6 +25,9 @@ from fotolab import save_image
24
25
 
25
26
  log = logging.getLogger(__name__)
26
27
 
28
+ FONT_SIZE_ASPECT_RATIO = 12 / 600
29
+ FONT_PADDING_ASPECT_RATIO = 15 / 600
30
+ FONT_OUTLINE_WIDTH_ASPECT_RATIO = 2 / 600
27
31
  POSITIONS = ["top-left", "top-right", "bottom-left", "bottom-right"]
28
32
 
29
33
 
@@ -140,19 +144,21 @@ def run(args: argparse.Namespace) -> None:
140
144
 
141
145
  draw = ImageDraw.Draw(watermarked_image)
142
146
 
143
- font = ImageFont.truetype("arial.ttf", args.font_size)
147
+ font = ImageFont.truetype(
148
+ "arial.ttf", calc_font_size(original_image, args)
149
+ )
144
150
 
145
151
  (left, top, right, bottom) = draw.textbbox(
146
152
  xy=(0, 0), text=args.text, font=font
147
153
  )
148
154
  text_width = right - left
149
155
  text_height = bottom - top
150
- (position_x, position_y) = calculate_position(
156
+ (position_x, position_y) = calc_position(
151
157
  watermarked_image,
152
158
  text_width,
153
159
  text_height,
154
160
  args.position,
155
- args.padding,
161
+ calc_padding(original_image, args),
156
162
  )
157
163
 
158
164
  draw.text(
@@ -160,16 +166,49 @@ def run(args: argparse.Namespace) -> None:
160
166
  args.text,
161
167
  font=font,
162
168
  fill=(*ImageColor.getrgb(args.font_color), 128),
163
- stroke_width=args.outline_width,
169
+ stroke_width=calc_font_outline_width(original_image, args),
164
170
  stroke_fill=(*ImageColor.getrgb(args.outline_color), 128),
165
171
  )
166
172
 
167
173
  save_image(args, watermarked_image, image_filename, "watermark")
168
174
 
169
175
 
170
- def calculate_position(
171
- image, text_width, text_height, position, padding
172
- ) -> tuple:
176
+ def calc_font_size(image, args) -> int:
177
+ """Calculate the font size based on the width of the image."""
178
+ width, _height = image.size
179
+ new_font_size = args.font_size
180
+ if width > 600:
181
+ new_font_size = math.floor(FONT_SIZE_ASPECT_RATIO * width)
182
+
183
+ log.debug("new font size: %d", new_font_size)
184
+ return new_font_size
185
+
186
+
187
+ def calc_font_outline_width(image, args) -> int:
188
+ """Calculate the font padding based on the width of the image."""
189
+ width, _height = image.size
190
+ new_font_outline_width = args.outline_width
191
+ if width > 600:
192
+ new_font_outline_width = math.floor(
193
+ FONT_OUTLINE_WIDTH_ASPECT_RATIO * width
194
+ )
195
+
196
+ log.debug("new font outline width: %d", new_font_outline_width)
197
+ return new_font_outline_width
198
+
199
+
200
+ def calc_padding(image, args) -> int:
201
+ """Calculate the font padding based on the width of the image."""
202
+ width, _height = image.size
203
+ new_padding = args.padding
204
+ if width > 600:
205
+ new_padding = math.floor(FONT_PADDING_ASPECT_RATIO * width)
206
+
207
+ log.debug("new padding: %d", new_padding)
208
+ return new_padding
209
+
210
+
211
+ def calc_position(image, text_width, text_height, position, padding) -> tuple:
173
212
  """Calculate the boundary coordinates of the watermark text."""
174
213
  if position == "top-left":
175
214
  position_x = 0 + padding
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fotolab
3
- Version: 0.13.0
3
+ Version: 0.16.3
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,8 +57,8 @@ fotolab -h
57
57
  ```
58
58
 
59
59
  ```console
60
- usage: fotolab [-h] [-o] [-op] [-od OUTPUT_DIR] [-q] [-d] [-V]
61
- {auto,border,contrast,info,resize,montage,sharpen,watermark,env}
60
+ usage: fotolab [-h] [-o] [-op] [-od OUTPUT_DIR] [-q] [-v] [-d] [-V]
61
+ {animate,auto,border,contrast,info,resize,rotate,montage,sharpen,watermark,env}
62
62
  ...
63
63
 
64
64
  A console program to manipulate photos.
@@ -68,13 +68,15 @@ A console program to manipulate photos.
68
68
  issues: https://github.com/kianmeng/fotolab/issues
69
69
 
70
70
  positional arguments:
71
- {auto,border,contrast,info,resize,montage,sharpen,watermark,env}
71
+ {animate,auto,border,contrast,info,resize,rotate,montage,sharpen,watermark,env}
72
72
  sub-command help
73
+ animate animate an image
73
74
  auto auto adjust (resize, contrast, and watermark) a photo
74
75
  border add border to image
75
76
  contrast contrast an image
76
77
  info info an image
77
78
  resize resize an image
79
+ rotate rotate an image
78
80
  montage montage a list of image
79
81
  sharpen sharpen an image
80
82
  watermark watermark an image
@@ -87,10 +89,33 @@ optional arguments:
87
89
  -od OUTPUT_DIR, --output-dir OUTPUT_DIR
88
90
  set default output folder (default: 'output')
89
91
  -q, --quiet suppress all logging
92
+ -v, --verbose show verbosity of debugging log, use -vv, -vvv for more details
90
93
  -d, --debug show debugging log and stacktrace
91
94
  -V, --version show program's version number and exit
92
95
  ```
93
96
 
97
+ ### fotolab animate
98
+
99
+ ```console
100
+ fotolab animate -h
101
+ ```
102
+
103
+ ```console
104
+ usage: fotolab animate [-h] [-f FORMAT] [-d DURATION] [-l LOOP]
105
+ IMAGE_FILENAMES [IMAGE_FILENAMES ...]
106
+
107
+ positional arguments:
108
+ IMAGE_FILENAMES set the image filenames
109
+
110
+ optional arguments:
111
+ -h, --help show this help message and exit
112
+ -f FORMAT, --format FORMAT
113
+ set the image format (default: 'gif')
114
+ -d DURATION, --duration DURATION
115
+ set the duration in milliseconds (default: '2500')
116
+ -l LOOP, --loop LOOP set the loop cycle (default: '0')
117
+ ```
118
+
94
119
  ### fotolab auto
95
120
 
96
121
  ```console
@@ -172,6 +197,22 @@ optional arguments:
172
197
  -h, --help show this help message and exit
173
198
  ```
174
199
 
200
+ ### fotolab rotate
201
+
202
+ ```console
203
+ fotolab rotate -h
204
+ ```
205
+
206
+ ```console
207
+ usage: fotolab rotate [-h] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
208
+
209
+ positional arguments:
210
+ IMAGE_FILENAMES set the image filenames
211
+
212
+ optional arguments:
213
+ -h, --help show this help message and exit
214
+ ```
215
+
175
216
  ### fotolab montage
176
217
 
177
218
  ```console
@@ -179,7 +220,6 @@ fotolab montage -h
179
220
  ```
180
221
 
181
222
  ```console
182
-
183
223
  usage: fotolab montage [-h] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
184
224
 
185
225
  positional arguments:
@@ -196,7 +236,7 @@ fotolab resize -h
196
236
  ```
197
237
 
198
238
  ```console
199
- usage: fotolab resize [-h] [-wh WIDTH] [-ht HEIGHT]
239
+ usage: fotolab resize [-h] [-wh WIDTH | -ht HEIGHT]
200
240
  IMAGE_FILENAMES [IMAGE_FILENAMES ...]
201
241
 
202
242
  positional arguments:
@@ -0,0 +1,19 @@
1
+ fotolab/__init__.py,sha256=_g6Bhn9-dug7d9e9oOkmCaQzvI5-B9_kCpKESDGmcJ8,2061
2
+ fotolab/__main__.py,sha256=aboOURPs_snOXTEWYR0q8oq1UTY9e-NxCd1j33V0wHI,833
3
+ fotolab/animate.py,sha256=ejimhTozo9DN7BbqqcV4x8zLnanZRKq1pxBBFeOdr6Q,2967
4
+ fotolab/auto.py,sha256=1Toxe8pA_tq15g1-imMFuHf1L94Ac7EthPTu7E8SAzE,2217
5
+ fotolab/border.py,sha256=5ch2d7LVPhB2OFuuXSW5ci6Cn967CPDQu0qSfaO7uMg,3591
6
+ fotolab/cli.py,sha256=YudfGpAdd7FlNk9GOoJvPuBOURRBqc-lzQz3cxlCH20,5008
7
+ fotolab/contrast.py,sha256=l7Bs5p8W8ypN9Cg3fFHnU-A20UwMKtjTiPk6D0PRwpM,2095
8
+ fotolab/env.py,sha256=NTTvfISWBBfIw5opWrUfg0BtkaAtdUtcISBAJC2gVUk,1449
9
+ fotolab/info.py,sha256=DawXTQJiQDBwy0Ml5Ysk8MvKga3ikp_aIw73AR3LdZo,1687
10
+ fotolab/montage.py,sha256=lUVY-zDSH7mwH-s34_XefdNp7CoDJHkwpbTUGiyJGgs,2037
11
+ fotolab/resize.py,sha256=cvPfh4wUfydM23Do7VnP6Bx2EqMHKfYFYrpiNhyWzCU,3259
12
+ fotolab/rotate.py,sha256=l_vQgf0IcI8AR1TSVsk4PrMZtJ3j_wpU77rKiGJ-KTA,1715
13
+ fotolab/sharpen.py,sha256=wUPtJdtB6mCRmcHrA0CoEVO0O0ROBJWhejTvUeL67QU,2655
14
+ fotolab/watermark.py,sha256=3yHqtrh6WtFcGArgrpKAL6849Y2R1Fk-g3FeKdejrz0,6513
15
+ fotolab-0.16.3.dist-info/entry_points.txt,sha256=mvw7AY_yZkIyjAxPtHNed9X99NZeLnMxEeAfEJUbrCM,44
16
+ fotolab-0.16.3.dist-info/LICENSE.md,sha256=tGtFDwxWTjuR9syrJoSv1Hiffd2u8Tu8cYClfrXS_YU,31956
17
+ fotolab-0.16.3.dist-info/WHEEL,sha256=Sgu64hAMa6g5FdzHxXv9Xdse9yxpGGMeagVtPMWpJQY,99
18
+ fotolab-0.16.3.dist-info/METADATA,sha256=Df4Afp009m7123lBt6H4rBQ3bvBstFPi7N3uPH2BeA8,10153
19
+ fotolab-0.16.3.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- fotolab/__init__.py,sha256=Z3guoQD823MHOmrtHNE9W0RnI6neYdRWNCv7QTxQb6s,2074
2
- fotolab/__main__.py,sha256=aboOURPs_snOXTEWYR0q8oq1UTY9e-NxCd1j33V0wHI,833
3
- fotolab/auto.py,sha256=1Toxe8pA_tq15g1-imMFuHf1L94Ac7EthPTu7E8SAzE,2217
4
- fotolab/border.py,sha256=5ch2d7LVPhB2OFuuXSW5ci6Cn967CPDQu0qSfaO7uMg,3591
5
- fotolab/cli.py,sha256=KoSDb752mewrtKE5wDs9icXF2McBVOKoEB98KYAV-jQ,4570
6
- fotolab/contrast.py,sha256=l7Bs5p8W8ypN9Cg3fFHnU-A20UwMKtjTiPk6D0PRwpM,2095
7
- fotolab/env.py,sha256=NTTvfISWBBfIw5opWrUfg0BtkaAtdUtcISBAJC2gVUk,1449
8
- fotolab/info.py,sha256=DawXTQJiQDBwy0Ml5Ysk8MvKga3ikp_aIw73AR3LdZo,1687
9
- fotolab/montage.py,sha256=lUVY-zDSH7mwH-s34_XefdNp7CoDJHkwpbTUGiyJGgs,2037
10
- fotolab/resize.py,sha256=y3JT2IZUOeWf4gjORtaJ_ZseklO2jG6XvR-d6EdhS0k,2242
11
- fotolab/sharpen.py,sha256=DsbIe4VU0Ty5oVzKnU70p9dMfhKr1i_UAQDimzAGX-Y,2655
12
- fotolab/watermark.py,sha256=6LeK5g6W7Gq5kpLinwqBCwFGCtp0Uz_hrqBE3BD_CEU,5210
13
- fotolab-0.13.0.dist-info/entry_points.txt,sha256=mvw7AY_yZkIyjAxPtHNed9X99NZeLnMxEeAfEJUbrCM,44
14
- fotolab-0.13.0.dist-info/LICENSE.md,sha256=tGtFDwxWTjuR9syrJoSv1Hiffd2u8Tu8cYClfrXS_YU,31956
15
- fotolab-0.13.0.dist-info/WHEEL,sha256=Sgu64hAMa6g5FdzHxXv9Xdse9yxpGGMeagVtPMWpJQY,99
16
- fotolab-0.13.0.dist-info/METADATA,sha256=v1RC94R2HI1BVbz7dyA3nAVuGso-OsRmDZQvrHnCfuU,9085
17
- fotolab-0.13.0.dist-info/RECORD,,