fotolab 0.14.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 +3 -3
- fotolab/cli.py +14 -0
- fotolab/resize.py +39 -5
- fotolab/rotate.py +61 -0
- fotolab/sharpen.py +3 -3
- fotolab/watermark.py +46 -7
- {fotolab-0.14.0.dist-info → fotolab-0.16.3.dist-info}/METADATA +23 -8
- fotolab-0.16.3.dist-info/RECORD +19 -0
- fotolab-0.14.0.dist-info/RECORD +0 -18
- {fotolab-0.14.0.dist-info → fotolab-0.16.3.dist-info}/LICENSE.md +0 -0
- {fotolab-0.14.0.dist-info → fotolab-0.16.3.dist-info}/WHEEL +0 -0
- {fotolab-0.14.0.dist-info → fotolab-0.16.3.dist-info}/entry_points.txt +0 -0
    
        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. | 
| 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 | 
| 67 | 
            +
                log.info("open image: %s", filename.resolve())
         | 
    
        fotolab/cli.py
    CHANGED
    
    | @@ -33,6 +33,7 @@ import fotolab.env | |
| 33 33 | 
             
            import fotolab.info
         | 
| 34 34 | 
             
            import fotolab.montage
         | 
| 35 35 | 
             
            import fotolab.resize
         | 
| 36 | 
            +
            import fotolab.rotate
         | 
| 36 37 | 
             
            import fotolab.sharpen
         | 
| 37 38 | 
             
            import fotolab.watermark
         | 
| 38 39 | 
             
            from fotolab import __version__
         | 
| @@ -42,6 +43,9 @@ log = logging.getLogger(__name__) | |
| 42 43 |  | 
| 43 44 | 
             
            def setup_logging(args: argparse.Namespace) -> None:
         | 
| 44 45 | 
             
                """Set up logging by level."""
         | 
| 46 | 
            +
                if args.verbose == 0:
         | 
| 47 | 
            +
                    logging.getLogger("PIL").setLevel(logging.ERROR)
         | 
| 48 | 
            +
             | 
| 45 49 | 
             
                if args.quiet:
         | 
| 46 50 | 
             
                    logging.disable(logging.NOTSET)
         | 
| 47 51 | 
             
                else:
         | 
| @@ -105,6 +109,15 @@ def build_parser() -> argparse.ArgumentParser: | |
| 105 109 | 
             
                    help="suppress all logging",
         | 
| 106 110 | 
             
                )
         | 
| 107 111 |  | 
| 112 | 
            +
                parser.add_argument(
         | 
| 113 | 
            +
                    "-v",
         | 
| 114 | 
            +
                    "--verbose",
         | 
| 115 | 
            +
                    default=0,
         | 
| 116 | 
            +
                    action="count",
         | 
| 117 | 
            +
                    dest="verbose",
         | 
| 118 | 
            +
                    help="show verbosity of debugging log, use -vv, -vvv for more details",
         | 
| 119 | 
            +
                )
         | 
| 120 | 
            +
             | 
| 108 121 | 
             
                parser.add_argument(
         | 
| 109 122 | 
             
                    "-d",
         | 
| 110 123 | 
             
                    "--debug",
         | 
| @@ -128,6 +141,7 @@ def build_parser() -> argparse.ArgumentParser: | |
| 128 141 | 
             
                fotolab.contrast.build_subparser(subparsers)
         | 
| 129 142 | 
             
                fotolab.info.build_subparser(subparsers)
         | 
| 130 143 | 
             
                fotolab.resize.build_subparser(subparsers)
         | 
| 144 | 
            +
                fotolab.rotate.build_subparser(subparsers)
         | 
| 131 145 | 
             
                fotolab.montage.build_subparser(subparsers)
         | 
| 132 146 | 
             
                fotolab.sharpen.build_subparser(subparsers)
         | 
| 133 147 | 
             
                fotolab.watermark.build_subparser(subparsers)
         | 
    
        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. | 
| 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= | 
| 55 | 
            +
                    default=DEFAULT_WIDTH,
         | 
| 50 56 | 
             
                    metavar="WIDTH",
         | 
| 51 57 | 
             
                )
         | 
| 52 58 |  | 
| 53 | 
            -
                 | 
| 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= | 
| 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 | 
            -
                        ( | 
| 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= | 
| 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= | 
| 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= | 
| 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( | 
| 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) =  | 
| 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 | 
| 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 | 
| 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  | 
| 171 | 
            -
                 | 
| 172 | 
            -
             | 
| 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. | 
| 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,9 +57,8 @@ fotolab -h | |
| 57 57 | 
             
            ```
         | 
| 58 58 |  | 
| 59 59 | 
             
            ```console
         | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
                           {animate,auto,border,contrast,info,resize,montage,sharpen,watermark,env}
         | 
| 60 | 
            +
            usage: fotolab [-h] [-o] [-op] [-od OUTPUT_DIR] [-q] [-v] [-d] [-V]
         | 
| 61 | 
            +
                           {animate,auto,border,contrast,info,resize,rotate,montage,sharpen,watermark,env}
         | 
| 63 62 | 
             
                           ...
         | 
| 64 63 |  | 
| 65 64 | 
             
            A console program to manipulate photos.
         | 
| @@ -69,7 +68,7 @@ A console program to manipulate photos. | |
| 69 68 | 
             
              issues: https://github.com/kianmeng/fotolab/issues
         | 
| 70 69 |  | 
| 71 70 | 
             
            positional arguments:
         | 
| 72 | 
            -
              {animate,auto,border,contrast,info,resize,montage,sharpen,watermark,env}
         | 
| 71 | 
            +
              {animate,auto,border,contrast,info,resize,rotate,montage,sharpen,watermark,env}
         | 
| 73 72 | 
             
                                sub-command help
         | 
| 74 73 | 
             
                animate         animate an image
         | 
| 75 74 | 
             
                auto            auto adjust (resize, contrast, and watermark) a photo
         | 
| @@ -77,6 +76,7 @@ positional arguments: | |
| 77 76 | 
             
                contrast        contrast an image
         | 
| 78 77 | 
             
                info            info an image
         | 
| 79 78 | 
             
                resize          resize an image
         | 
| 79 | 
            +
                rotate          rotate an image
         | 
| 80 80 | 
             
                montage         montage a list of image
         | 
| 81 81 | 
             
                sharpen         sharpen an image
         | 
| 82 82 | 
             
                watermark       watermark an image
         | 
| @@ -89,6 +89,7 @@ optional arguments: | |
| 89 89 | 
             
              -od OUTPUT_DIR, --output-dir OUTPUT_DIR
         | 
| 90 90 | 
             
                                set default output folder (default: 'output')
         | 
| 91 91 | 
             
              -q, --quiet       suppress all logging
         | 
| 92 | 
            +
              -v, --verbose     show verbosity of debugging log, use -vv, -vvv for more details
         | 
| 92 93 | 
             
              -d, --debug       show debugging log and stacktrace
         | 
| 93 94 | 
             
              -V, --version     show program's version number and exit
         | 
| 94 95 | 
             
            ```
         | 
| @@ -100,7 +101,6 @@ fotolab animate -h | |
| 100 101 | 
             
            ```
         | 
| 101 102 |  | 
| 102 103 | 
             
            ```console
         | 
| 103 | 
            -
             | 
| 104 104 | 
             
            usage: fotolab animate [-h] [-f FORMAT] [-d DURATION] [-l LOOP]
         | 
| 105 105 | 
             
                                   IMAGE_FILENAMES [IMAGE_FILENAMES ...]
         | 
| 106 106 |  | 
| @@ -197,6 +197,22 @@ optional arguments: | |
| 197 197 | 
             
              -h, --help      show this help message and exit
         | 
| 198 198 | 
             
            ```
         | 
| 199 199 |  | 
| 200 | 
            +
            ### fotolab rotate
         | 
| 201 | 
            +
             | 
| 202 | 
            +
            ```console
         | 
| 203 | 
            +
            fotolab rotate -h
         | 
| 204 | 
            +
            ```
         | 
| 205 | 
            +
             | 
| 206 | 
            +
            ```console
         | 
| 207 | 
            +
            usage: fotolab rotate [-h] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
         | 
| 208 | 
            +
             | 
| 209 | 
            +
            positional arguments:
         | 
| 210 | 
            +
              IMAGE_FILENAMES  set the image filenames
         | 
| 211 | 
            +
             | 
| 212 | 
            +
            optional arguments:
         | 
| 213 | 
            +
              -h, --help       show this help message and exit
         | 
| 214 | 
            +
            ```
         | 
| 215 | 
            +
             | 
| 200 216 | 
             
            ### fotolab montage
         | 
| 201 217 |  | 
| 202 218 | 
             
            ```console
         | 
| @@ -204,7 +220,6 @@ fotolab montage -h | |
| 204 220 | 
             
            ```
         | 
| 205 221 |  | 
| 206 222 | 
             
            ```console
         | 
| 207 | 
            -
             | 
| 208 223 | 
             
            usage: fotolab montage [-h] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
         | 
| 209 224 |  | 
| 210 225 | 
             
            positional arguments:
         | 
| @@ -221,7 +236,7 @@ fotolab resize -h | |
| 221 236 | 
             
            ```
         | 
| 222 237 |  | 
| 223 238 | 
             
            ```console
         | 
| 224 | 
            -
            usage: fotolab resize [-h] [-wh WIDTH | 
| 239 | 
            +
            usage: fotolab resize [-h] [-wh WIDTH | -ht HEIGHT]
         | 
| 225 240 | 
             
                                  IMAGE_FILENAMES [IMAGE_FILENAMES ...]
         | 
| 226 241 |  | 
| 227 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,,
         | 
    
        fotolab-0.14.0.dist-info/RECORD
    DELETED
    
    | @@ -1,18 +0,0 @@ | |
| 1 | 
            -
            fotolab/__init__.py,sha256=cpDAMSgpSA1exqkB5BGL2OrPVQfTzxV8nOTS5iP0rUE,2074
         | 
| 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=ZbXkftGAIrlcOgWju6KIeCBA8-EDVop2mkRN5f1Sa34,4641
         | 
| 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=y3JT2IZUOeWf4gjORtaJ_ZseklO2jG6XvR-d6EdhS0k,2242
         | 
| 12 | 
            -
            fotolab/sharpen.py,sha256=DsbIe4VU0Ty5oVzKnU70p9dMfhKr1i_UAQDimzAGX-Y,2655
         | 
| 13 | 
            -
            fotolab/watermark.py,sha256=6LeK5g6W7Gq5kpLinwqBCwFGCtp0Uz_hrqBE3BD_CEU,5210
         | 
| 14 | 
            -
            fotolab-0.14.0.dist-info/entry_points.txt,sha256=mvw7AY_yZkIyjAxPtHNed9X99NZeLnMxEeAfEJUbrCM,44
         | 
| 15 | 
            -
            fotolab-0.14.0.dist-info/LICENSE.md,sha256=tGtFDwxWTjuR9syrJoSv1Hiffd2u8Tu8cYClfrXS_YU,31956
         | 
| 16 | 
            -
            fotolab-0.14.0.dist-info/WHEEL,sha256=Sgu64hAMa6g5FdzHxXv9Xdse9yxpGGMeagVtPMWpJQY,99
         | 
| 17 | 
            -
            fotolab-0.14.0.dist-info/METADATA,sha256=jHoSNuIcWosz5li1NRXRTHRxpIFX3sjPv11jFAi7kMQ,9744
         | 
| 18 | 
            -
            fotolab-0.14.0.dist-info/RECORD,,
         | 
| 
            File without changes
         | 
| 
            File without changes
         | 
| 
            File without changes
         |