Move SheetLayout, make InputCardImage to resize

This commit is contained in:
henriquenakashima 2023-07-10 15:07:22 -04:00
parent 902482abb0
commit 89e20fd61e
4 changed files with 123 additions and 108 deletions

View file

@ -1,5 +1,8 @@
from pathlib import Path from pathlib import Path
CARD_W_INCHES = 2.48031 # 63mm
CARD_H_INCHES = 3.46457 # 88mm
LEAVE_BLEED_INCHES = 0.04 LEAVE_BLEED_INCHES = 0.04
BASE_DIR = Path(__file__).parent BASE_DIR = Path(__file__).parent

View file

@ -3,6 +3,7 @@ from pathlib import Path
from PIL import Image from PIL import Image
from base_proxy import LEAVE_BLEED_INCHES from base_proxy import LEAVE_BLEED_INCHES
from sheet_layout import SheetLayout
W_INCHES_EXPECTED = 2.72 W_INCHES_EXPECTED = 2.72
@ -33,3 +34,8 @@ class InputCardImage:
cropped_part = rgb_im.crop(crop_box) cropped_part = rgb_im.crop(crop_box)
return cropped_part return cropped_part
def get_cropped_and_resized(self, sheet_layout: SheetLayout) -> Image:
im = self.crop()
im = im.resize((sheet_layout.cropped_image_dest_w, sheet_layout.cropped_image_dest_h), resample=Image.Resampling.LANCZOS)
return im

View file

@ -1,12 +1,14 @@
import math import math
import os import os
from pathlib import Path from pathlib import Path
from typing import List, Optional, Tuple from typing import List
from PIL import Image, ImageDraw, ImageCms from PIL import Image, ImageDraw, ImageCms
from base_proxy import BASE_DIR, LEAVE_BLEED_INCHES from base_proxy import BASE_DIR
from card_images import InputCardImage from card_images import InputCardImage
import sheet_layout
from sheet_layout import SheetLayout
import util import util
@ -31,126 +33,49 @@ def gather_input_cards() -> List[Path]:
return gather_images(directory=BASE_DIR / 'input_cards') return gather_images(directory=BASE_DIR / 'input_cards')
CARD_W_INCHES = 2.48031 # 63mm def create_sheet(input_cards: List[InputCardImage], sheet_filepath: Path, layout: SheetLayout):
CARD_H_INCHES = 3.46457 # 88mm sheet = Image.new('RGB', (layout.w, layout.h), 'white')
class SheetLayout:
def __init__(self, dpi: int, w_inches: float, h_inches: float):
self.dpi = dpi
self.card_w_inches = CARD_W_INCHES
self.card_h_inches = CARD_H_INCHES
self.card_w = self.in_to_px(self.card_w_inches)
self.card_h = self.in_to_px(self.card_h_inches)
self.w_inches = w_inches
self.h_inches = h_inches
self.w = self.in_to_px(self.w_inches)
self.h = self.in_to_px(self.h_inches)
self.leave_bleed = self.in_to_px(LEAVE_BLEED_INCHES)
self.cropped_image_dest_w = self.card_w + self.leave_bleed * 2
self.cropped_image_dest_h = self.card_h + self.leave_bleed * 2
self.spacing_inches = 0.05
self.spacing = self.in_to_px(self.spacing_inches)
self.min_padding_inches = 0.12
self.min_padding = self.in_to_px(self.min_padding_inches)
self.cards_per_row = ((self.w - self.min_padding * 2 + self.spacing)
// (self.cropped_image_dest_w + self.spacing))
self.cards_per_column = ((self.h - self.min_padding * 2 + self.spacing)
// (self.cropped_image_dest_h + self.spacing))
self.cards_per_sheet = self.cards_per_row * self.cards_per_column
self.total_w_content = self.cropped_image_dest_w * self.cards_per_row + self.spacing * (self.cards_per_row - 1)
self.total_h_content = self.cropped_image_dest_h * self.cards_per_column + self.spacing * (self.cards_per_column - 1)
self.blank_space_w = (self.w - self.total_w_content) // 2
self.blank_space_h = (self.h - self.total_h_content) // 2
print(self.__dict__)
def in_to_px(self, inches: float) -> float:
return round(inches * self.dpi)
def top_of_row(self, y_index: int, with_bleed: bool) -> int:
assert 0 <= y_index < self.cards_per_column
top = self.blank_space_h + (self.cropped_image_dest_h + self.spacing) * y_index
if not with_bleed:
top += self.leave_bleed
return top
def bottom_of_row(self, y_index: int, with_bleed: bool) -> int:
if with_bleed:
return self.top_of_row(y_index, True) + self.cropped_image_dest_h
else:
return self.top_of_row(y_index, False) + self.card_h
def left_of_column(self, x_index: int, with_bleed: bool) -> int:
assert 0 <= x_index < self.cards_per_row
left = self.blank_space_w + (self.cropped_image_dest_w + self.spacing) * x_index
if not with_bleed:
left += self.leave_bleed
return left
def right_of_column(self, x_index: int, with_bleed: bool) -> int:
if with_bleed:
return self.left_of_column(x_index, True) + self.cropped_image_dest_w
else:
return self.left_of_column(x_index, False) + self.card_w
def topleft_of_card_with_bleed(self, y_index: int, x_index: int) -> Tuple[int, int]:
x = self.left_of_column(x_index, with_bleed=True)
y = self.top_of_row(y_index, with_bleed=True)
return (x, y)
def create_sheet(input_cards: List[InputCardImage], sheet_filepath: Path, sheet_layout: SheetLayout):
sheet = Image.new('RGB', (sheet_layout.w, sheet_layout.h), 'white')
# icc_profile = ImageCms.getOpenProfile(str(BASE_DIR / 'icc' / 'AdobeRGB1998.icc')) # icc_profile = ImageCms.getOpenProfile(str(BASE_DIR / 'icc' / 'AdobeRGB1998.icc'))
# Draw external guidelines # Draw external guidelines
draw = ImageDraw.Draw(sheet) draw = ImageDraw.Draw(sheet)
color = 'black' color = 'black'
guide_thickness = sheet_layout.in_to_px(CUT_GUIDE_WIDTH_INCHES) guide_thickness = layout.in_to_px(CUT_GUIDE_WIDTH_INCHES)
for x_index in range(sheet_layout.cards_per_column): for x_index in range(layout.cards_per_column):
left_x = sheet_layout.left_of_column(x_index, with_bleed=False) left_x = layout.left_of_column(x_index, with_bleed=False)
right_x = sheet_layout.right_of_column(x_index, with_bleed=False) right_x = layout.right_of_column(x_index, with_bleed=False)
draw.line([(left_x, 0), (left_x, sheet_layout.h)], fill=color, width=guide_thickness) draw.line([(left_x, 0), (left_x, layout.h)], fill=color, width=guide_thickness)
draw.line([(right_x, 0), (right_x, sheet_layout.h)], fill=color, width=guide_thickness) draw.line([(right_x, 0), (right_x, layout.h)], fill=color, width=guide_thickness)
for y_index in range(sheet_layout.cards_per_row): for y_index in range(layout.cards_per_row):
top_y = sheet_layout.top_of_row(y_index, with_bleed=False) top_y = layout.top_of_row(y_index, with_bleed=False)
bottom_y = sheet_layout.bottom_of_row(y_index, with_bleed=False) bottom_y = layout.bottom_of_row(y_index, with_bleed=False)
draw.line([(0, top_y), (sheet_layout.w, top_y)], fill=color, width=guide_thickness) draw.line([(0, top_y), (layout.w, top_y)], fill=color, width=guide_thickness)
draw.line([(0, bottom_y), (sheet_layout.w, bottom_y)], fill=color, width=guide_thickness) draw.line([(0, bottom_y), (layout.w, bottom_y)], fill=color, width=guide_thickness)
# Draw cards # Draw cards
input_cards_iter = input_cards.__iter__() input_cards_iter = input_cards.__iter__()
for y_index in range(sheet_layout.cards_per_column): for y_index in range(layout.cards_per_column):
for x_index in range(sheet_layout.cards_per_row): for x_index in range(layout.cards_per_row):
try: try:
input_card: InputCardImage = input_cards_iter.__next__() input_card: InputCardImage = input_cards_iter.__next__()
except StopIteration: except StopIteration:
break break
card_im = input_card.crop() card_im = input_card.get_cropped_and_resized(layout)
card_im = card_im.resize((sheet_layout.cropped_image_dest_w, sheet_layout.cropped_image_dest_h), sheet.paste(card_im, layout.topleft_of_card_with_bleed(y_index, x_index))
resample = Image.Resampling.LANCZOS)
sheet.paste(card_im, sheet_layout.topleft_of_card_with_bleed(y_index, x_index))
# Draw internal guidelines # Draw internal guidelines
color = 'magenta' color = 'magenta'
guide_len = sheet_layout.in_to_px(LENGTH_CUT_GUIDE_INCHES) guide_len = layout.in_to_px(LENGTH_CUT_GUIDE_INCHES)
# -1 because the pixels are in [0, W - 1] and [0, H - 1] # -1 because the pixels are in [0, W - 1] and [0, H - 1]
left_x = sheet_layout.left_of_column(x_index, with_bleed=False) left_x = layout.left_of_column(x_index, with_bleed=False)
right_x = sheet_layout.right_of_column(x_index, with_bleed=False) - 1 right_x = layout.right_of_column(x_index, with_bleed=False) - 1
top_y = sheet_layout.top_of_row(y_index, with_bleed=False) top_y = layout.top_of_row(y_index, with_bleed=False)
bottom_y = sheet_layout.bottom_of_row(y_index, with_bleed=False) - 1 bottom_y = layout.bottom_of_row(y_index, with_bleed=False) - 1
# Top left # Top left
draw.line([(left_x, top_y), (left_x, top_y + guide_len)], fill = color, width = guide_thickness) draw.line([(left_x, top_y), (left_x, top_y + guide_len)], fill = color, width = guide_thickness)
@ -166,8 +91,8 @@ def create_sheet(input_cards: List[InputCardImage], sheet_filepath: Path, sheet_
draw.line([(right_x, bottom_y), (right_x - guide_len, bottom_y)], fill = color, width = guide_thickness) draw.line([(right_x, bottom_y), (right_x - guide_len, bottom_y)], fill = color, width = guide_thickness)
# Save sheet # Save sheet
# sheet.save(sheet_filepath, 'JPEG', dpi=(sheet_layout.dpi, sheet_layout.dpi), quality=100, subsampling=0, icc_profile=icc_profile.tobytes()) # sheet.save(sheet_filepath, 'JPEG', dpi=(layout.dpi, layout.dpi), quality=100, subsampling=0, icc_profile=icc_profile.tobytes())
sheet.save(sheet_filepath, 'JPEG', dpi=(sheet_layout.dpi, sheet_layout.dpi), quality=100, subsampling=0) sheet.save(sheet_filepath, 'JPEG', dpi=(layout.dpi, layout.dpi), quality=100, subsampling=0)
def main(): def main():
@ -177,15 +102,14 @@ def main():
all_input_cards = [InputCardImage(p) for p in all_input_cards_paths] all_input_cards = [InputCardImage(p) for p in all_input_cards_paths]
#sheet_layout = SheetLayout(1200, 11, 8.5) layout = sheet_layout.letter_portrait_3x3_sheet_layout()
sheet_layout = SheetLayout(1200, 8.5, 11)
sheets_dir = BASE_DIR / 'sheets' sheets_dir = BASE_DIR / 'sheets'
sheets_dir.mkdir(parents=True, exist_ok=True) sheets_dir.mkdir(parents=True, exist_ok=True)
total_sheet_number = math.ceil(len(all_input_cards) / sheet_layout.cards_per_sheet) total_sheet_number = math.ceil(len(all_input_cards) / layout.cards_per_sheet)
for i, cards_in_sheet in enumerate(util.batched(all_input_cards, sheet_layout.cards_per_sheet), 1): for i, cards_in_sheet in enumerate(util.batched(all_input_cards, layout.cards_per_sheet), 1):
print(f'Will create sheet {i}/{total_sheet_number}') print(f'Will create sheet {i}/{total_sheet_number}')
sheet_filepath = sheets_dir / f'sheet{i:03d}.jpg' sheet_filepath = sheets_dir / f'sheet{i:03d}.jpg'
create_sheet(cards_in_sheet, sheet_filepath, sheet_layout) create_sheet(cards_in_sheet, sheet_filepath, layout)
print(f'Output {sheet_filepath}') print(f'Output {sheet_filepath}')

View file

@ -0,0 +1,82 @@
from typing import Tuple
from base_proxy import CARD_W_INCHES, CARD_H_INCHES, LEAVE_BLEED_INCHES
class SheetLayout:
def __init__(self, dpi: int, w_inches: float, h_inches: float):
self.dpi = dpi
self.card_w_inches = CARD_W_INCHES
self.card_h_inches = CARD_H_INCHES
self.card_w = self.in_to_px(self.card_w_inches)
self.card_h = self.in_to_px(self.card_h_inches)
self.w_inches = w_inches
self.h_inches = h_inches
self.w = self.in_to_px(self.w_inches)
self.h = self.in_to_px(self.h_inches)
self.leave_bleed = self.in_to_px(LEAVE_BLEED_INCHES)
self.cropped_image_dest_w = self.card_w + self.leave_bleed * 2
self.cropped_image_dest_h = self.card_h + self.leave_bleed * 2
self.spacing_inches = 0.05
self.spacing = self.in_to_px(self.spacing_inches)
self.min_padding_inches = 0.12
self.min_padding = self.in_to_px(self.min_padding_inches)
self.cards_per_row = ((self.w - self.min_padding * 2 + self.spacing)
// (self.cropped_image_dest_w + self.spacing))
self.cards_per_column = ((self.h - self.min_padding * 2 + self.spacing)
// (self.cropped_image_dest_h + self.spacing))
self.cards_per_sheet = self.cards_per_row * self.cards_per_column
self.total_w_content = self.cropped_image_dest_w * self.cards_per_row + self.spacing * (self.cards_per_row - 1)
self.total_h_content = self.cropped_image_dest_h * self.cards_per_column + self.spacing * (self.cards_per_column - 1)
self.blank_space_w = (self.w - self.total_w_content) // 2
self.blank_space_h = (self.h - self.total_h_content) // 2
print(self.__dict__)
def in_to_px(self, inches: float) -> float:
return round(inches * self.dpi)
def top_of_row(self, y_index: int, with_bleed: bool) -> int:
assert 0 <= y_index < self.cards_per_column
top = self.blank_space_h + (self.cropped_image_dest_h + self.spacing) * y_index
if not with_bleed:
top += self.leave_bleed
return top
def bottom_of_row(self, y_index: int, with_bleed: bool) -> int:
if with_bleed:
return self.top_of_row(y_index, True) + self.cropped_image_dest_h
else:
return self.top_of_row(y_index, False) + self.card_h
def left_of_column(self, x_index: int, with_bleed: bool) -> int:
assert 0 <= x_index < self.cards_per_row
left = self.blank_space_w + (self.cropped_image_dest_w + self.spacing) * x_index
if not with_bleed:
left += self.leave_bleed
return left
def right_of_column(self, x_index: int, with_bleed: bool) -> int:
if with_bleed:
return self.left_of_column(x_index, True) + self.cropped_image_dest_w
else:
return self.left_of_column(x_index, False) + self.card_w
def topleft_of_card_with_bleed(self, y_index: int, x_index: int) -> Tuple[int, int]:
x = self.left_of_column(x_index, with_bleed=True)
y = self.top_of_row(y_index, with_bleed=True)
return (x, y)
def letter_portrait_3x3_sheet_layout() -> SheetLayout:
return SheetLayout(1200, 8.5, 11)
def letter_landscape_2x4_sheet_layout() -> SheetLayout:
return SheetLayout(1200, 11, 8.5)