Metadata-Version: 2.4
Name: image-grid-combine
Version: 1.0.0
Summary: Combine several images into one grid image, with Pillow. Pure-Python layout maths plus a small CLI.
License-Expression: MIT
Project-URL: Homepage, https://aiimagecombiner.app
Project-URL: Source Code, https://aiimagecombiner.app
Keywords: image,images,combine,merge,grid,collage,photo,pillow,montage
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Pillow>=9.0
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"
Dynamic: license-file

# image-grid-combine

Combine several images into one grid image, with [Pillow](https://python-pillow.org/).

Point it at a handful of photos and it lays them out on a single canvas: square-ish
grid by default, equal cells, `cover`-cropped so nothing is distorted. The layout
maths is plain Python with no Pillow import, so you can reuse the geometry on its
own if you already have your own drawing code.

```bash
pip install image-grid-combine
```

## Command line

```bash
image-grid-combine grid.png one.jpg two.jpg three.jpg four.jpg
```

Four images become a 2x2 grid. Add a gutter and a margin:

```bash
image-grid-combine grid.png *.jpg --columns 3 --gap 12 --padding 24
```

Transparent background, cells pinned to 512x512, letterboxed instead of cropped:

```bash
image-grid-combine grid.png *.png --cell-size 512 --fit contain --background none
```

Run `image-grid-combine --help` for the full list.

| Option | Default | Meaning |
| --- | --- | --- |
| `--columns` | auto | Cells per row. Auto keeps the grid as square as the count allows. |
| `--gap` | `0` | Pixels between neighbouring cells. |
| `--padding` | `0` | Pixels around the outside of the grid. |
| `--background` | `white` | `#rgb`, `#rrggbb`, `#rrggbbaa`, `r,g,b[,a]`, a colour name, or `none`. |
| `--fit` | `cover` | `cover` crops to fill, `contain` letterboxes, `stretch` distorts. |
| `--cell-size` | auto | `512` or `512x384`. Auto uses the largest source, capped by `--max-width`. |
| `--max-width` | `2048` | Width cap when the cell size is auto. |
| `--max-height` | none | Optional height cap when the cell size is auto. |

## Library

```python
from image_grid_combine import combine

grid = combine(["a.jpg", "b.jpg", "c.jpg", "d.jpg"], columns=2, gap=12)
grid.save("grid.png")
```

Sources can be paths, `bytes`, binary file objects, or `PIL.Image.Image`
instances — handy if the images came off the network or out of a database and you
would rather not round-trip them through disk. The result is always an `RGBA`
image; nothing you pass in is modified.

```python
from io import BytesIO
from image_grid_combine import combine, combine_to_file

grid = combine([open("a.jpg", "rb"), BytesIO(response.content)], fit="contain")
combine_to_file("grid.jpg", ["a.jpg", "b.jpg"], gap=8)  # JPEG flattens onto white
```

`combine_to_file` picks the format from the file extension. JPEG has no alpha
channel, so the grid is flattened onto the background before saving.

### Layout on its own

If you are drawing somewhere other than a Pillow canvas — a PDF, a reportlab
document, a matplotlib figure — the geometry is importable without Pillow:

```python
from image_grid_combine.layout import auto_cell_size, compute_grid_layout, fit_rect

cell_w, cell_h, columns = auto_cell_size([(1600, 900), (900, 1600)], gap=12)
for cell in compute_grid_layout(2, columns, cell_w, cell_h, gap=12):
    rect = fit_rect(1600, 900, cell.width, cell.height, "cover")
    print(cell.x, cell.y, rect.sx, rect.sy, rect.sw, rect.sh)
```

`fit_rect` returns a `FitResult` naming both rectangles: the crop to take from the
source (`sx`, `sy`, `sw`, `sh`) and the box to draw it into (`dx`, `dy`, `dw`,
`dh`). Under `cover` the destination is the whole cell and the source is cropped;
under `contain` it is the other way round.

## Notes

- Cell sizes are equal across the grid, so mixing a panorama with a portrait
  gives you two cropped images rather than a ragged layout. `--fit contain`
  trades the crop for letterboxing.
- Cropping and scaling round to whole pixels, so the last row of a partial grid
  can differ by a pixel from the rows above it. That is deliberate — sub-pixel
  placement blurs edges.
- Works on Python 3.8+. The only dependency is Pillow.

## Related

- **[ai image combiner](https://aiimagecombiner.app)** — a browser-based AI image
  combiner for when you would rather not write code: drop in two or more photos
  and merge them with AI blending, no install and no signup.

## License

MIT
