Skip to content

Instantly share code, notes, and snippets.

@mardiros
Last active October 22, 2022 06:43
Show Gist options
  • Save mardiros/f594f6f2536f8b8399511683d0ec9b08 to your computer and use it in GitHub Desktop.
Save mardiros/f594f6f2536f8b8399511683d0ec9b08 to your computer and use it in GitHub Desktop.
Massonry
import sys
import random
from mimetypes import guess_type
from dataclasses import dataclass, field
from pathlib import Path
from typing import Iterator
from PIL import Image as PILImage
@dataclass
class Image:
name: str = field()
width: int = field()
ratio: float = field()
@property
def height(self) -> float:
return self.width / self.ratio
def yield_image(path: Path) -> Iterator[Image]:
images = path.glob("*.*")
for image in images:
typ_, _ = guess_type(image.name)
if not typ_ or typ_ and not typ_.startswith("image/"):
continue
with PILImage.open(image) as img:
yield Image(image.name, img.width, img.width / img.height)
def resize_chunk(chunk: list[Image], target_tot_width: int) -> list[Image]:
tot_width = sum([i.width for i in chunk])
ref = chunk[0]
target_width = round(ref.width / tot_width * target_tot_width)
target_height = target_width / ref.ratio
ret = []
last_bisect = target_height * 2
for i in range(1000):
ret = [
Image(
name=image.name,
width=round(image.width * target_height / image.height),
ratio=image.ratio,
)
for image in chunk
]
tot_width = sum([i.width for i in ret])
if target_tot_width - 1 < round(tot_width) < target_tot_width + 1:
# it's a win
return ret
if tot_width > target_tot_width:
# if it's larger, reduce the height to fit
target_height -= last_bisect/2
else:
target_height += last_bisect/2
last_bisect = last_bisect/2
raise RuntimeError("Bisect Error")
def yield_resized_images(
path: Path, max_height: int, width: int
) -> Iterator[list[Image]]:
images = list(yield_image(path))
random.shuffle(images)
chunk = []
curX = 0
for image in images:
chunk.append(image)
resized_x = max_height * image.ratio
curX += resized_x
if curX > width:
yield resize_chunk(chunk, width)
chunk = []
curX = 0
# the rest
yield resize_chunk(chunk, width)
def print_html(path: Path, max_height: int, width: int):
print(
f"""
<html>
<body style="background-color: darkseagreen">
<div style="width: {width}px; overflow: auto; margin: auto; background-color: black;">
""".strip()
)
images_chunks = yield_resized_images(path, max_height, width)
for images in images_chunks:
for image in images:
print(
f"""
<div style="float: left">
<img src="{image.name}" width="{image.width}" height="{image.height}">
</div>
"""
)
print("""<hr style="clear: both; visibility: hidden"/>""")
print(
"""
</div>
</body>
</html>
""".strip()
)
def main():
src = sys.argv[1]
width = 920
max_height = round(width * 0.75 / 3.5)
path = Path(src).resolve()
print_html(path, max_height, width)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment