fotolab 0.9.1__py2.py3-none-any.whl → 0.14.0__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 +7 -7
- fotolab/animate.py +114 -0
- fotolab/auto.py +4 -2
- fotolab/border.py +27 -25
- fotolab/cli.py +7 -1
- fotolab/contrast.py +9 -5
- fotolab/info.py +58 -0
- fotolab/montage.py +71 -0
- fotolab/resize.py +10 -8
- fotolab/sharpen.py +11 -9
- fotolab/watermark.py +53 -33
- {fotolab-0.9.1.dist-info → fotolab-0.14.0.dist-info}/METADATA +129 -17
- fotolab-0.14.0.dist-info/RECORD +18 -0
- fotolab-0.9.1.dist-info/RECORD +0 -15
- {fotolab-0.9.1.dist-info → fotolab-0.14.0.dist-info}/LICENSE.md +0 -0
- {fotolab-0.9.1.dist-info → fotolab-0.14.0.dist-info}/WHEEL +0 -0
- {fotolab-0.9.1.dist-info → fotolab-0.14.0.dist-info}/entry_points.txt +0 -0
    
        fotolab/__init__.py
    CHANGED
    
    | @@ -21,34 +21,34 @@ import subprocess | |
| 21 21 | 
             
            import sys
         | 
| 22 22 | 
             
            from pathlib import Path
         | 
| 23 23 |  | 
| 24 | 
            -
            __version__ = "0. | 
| 24 | 
            +
            __version__ = "0.14.0"
         | 
| 25 25 |  | 
| 26 26 | 
             
            log = logging.getLogger(__name__)
         | 
| 27 27 |  | 
| 28 28 |  | 
| 29 | 
            -
            def save_image(args, new_image):
         | 
| 30 | 
            -
                """ | 
| 29 | 
            +
            def save_image(args, new_image, output_filename, subcommand):
         | 
| 30 | 
            +
                """Save image after image operation.
         | 
| 31 31 |  | 
| 32 32 | 
             
                Args:
         | 
| 33 33 | 
             
                    args (argparse.Namespace): Config from command line arguments
         | 
| 34 34 | 
             
                    new_image(PIL.Image.Image): Modified image
         | 
| 35 | 
            +
                    subcommand(str): Subcommand used to call this function
         | 
| 35 36 |  | 
| 36 37 | 
             
                Returns:
         | 
| 37 38 | 
             
                    None
         | 
| 38 39 | 
             
                """
         | 
| 39 | 
            -
                 | 
| 40 | 
            -
                image_file = Path(args.image_filename)
         | 
| 40 | 
            +
                image_file = Path(output_filename)
         | 
| 41 41 |  | 
| 42 42 | 
             
                if args.overwrite:
         | 
| 43 43 | 
             
                    new_filename = image_file.with_name(image_file.name)
         | 
| 44 44 | 
             
                else:
         | 
| 45 45 | 
             
                    new_filename = Path(
         | 
| 46 46 | 
             
                        args.output_dir,
         | 
| 47 | 
            -
                        image_file.with_name(f"{ | 
| 47 | 
            +
                        image_file.with_name(f"{subcommand}_{image_file.name}"),
         | 
| 48 48 | 
             
                    )
         | 
| 49 49 | 
             
                    new_filename.parent.mkdir(parents=True, exist_ok=True)
         | 
| 50 50 |  | 
| 51 | 
            -
                log.info("%s image: %s",  | 
| 51 | 
            +
                log.info("%s image: %s", subcommand, new_filename)
         | 
| 52 52 | 
             
                new_image.save(new_filename)
         | 
| 53 53 |  | 
| 54 54 | 
             
                if args.open:
         | 
    
        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/auto.py
    CHANGED
    
    | @@ -35,11 +35,12 @@ def build_subparser(subparsers) -> None: | |
| 35 35 | 
             
                auto_parser.set_defaults(func=run)
         | 
| 36 36 |  | 
| 37 37 | 
             
                auto_parser.add_argument(
         | 
| 38 | 
            -
                    dest=" | 
| 38 | 
            +
                    dest="image_filenames",
         | 
| 39 39 | 
             
                    help="set the image filename",
         | 
| 40 | 
            +
                    nargs="+",
         | 
| 40 41 | 
             
                    type=str,
         | 
| 41 42 | 
             
                    default=None,
         | 
| 42 | 
            -
                    metavar=" | 
| 43 | 
            +
                    metavar="IMAGE_FILENAMES",
         | 
| 43 44 | 
             
                )
         | 
| 44 45 |  | 
| 45 46 |  | 
| @@ -65,6 +66,7 @@ def run(args: argparse.Namespace) -> None: | |
| 65 66 | 
             
                    "font_color": "white",
         | 
| 66 67 | 
             
                    "outline_width": 2,
         | 
| 67 68 | 
             
                    "outline_color": "black",
         | 
| 69 | 
            +
                    "padding": 15,
         | 
| 68 70 | 
             
                }
         | 
| 69 71 | 
             
                combined_args = argparse.Namespace(**vars(args), **extra_args)
         | 
| 70 72 | 
             
                combined_args.overwrite = True
         | 
    
        fotolab/border.py
    CHANGED
    
    | @@ -32,11 +32,12 @@ def build_subparser(subparsers) -> None: | |
| 32 32 | 
             
                border_parser.set_defaults(func=run)
         | 
| 33 33 |  | 
| 34 34 | 
             
                border_parser.add_argument(
         | 
| 35 | 
            -
                    dest=" | 
| 36 | 
            -
                    help="set the image  | 
| 35 | 
            +
                    dest="image_filenames",
         | 
| 36 | 
            +
                    help="set the image filenames",
         | 
| 37 | 
            +
                    nargs="+",
         | 
| 37 38 | 
             
                    type=str,
         | 
| 38 39 | 
             
                    default=None,
         | 
| 39 | 
            -
                    metavar=" | 
| 40 | 
            +
                    metavar="IMAGE_FILENAMES",
         | 
| 40 41 | 
             
                )
         | 
| 41 42 |  | 
| 42 43 | 
             
                border_parser.add_argument(
         | 
| @@ -111,27 +112,28 @@ def run(args: argparse.Namespace) -> None: | |
| 111 112 | 
             
                """
         | 
| 112 113 | 
             
                log.debug(args)
         | 
| 113 114 |  | 
| 114 | 
            -
                 | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 117 | 
            -
                     | 
| 118 | 
            -
             | 
| 119 | 
            -
             | 
| 120 | 
            -
             | 
| 121 | 
            -
             | 
| 122 | 
            -
                     | 
| 123 | 
            -
                         | 
| 124 | 
            -
             | 
| 125 | 
            -
             | 
| 126 | 
            -
             | 
| 115 | 
            +
                for image_filename in args.image_filenames:
         | 
| 116 | 
            +
                    original_image = Image.open(image_filename)
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                    if (
         | 
| 119 | 
            +
                        args.width_left
         | 
| 120 | 
            +
                        or args.width_top
         | 
| 121 | 
            +
                        or args.width_right
         | 
| 122 | 
            +
                        or args.width_bottom
         | 
| 123 | 
            +
                    ):
         | 
| 124 | 
            +
                        border = (
         | 
| 125 | 
            +
                            int(args.width_left),
         | 
| 126 | 
            +
                            int(args.width_top),
         | 
| 127 | 
            +
                            int(args.width_right),
         | 
| 128 | 
            +
                            int(args.width_bottom),
         | 
| 129 | 
            +
                        )
         | 
| 130 | 
            +
                    else:
         | 
| 131 | 
            +
                        border = args.width
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                    bordered_image = ImageOps.expand(
         | 
| 134 | 
            +
                        original_image,
         | 
| 135 | 
            +
                        border=border,
         | 
| 136 | 
            +
                        fill=ImageColor.getrgb(args.color),
         | 
| 127 137 | 
             
                    )
         | 
| 128 | 
            -
                else:
         | 
| 129 | 
            -
                    border = args.width
         | 
| 130 138 |  | 
| 131 | 
            -
             | 
| 132 | 
            -
                    original_image,
         | 
| 133 | 
            -
                    border=border,
         | 
| 134 | 
            -
                    fill=ImageColor.getrgb(args.color),
         | 
| 135 | 
            -
                )
         | 
| 136 | 
            -
             | 
| 137 | 
            -
                save_image(args, bordered_image)
         | 
| 139 | 
            +
                    save_image(args, bordered_image, image_filename, "watermark")
         | 
    
        fotolab/cli.py
    CHANGED
    
    | @@ -25,10 +25,13 @@ 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
         | 
| 31 32 | 
             
            import fotolab.env
         | 
| 33 | 
            +
            import fotolab.info
         | 
| 34 | 
            +
            import fotolab.montage
         | 
| 32 35 | 
             
            import fotolab.resize
         | 
| 33 36 | 
             
            import fotolab.sharpen
         | 
| 34 37 | 
             
            import fotolab.watermark
         | 
| @@ -82,7 +85,7 @@ def build_parser() -> argparse.ArgumentParser: | |
| 82 85 | 
             
                    default=False,
         | 
| 83 86 | 
             
                    action="store_true",
         | 
| 84 87 | 
             
                    dest="open",
         | 
| 85 | 
            -
                    help="open the image using default program (default: '%(default)s'",
         | 
| 88 | 
            +
                    help="open the image using default program (default: '%(default)s')",
         | 
| 86 89 | 
             
                )
         | 
| 87 90 |  | 
| 88 91 | 
             
                parser.add_argument(
         | 
| @@ -119,10 +122,13 @@ def build_parser() -> argparse.ArgumentParser: | |
| 119 122 | 
             
                )
         | 
| 120 123 |  | 
| 121 124 | 
             
                subparsers = parser.add_subparsers(help="sub-command help")
         | 
| 125 | 
            +
                fotolab.animate.build_subparser(subparsers)
         | 
| 122 126 | 
             
                fotolab.auto.build_subparser(subparsers)
         | 
| 123 127 | 
             
                fotolab.border.build_subparser(subparsers)
         | 
| 124 128 | 
             
                fotolab.contrast.build_subparser(subparsers)
         | 
| 129 | 
            +
                fotolab.info.build_subparser(subparsers)
         | 
| 125 130 | 
             
                fotolab.resize.build_subparser(subparsers)
         | 
| 131 | 
            +
                fotolab.montage.build_subparser(subparsers)
         | 
| 126 132 | 
             
                fotolab.sharpen.build_subparser(subparsers)
         | 
| 127 133 | 
             
                fotolab.watermark.build_subparser(subparsers)
         | 
| 128 134 | 
             
                fotolab.env.build_subparser(subparsers)
         | 
    
        fotolab/contrast.py
    CHANGED
    
    | @@ -34,11 +34,12 @@ def build_subparser(subparsers) -> None: | |
| 34 34 | 
             
                contrast_parser.set_defaults(func=run)
         | 
| 35 35 |  | 
| 36 36 | 
             
                contrast_parser.add_argument(
         | 
| 37 | 
            -
                    dest=" | 
| 37 | 
            +
                    dest="image_filenames",
         | 
| 38 38 | 
             
                    help="set the image filename",
         | 
| 39 | 
            +
                    nargs="+",
         | 
| 39 40 | 
             
                    type=str,
         | 
| 40 41 | 
             
                    default=None,
         | 
| 41 | 
            -
                    metavar=" | 
| 42 | 
            +
                    metavar="IMAGE_FILENAMES",
         | 
| 42 43 | 
             
                )
         | 
| 43 44 |  | 
| 44 45 | 
             
                contrast_parser.add_argument(
         | 
| @@ -67,7 +68,10 @@ def run(args: argparse.Namespace) -> None: | |
| 67 68 | 
             
                """
         | 
| 68 69 | 
             
                log.debug(args)
         | 
| 69 70 |  | 
| 70 | 
            -
                 | 
| 71 | 
            -
             | 
| 71 | 
            +
                for image_filename in args.image_filenames:
         | 
| 72 | 
            +
                    original_image = Image.open(image_filename)
         | 
| 73 | 
            +
                    contrast_image = ImageOps.autocontrast(
         | 
| 74 | 
            +
                        original_image, cutoff=args.cutoff
         | 
| 75 | 
            +
                    )
         | 
| 72 76 |  | 
| 73 | 
            -
             | 
| 77 | 
            +
                    save_image(args, contrast_image, image_filename, "contrast")
         | 
    
        fotolab/info.py
    ADDED
    
    | @@ -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}")
         | 
    
        fotolab/montage.py
    ADDED
    
    | @@ -0,0 +1,71 @@ | |
| 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 | 
            +
            """Montage 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 | 
            +
                montage_parser = subparsers.add_parser(
         | 
| 31 | 
            +
                    "montage", help="montage a list of image"
         | 
| 32 | 
            +
                )
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                montage_parser.set_defaults(func=run)
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                montage_parser.add_argument(
         | 
| 37 | 
            +
                    dest="image_filenames",
         | 
| 38 | 
            +
                    help="set the image filenames",
         | 
| 39 | 
            +
                    nargs="+",
         | 
| 40 | 
            +
                    type=argparse.FileType("r"),
         | 
| 41 | 
            +
                    default=None,
         | 
| 42 | 
            +
                    metavar="IMAGE_FILENAMES",
         | 
| 43 | 
            +
                )
         | 
| 44 | 
            +
             | 
| 45 | 
            +
             | 
| 46 | 
            +
            def run(args: argparse.Namespace) -> None:
         | 
| 47 | 
            +
                """Run montage subcommand.
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                Args:
         | 
| 50 | 
            +
                    config (argparse.Namespace): Config from command line arguments
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                Returns:
         | 
| 53 | 
            +
                    None
         | 
| 54 | 
            +
                """
         | 
| 55 | 
            +
                log.debug(args)
         | 
| 56 | 
            +
                images = []
         | 
| 57 | 
            +
                for image_filename in args.image_filenames:
         | 
| 58 | 
            +
                    images.append(Image.open(image_filename.name))
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                total_width = sum(img.width for img in images)
         | 
| 61 | 
            +
                total_height = max(img.height for img in images)
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                montaged_image = Image.new("RGB", (total_width, total_height))
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                x_offset = 0
         | 
| 66 | 
            +
                for img in images:
         | 
| 67 | 
            +
                    montaged_image.paste(img, (x_offset, 0))
         | 
| 68 | 
            +
                    x_offset += img.width
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                output_image_filename = args.image_filenames[0].name
         | 
| 71 | 
            +
                save_image(args, montaged_image, output_image_filename, "montage")
         | 
    
        fotolab/resize.py
    CHANGED
    
    | @@ -32,11 +32,12 @@ def build_subparser(subparsers) -> None: | |
| 32 32 | 
             
                resize_parser.set_defaults(func=run)
         | 
| 33 33 |  | 
| 34 34 | 
             
                resize_parser.add_argument(
         | 
| 35 | 
            -
                    dest=" | 
| 35 | 
            +
                    dest="image_filenames",
         | 
| 36 36 | 
             
                    help="set the image filename",
         | 
| 37 | 
            +
                    nargs="+",
         | 
| 37 38 | 
             
                    type=str,
         | 
| 38 39 | 
             
                    default=None,
         | 
| 39 | 
            -
                    metavar=" | 
| 40 | 
            +
                    metavar="IMAGE_FILENAMES",
         | 
| 40 41 | 
             
                )
         | 
| 41 42 |  | 
| 42 43 | 
             
                resize_parser.add_argument(
         | 
| @@ -71,10 +72,11 @@ def run(args: argparse.Namespace) -> None: | |
| 71 72 | 
             
                """
         | 
| 72 73 | 
             
                log.debug(args)
         | 
| 73 74 |  | 
| 74 | 
            -
                 | 
| 75 | 
            -
             | 
| 76 | 
            -
             | 
| 77 | 
            -
                     | 
| 78 | 
            -
             | 
| 75 | 
            +
                for image_filename in args.image_filenames:
         | 
| 76 | 
            +
                    original_image = Image.open(image_filename)
         | 
| 77 | 
            +
                    resized_image = original_image.copy()
         | 
| 78 | 
            +
                    resized_image = resized_image.resize(
         | 
| 79 | 
            +
                        (args.width, args.height), Image.Resampling.LANCZOS
         | 
| 80 | 
            +
                    )
         | 
| 79 81 |  | 
| 80 | 
            -
             | 
| 82 | 
            +
                    save_image(args, resized_image, image_filename, "resize")
         | 
    
        fotolab/sharpen.py
    CHANGED
    
    | @@ -32,11 +32,12 @@ def build_subparser(subparsers) -> None: | |
| 32 32 | 
             
                sharpen_parser.set_defaults(func=run)
         | 
| 33 33 |  | 
| 34 34 | 
             
                sharpen_parser.add_argument(
         | 
| 35 | 
            -
                    dest=" | 
| 36 | 
            -
                    help="set the image  | 
| 35 | 
            +
                    dest="image_filenames",
         | 
| 36 | 
            +
                    help="set the image filenames",
         | 
| 37 | 
            +
                    nargs="+",
         | 
| 37 38 | 
             
                    type=str,
         | 
| 38 39 | 
             
                    default=None,
         | 
| 39 | 
            -
                    metavar=" | 
| 40 | 
            +
                    metavar="IMAGE_FILENAMES",
         | 
| 40 41 | 
             
                )
         | 
| 41 42 |  | 
| 42 43 | 
             
                sharpen_parser.add_argument(
         | 
| @@ -87,10 +88,11 @@ def run(args: argparse.Namespace) -> None: | |
| 87 88 | 
             
                """
         | 
| 88 89 | 
             
                log.debug(args)
         | 
| 89 90 |  | 
| 90 | 
            -
                 | 
| 91 | 
            -
             | 
| 92 | 
            -
                     | 
| 93 | 
            -
                         | 
| 91 | 
            +
                for image_filename in args.image_filenames:
         | 
| 92 | 
            +
                    original_image = Image.open(image_filename)
         | 
| 93 | 
            +
                    sharpen_image = original_image.filter(
         | 
| 94 | 
            +
                        ImageFilter.UnsharpMask(
         | 
| 95 | 
            +
                            args.radius, percent=args.percent, threshold=args.threshold
         | 
| 96 | 
            +
                        )
         | 
| 94 97 | 
             
                    )
         | 
| 95 | 
            -
             | 
| 96 | 
            -
                save_image(args, sharpen_image)
         | 
| 98 | 
            +
                    save_image(args, sharpen_image, image_filename, "sharpen")
         | 
    
        fotolab/watermark.py
    CHANGED
    
    | @@ -36,11 +36,12 @@ def build_subparser(subparsers) -> None: | |
| 36 36 | 
             
                watermark_parser.set_defaults(func=run)
         | 
| 37 37 |  | 
| 38 38 | 
             
                watermark_parser.add_argument(
         | 
| 39 | 
            -
                    dest=" | 
| 40 | 
            -
                    help="set the image  | 
| 39 | 
            +
                    dest="image_filenames",
         | 
| 40 | 
            +
                    help="set the image filenames",
         | 
| 41 | 
            +
                    nargs="+",
         | 
| 41 42 | 
             
                    type=str,
         | 
| 42 43 | 
             
                    default=None,
         | 
| 43 | 
            -
                    metavar=" | 
| 44 | 
            +
                    metavar="IMAGE_FILENAMES",
         | 
| 44 45 | 
             
                )
         | 
| 45 46 |  | 
| 46 47 | 
             
                watermark_parser.add_argument(
         | 
| @@ -62,6 +63,19 @@ def build_subparser(subparsers) -> None: | |
| 62 63 | 
             
                    default="bottom-left",
         | 
| 63 64 | 
             
                )
         | 
| 64 65 |  | 
| 66 | 
            +
                watermark_parser.add_argument(
         | 
| 67 | 
            +
                    "-pd",
         | 
| 68 | 
            +
                    "--padding",
         | 
| 69 | 
            +
                    dest="padding",
         | 
| 70 | 
            +
                    type=int,
         | 
| 71 | 
            +
                    default=15,
         | 
| 72 | 
            +
                    help=(
         | 
| 73 | 
            +
                        "set the padding of the watermark text relative to the image "
         | 
| 74 | 
            +
                        "(default: '%(default)s')"
         | 
| 75 | 
            +
                    ),
         | 
| 76 | 
            +
                    metavar="PADDING",
         | 
| 77 | 
            +
                )
         | 
| 78 | 
            +
             | 
| 65 79 | 
             
                watermark_parser.add_argument(
         | 
| 66 80 | 
             
                    "-fs",
         | 
| 67 81 | 
             
                    "--font-size",
         | 
| @@ -120,37 +134,43 @@ def run(args: argparse.Namespace) -> None: | |
| 120 134 | 
             
                """
         | 
| 121 135 | 
             
                log.debug(args)
         | 
| 122 136 |  | 
| 123 | 
            -
                 | 
| 124 | 
            -
             | 
| 125 | 
            -
             | 
| 126 | 
            -
             | 
| 127 | 
            -
             | 
| 128 | 
            -
             | 
| 129 | 
            -
             | 
| 130 | 
            -
             | 
| 131 | 
            -
                     | 
| 132 | 
            -
             | 
| 133 | 
            -
             | 
| 134 | 
            -
             | 
| 135 | 
            -
             | 
| 136 | 
            -
                     | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 139 | 
            -
             | 
| 140 | 
            -
             | 
| 141 | 
            -
             | 
| 142 | 
            -
                     | 
| 143 | 
            -
             | 
| 144 | 
            -
                     | 
| 145 | 
            -
             | 
| 146 | 
            -
             | 
| 147 | 
            -
             | 
| 148 | 
            -
             | 
| 149 | 
            -
             | 
| 150 | 
            -
             | 
| 151 | 
            -
             | 
| 137 | 
            +
                for image_filename in args.image_filenames:
         | 
| 138 | 
            +
                    original_image = Image.open(image_filename)
         | 
| 139 | 
            +
                    watermarked_image = original_image.copy()
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                    draw = ImageDraw.Draw(watermarked_image)
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                    font = ImageFont.truetype("arial.ttf", args.font_size)
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                    (left, top, right, bottom) = draw.textbbox(
         | 
| 146 | 
            +
                        xy=(0, 0), text=args.text, font=font
         | 
| 147 | 
            +
                    )
         | 
| 148 | 
            +
                    text_width = right - left
         | 
| 149 | 
            +
                    text_height = bottom - top
         | 
| 150 | 
            +
                    (position_x, position_y) = calculate_position(
         | 
| 151 | 
            +
                        watermarked_image,
         | 
| 152 | 
            +
                        text_width,
         | 
| 153 | 
            +
                        text_height,
         | 
| 154 | 
            +
                        args.position,
         | 
| 155 | 
            +
                        args.padding,
         | 
| 156 | 
            +
                    )
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                    draw.text(
         | 
| 159 | 
            +
                        (position_x, position_y),
         | 
| 160 | 
            +
                        args.text,
         | 
| 161 | 
            +
                        font=font,
         | 
| 162 | 
            +
                        fill=(*ImageColor.getrgb(args.font_color), 128),
         | 
| 163 | 
            +
                        stroke_width=args.outline_width,
         | 
| 164 | 
            +
                        stroke_fill=(*ImageColor.getrgb(args.outline_color), 128),
         | 
| 165 | 
            +
                    )
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                    save_image(args, watermarked_image, image_filename, "watermark")
         | 
| 168 | 
            +
             | 
| 169 | 
            +
             | 
| 170 | 
            +
            def calculate_position(
         | 
| 171 | 
            +
                image, text_width, text_height, position, padding
         | 
| 172 | 
            +
            ) -> tuple:
         | 
| 152 173 | 
             
                """Calculate the boundary coordinates of the watermark text."""
         | 
| 153 | 
            -
                padding = 10
         | 
| 154 174 | 
             
                if position == "top-left":
         | 
| 155 175 | 
             
                    position_x = 0 + padding
         | 
| 156 176 | 
             
                    position_y = 0 + padding
         | 
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            Metadata-Version: 2.1
         | 
| 2 2 | 
             
            Name: fotolab
         | 
| 3 | 
            -
            Version: 0. | 
| 3 | 
            +
            Version: 0.14.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>
         | 
| @@ -59,7 +59,8 @@ fotolab -h | |
| 59 59 | 
             
            ```console
         | 
| 60 60 |  | 
| 61 61 | 
             
            usage: fotolab [-h] [-o] [-op] [-od OUTPUT_DIR] [-q] [-d] [-V]
         | 
| 62 | 
            -
                           {auto,border,contrast,resize,sharpen,watermark,env} | 
| 62 | 
            +
                           {animate,auto,border,contrast,info,resize,montage,sharpen,watermark,env}
         | 
| 63 | 
            +
                           ...
         | 
| 63 64 |  | 
| 64 65 | 
             
            A console program to manipulate photos.
         | 
| 65 66 |  | 
| @@ -68,12 +69,15 @@ A console program to manipulate photos. | |
| 68 69 | 
             
              issues: https://github.com/kianmeng/fotolab/issues
         | 
| 69 70 |  | 
| 70 71 | 
             
            positional arguments:
         | 
| 71 | 
            -
              {auto,border,contrast,resize,sharpen,watermark,env}
         | 
| 72 | 
            +
              {animate,auto,border,contrast,info,resize,montage,sharpen,watermark,env}
         | 
| 72 73 | 
             
                                sub-command help
         | 
| 74 | 
            +
                animate         animate an image
         | 
| 73 75 | 
             
                auto            auto adjust (resize, contrast, and watermark) a photo
         | 
| 74 76 | 
             
                border          add border to image
         | 
| 75 77 | 
             
                contrast        contrast an image
         | 
| 78 | 
            +
                info            info an image
         | 
| 76 79 | 
             
                resize          resize an image
         | 
| 80 | 
            +
                montage         montage a list of image
         | 
| 77 81 | 
             
                sharpen         sharpen an image
         | 
| 78 82 | 
             
                watermark       watermark an image
         | 
| 79 83 | 
             
                env             print environment information for bug reporting
         | 
| @@ -81,7 +85,7 @@ positional arguments: | |
| 81 85 | 
             
            optional arguments:
         | 
| 82 86 | 
             
              -h, --help        show this help message and exit
         | 
| 83 87 | 
             
              -o, --overwrite   overwrite existing image
         | 
| 84 | 
            -
              -op, --open       open the image using default program (default: 'False'
         | 
| 88 | 
            +
              -op, --open       open the image using default program (default: 'False')
         | 
| 85 89 | 
             
              -od OUTPUT_DIR, --output-dir OUTPUT_DIR
         | 
| 86 90 | 
             
                                set default output folder (default: 'output')
         | 
| 87 91 | 
             
              -q, --quiet       suppress all logging
         | 
| @@ -89,6 +93,45 @@ optional arguments: | |
| 89 93 | 
             
              -V, --version     show program's version number and exit
         | 
| 90 94 | 
             
            ```
         | 
| 91 95 |  | 
| 96 | 
            +
            ### fotolab animate
         | 
| 97 | 
            +
             | 
| 98 | 
            +
            ```console
         | 
| 99 | 
            +
            fotolab animate -h
         | 
| 100 | 
            +
            ```
         | 
| 101 | 
            +
             | 
| 102 | 
            +
            ```console
         | 
| 103 | 
            +
             | 
| 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 | 
            +
             | 
| 119 | 
            +
            ### fotolab auto
         | 
| 120 | 
            +
             | 
| 121 | 
            +
            ```console
         | 
| 122 | 
            +
            fotolab auto -h
         | 
| 123 | 
            +
            ```
         | 
| 124 | 
            +
             | 
| 125 | 
            +
            ```console
         | 
| 126 | 
            +
            usage: fotolab auto [-h] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
         | 
| 127 | 
            +
             | 
| 128 | 
            +
            positional arguments:
         | 
| 129 | 
            +
              IMAGE_FILENAMES  set the image filename
         | 
| 130 | 
            +
             | 
| 131 | 
            +
            optional arguments:
         | 
| 132 | 
            +
              -h, --help       show this help message and exit
         | 
| 133 | 
            +
            ```
         | 
| 134 | 
            +
             | 
| 92 135 | 
             
            ### fotolab border
         | 
| 93 136 |  | 
| 94 137 | 
             
            ```console
         | 
| @@ -98,12 +141,12 @@ fotolab border -h | |
| 98 141 | 
             
            ```console
         | 
| 99 142 | 
             
            usage: fotolab border [-h] [-c COLOR] [-w WIDTH] [-wt WIDTH] [-wr WIDTH]
         | 
| 100 143 | 
             
                                  [-wb WIDTH] [-wl WIDTH]
         | 
| 101 | 
            -
                                   | 
| 144 | 
            +
                                  IMAGE_FILENAMES [IMAGE_FILENAMES ...]
         | 
| 102 145 |  | 
| 103 146 | 
             
            positional arguments:
         | 
| 104 | 
            -
               | 
| 147 | 
            +
              IMAGE_FILENAMES       set the image filenames
         | 
| 105 148 |  | 
| 106 | 
            -
             | 
| 149 | 
            +
            optional arguments:
         | 
| 107 150 | 
             
              -h, --help            show this help message and exit
         | 
| 108 151 | 
             
              -c COLOR, --color COLOR
         | 
| 109 152 | 
             
                                    set the color of border (default: 'black')
         | 
| @@ -126,7 +169,26 @@ fotolab contrast -h | |
| 126 169 | 
             
            ```
         | 
| 127 170 |  | 
| 128 171 | 
             
            ```console
         | 
| 129 | 
            -
            usage: fotolab contrast [-h]  | 
| 172 | 
            +
            usage: fotolab contrast [-h] [-c CUTOFF] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
         | 
| 173 | 
            +
             | 
| 174 | 
            +
            positional arguments:
         | 
| 175 | 
            +
              IMAGE_FILENAMES       set the image filename
         | 
| 176 | 
            +
             | 
| 177 | 
            +
            optional arguments:
         | 
| 178 | 
            +
              -h, --help            show this help message and exit
         | 
| 179 | 
            +
              -c CUTOFF, --cutoff CUTOFF
         | 
| 180 | 
            +
                                    set the percentage of lightest or darkest pixels to
         | 
| 181 | 
            +
                                    discard from histogram (default: '1')
         | 
| 182 | 
            +
            ```
         | 
| 183 | 
            +
             | 
| 184 | 
            +
            ### fotolab info
         | 
| 185 | 
            +
             | 
| 186 | 
            +
            ```console
         | 
| 187 | 
            +
            fotolab info -h
         | 
| 188 | 
            +
            ```
         | 
| 189 | 
            +
             | 
| 190 | 
            +
            ```console
         | 
| 191 | 
            +
            usage: fotolab info [-h] IMAGE_FILENAME
         | 
| 130 192 |  | 
| 131 193 | 
             
            positional arguments:
         | 
| 132 194 | 
             
              IMAGE_FILENAME  set the image filename
         | 
| @@ -135,6 +197,44 @@ optional arguments: | |
| 135 197 | 
             
              -h, --help      show this help message and exit
         | 
| 136 198 | 
             
            ```
         | 
| 137 199 |  | 
| 200 | 
            +
            ### fotolab montage
         | 
| 201 | 
            +
             | 
| 202 | 
            +
            ```console
         | 
| 203 | 
            +
            fotolab montage -h
         | 
| 204 | 
            +
            ```
         | 
| 205 | 
            +
             | 
| 206 | 
            +
            ```console
         | 
| 207 | 
            +
             | 
| 208 | 
            +
            usage: fotolab montage [-h] IMAGE_FILENAMES [IMAGE_FILENAMES ...]
         | 
| 209 | 
            +
             | 
| 210 | 
            +
            positional arguments:
         | 
| 211 | 
            +
              IMAGE_FILENAMES  set the image filenames
         | 
| 212 | 
            +
             | 
| 213 | 
            +
            optional arguments:
         | 
| 214 | 
            +
              -h, --help       show this help message and exit
         | 
| 215 | 
            +
            ```
         | 
| 216 | 
            +
             | 
| 217 | 
            +
            ### fotolab resize
         | 
| 218 | 
            +
             | 
| 219 | 
            +
            ```console
         | 
| 220 | 
            +
            fotolab resize -h
         | 
| 221 | 
            +
            ```
         | 
| 222 | 
            +
             | 
| 223 | 
            +
            ```console
         | 
| 224 | 
            +
            usage: fotolab resize [-h] [-wh WIDTH] [-ht HEIGHT]
         | 
| 225 | 
            +
                                  IMAGE_FILENAMES [IMAGE_FILENAMES ...]
         | 
| 226 | 
            +
             | 
| 227 | 
            +
            positional arguments:
         | 
| 228 | 
            +
              IMAGE_FILENAMES       set the image filename
         | 
| 229 | 
            +
             | 
| 230 | 
            +
            optional arguments:
         | 
| 231 | 
            +
              -h, --help            show this help message and exit
         | 
| 232 | 
            +
              -wh WIDTH, --width WIDTH
         | 
| 233 | 
            +
                                    set the width of the image (default: '600')
         | 
| 234 | 
            +
              -ht HEIGHT, --height HEIGHT
         | 
| 235 | 
            +
                                    set the height of the image (default: '277')
         | 
| 236 | 
            +
            ```
         | 
| 237 | 
            +
             | 
| 138 238 | 
             
            ### fotolab sharpen
         | 
| 139 239 |  | 
| 140 240 | 
             
            ```console
         | 
| @@ -142,13 +242,22 @@ fotolab sharpen -h | |
| 142 242 | 
             
            ```
         | 
| 143 243 |  | 
| 144 244 | 
             
            ```console
         | 
| 145 | 
            -
            usage: fotolab sharpen [-h]  | 
| 245 | 
            +
            usage: fotolab sharpen [-h] [-r RADIUS] [-p PERCENT] [-t THRESHOLD]
         | 
| 246 | 
            +
                                   IMAGE_FILENAMES [IMAGE_FILENAMES ...]
         | 
| 146 247 |  | 
| 147 248 | 
             
            positional arguments:
         | 
| 148 | 
            -
               | 
| 249 | 
            +
              IMAGE_FILENAMES       set the image filenames
         | 
| 149 250 |  | 
| 150 | 
            -
             | 
| 151 | 
            -
              -h, --help | 
| 251 | 
            +
            optional arguments:
         | 
| 252 | 
            +
              -h, --help            show this help message and exit
         | 
| 253 | 
            +
              -r RADIUS, --radius RADIUS
         | 
| 254 | 
            +
                                    set the radius or size of edges (default: '1')
         | 
| 255 | 
            +
              -p PERCENT, --percent PERCENT
         | 
| 256 | 
            +
                                    set the amount of overall strength of sharpening
         | 
| 257 | 
            +
                                    effect (default: '100')
         | 
| 258 | 
            +
              -t THRESHOLD, --threshold THRESHOLD
         | 
| 259 | 
            +
                                    set the minimum brightness changed to be sharpened
         | 
| 260 | 
            +
                                    (default: '3')
         | 
| 152 261 | 
             
            ```
         | 
| 153 262 |  | 
| 154 263 | 
             
            ### fotolab watermark
         | 
| @@ -160,20 +269,23 @@ fotolab watermark -h | |
| 160 269 | 
             
            ```console
         | 
| 161 270 | 
             
            usage: fotolab watermark [-h] [-t WATERMARK_TEXT]
         | 
| 162 271 | 
             
                                     [-p {top-left,top-right,bottom-left,bottom-right}]
         | 
| 163 | 
            -
                                     [- | 
| 164 | 
            -
                                     [-oc OUTLINE_COLOR]
         | 
| 165 | 
            -
                                      | 
| 272 | 
            +
                                     [-pd PADDING] [-fs FONT_SIZE] [-fc FONT_COLOR]
         | 
| 273 | 
            +
                                     [-ow OUTLINE_WIDTH] [-oc OUTLINE_COLOR]
         | 
| 274 | 
            +
                                     IMAGE_FILENAMES [IMAGE_FILENAMES ...]
         | 
| 166 275 |  | 
| 167 276 | 
             
            positional arguments:
         | 
| 168 | 
            -
               | 
| 277 | 
            +
              IMAGE_FILENAMES       set the image filenames
         | 
| 169 278 |  | 
| 170 | 
            -
             | 
| 279 | 
            +
            optional arguments:
         | 
| 171 280 | 
             
              -h, --help            show this help message and exit
         | 
| 172 281 | 
             
              -t WATERMARK_TEXT, --text WATERMARK_TEXT
         | 
| 173 282 | 
             
                                    set the watermark text (default: 'kianmeng.org')
         | 
| 174 283 | 
             
              -p {top-left,top-right,bottom-left,bottom-right}, --position {top-left,top-right,bottom-left,bottom-right}
         | 
| 175 284 | 
             
                                    set position of the watermark text (default: 'bottom-
         | 
| 176 285 | 
             
                                    left')
         | 
| 286 | 
            +
              -pd PADDING, --padding PADDING
         | 
| 287 | 
            +
                                    set the padding of the watermark text relative to the
         | 
| 288 | 
            +
                                    image (default: '15')
         | 
| 177 289 | 
             
              -fs FONT_SIZE, --font-size FONT_SIZE
         | 
| 178 290 | 
             
                                    set the font size of watermark text (default: '12')
         | 
| 179 291 | 
             
              -fc FONT_COLOR, --font-color FONT_COLOR
         | 
| @@ -0,0 +1,18 @@ | |
| 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,,
         | 
    
        fotolab-0.9.1.dist-info/RECORD
    DELETED
    
    | @@ -1,15 +0,0 @@ | |
| 1 | 
            -
            fotolab/__init__.py,sha256=3N1D57pcYWYmpOUqPIomNqEWg4qVTyVQP6AUvKRNGOg,2015
         | 
| 2 | 
            -
            fotolab/__main__.py,sha256=aboOURPs_snOXTEWYR0q8oq1UTY9e-NxCd1j33V0wHI,833
         | 
| 3 | 
            -
            fotolab/auto.py,sha256=k4DkHQp05m7UYUgHRrH9L1d0eSgOzrn2Cyk5BG27KyA,2173
         | 
| 4 | 
            -
            fotolab/border.py,sha256=-XNa118OpiXwCXptSCl1HdxTJ2SSnv62HSg6xfBChaY,3413
         | 
| 5 | 
            -
            fotolab/cli.py,sha256=4IEk0DRrCPIG0PB37f4zSyWUEbrZrLSKi5qJxb1M0Is,4433
         | 
| 6 | 
            -
            fotolab/contrast.py,sha256=chIjzw2h7TA_u63Y_ZNS_0otxtyXgHmHaCDOMUayS_A,1969
         | 
| 7 | 
            -
            fotolab/env.py,sha256=NTTvfISWBBfIw5opWrUfg0BtkaAtdUtcISBAJC2gVUk,1449
         | 
| 8 | 
            -
            fotolab/resize.py,sha256=6314jLmhwb_ktd6uKEToaNHW0ZOyPW2IiELosBsIcXs,2128
         | 
| 9 | 
            -
            fotolab/sharpen.py,sha256=5lLWZ2JOM-jfsqr6Lt9tlijZH1oz4KlvBytTL0-QLQI,2535
         | 
| 10 | 
            -
            fotolab/watermark.py,sha256=Hn3nUhQo3iFhvtlsLjGjFMGG8rqKFNGOYQBcwRS250M,4648
         | 
| 11 | 
            -
            fotolab-0.9.1.dist-info/entry_points.txt,sha256=mvw7AY_yZkIyjAxPtHNed9X99NZeLnMxEeAfEJUbrCM,44
         | 
| 12 | 
            -
            fotolab-0.9.1.dist-info/LICENSE.md,sha256=tGtFDwxWTjuR9syrJoSv1Hiffd2u8Tu8cYClfrXS_YU,31956
         | 
| 13 | 
            -
            fotolab-0.9.1.dist-info/WHEEL,sha256=Sgu64hAMa6g5FdzHxXv9Xdse9yxpGGMeagVtPMWpJQY,99
         | 
| 14 | 
            -
            fotolab-0.9.1.dist-info/METADATA,sha256=ukyF71SeL7LHXiYzH90aXgo6xzA94Q3aQ_iZjZDRr7s,6688
         | 
| 15 | 
            -
            fotolab-0.9.1.dist-info/RECORD,,
         | 
| 
            File without changes
         | 
| 
            File without changes
         | 
| 
            File without changes
         |