Last active
October 22, 2022 06:43
-
-
Save mardiros/f594f6f2536f8b8399511683d0ec9b08 to your computer and use it in GitHub Desktop.
Massonry
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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