[
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nenv/\nbin/\nbuild/\ndevelop-eggs/\ndist/\neggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\n*.egg-info/\n.installed.cfg\n*.egg\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.cache\nnosetests.xml\ncoverage.xml\n\n# Translations\n*.mo\n\n# Mr Developer\n.mr.developer.cfg\n.project\n.pydevproject\n\n# Rope\n.ropeproject\n\n# Django stuff:\n*.log\n*.pot\n\n# Sphinx documentation\ndocs/_build/\n\n# Test outputs\n*.png\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014 Dmitry Alimov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README.md",
    "content": "Collage maker\n=============\n\nPicture collage maker in Python\n\nUsage:\n------\nThe usage of `collage_maker.py` is very simple:\n```\nusage: collage_maker.py [-h] [-f FOLDER] [-o OUTPUT] [-w WIDTH]\n                        [-i INIT_HEIGHT] [-s]\n\nPhoto collage maker\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -f FOLDER, --folder FOLDER\n                        folder with images (*.jpg, *.jpeg, *.png)\n  -o OUTPUT, --output OUTPUT\n                        output collage image filename\n  -w WIDTH, --width WIDTH\n                        resulting collage image width\n  -i INIT_HEIGHT, --init_height INIT_HEIGHT\n                        initial height for resize the images\n  -s, --shuffle         enable images shuffle\n```\n\nExample:\n```\ncollage_maker.py -o my_collage.png -w 800 -i 250 -s\n```\n\nDescription:\n------------\n\nDescription of algorithm is available in my blog:\nhttp://delimitry.blogspot.com/2014/07/picture-collage-maker-using-python.html\n\nLicense:\n--------\nReleased under [The MIT License](https://github.com/delimitry/collage_maker/blob/master/LICENSE).\n"
  },
  {
    "path": "collage_maker.py",
    "content": "# -*- coding: utf-8 -*-\n\"\"\"\nCollage maker - tool to create picture collages\nAuthor: Delimitry\n\"\"\"\n\nimport argparse\nimport os\nimport random\nfrom PIL import Image\n\n\ndef make_collage(images, filename, width, init_height):\n    \"\"\"\n    Make a collage image with a width equal to `width` from `images` and save to `filename`.\n    \"\"\"\n    if not images:\n        print('No images for collage found!')\n        return False\n\n    margin_size = 2\n    # run until a suitable arrangement of images is found\n    while True:\n        # copy images to images_list\n        images_list = images[:]\n        coefs_lines = []\n        images_line = []\n        x = 0\n        while images_list:\n            # get first image and resize to `init_height`\n            img_path = images_list.pop(0)\n            img = Image.open(img_path)\n            img.thumbnail((width, init_height))\n            # when `x` will go beyond the `width`, start the next line\n            if x > width:\n                coefs_lines.append((float(x) / width, images_line))\n                images_line = []\n                x = 0\n            x += img.size[0] + margin_size\n            images_line.append(img_path)\n        # finally add the last line with images\n        coefs_lines.append((float(x) / width, images_line))\n\n        # compact the lines, by reducing the `init_height`, if any with one or less images\n        if len(coefs_lines) <= 1:\n            break\n        if any(map(lambda c: len(c[1]) <= 1, coefs_lines)):\n            # reduce `init_height`\n            init_height -= 10\n        else:\n            break\n\n    # get output height\n    out_height = 0\n    for coef, imgs_line in coefs_lines:\n        if imgs_line:\n            out_height += int(init_height / coef) + margin_size\n    if not out_height:\n        print('Height of collage could not be 0!')\n        return False\n\n    collage_image = Image.new('RGB', (width, int(out_height)), (35, 35, 35))\n    # put images to the collage\n    y = 0\n    for coef, imgs_line in coefs_lines:\n        if imgs_line:\n            x = 0\n            for img_path in imgs_line:\n                img = Image.open(img_path)\n                # if need to enlarge an image - use `resize`, otherwise use `thumbnail`, it's faster\n                k = (init_height / coef) / img.size[1]\n                if k > 1:\n                    img = img.resize((int(img.size[0] * k), int(img.size[1] * k)), Image.LANCZOS)\n                else:\n                    img.thumbnail((int(width / coef), int(init_height / coef)), Image.LANCZOS)\n                if collage_image:\n                    collage_image.paste(img, (int(x), int(y)))\n                x += img.size[0] + margin_size\n            y += int(init_height / coef) + margin_size\n    collage_image.save(filename)\n    return True\n\n\ndef main():\n    # prepare argument parser\n    parse = argparse.ArgumentParser(description='Photo collage maker')\n    parse.add_argument('-f', '--folder', dest='folder', help='folder with images (*.jpg, *.jpeg, *.png)', default='.')\n    parse.add_argument('-o', '--output', dest='output', help='output collage image filename', default='collage.png')\n    parse.add_argument('-w', '--width', dest='width', type=int, help='resulting collage image width')\n    parse.add_argument('-i', '--init_height', dest='init_height', type=int, help='initial height for resize the images')\n    parse.add_argument('-s', '--shuffle', action='store_true', dest='shuffle', help='enable images shuffle')\n\n    args = parse.parse_args()\n    if not args.width or not args.init_height:\n        parse.print_help()\n        exit(1)\n\n    # get images\n    files = [os.path.join(args.folder, fn) for fn in os.listdir(args.folder)]\n    images = [fn for fn in files if os.path.splitext(fn)[1].lower() in ('.jpg', '.jpeg', '.png')]\n    if not images:\n        print('No images for making collage! Please select other directory with images!')\n        exit(1)\n\n    # shuffle images if needed\n    if args.shuffle:\n        random.shuffle(images)\n\n    print('Making collage...')\n    res = make_collage(images, args.output, args.width, args.init_height)\n    if not res:\n        print('Failed to create collage!')\n        exit(1)\n    print('Collage is ready!')\n\n\nif __name__ == '__main__':\n    main()\n"
  }
]