import os from pathlib import Path from typing import List, Optional from PIL import Image, ImageDraw W_INCHES_EXPECTED = 2.72 H_INCHES_EXPECTED = 3.7 BLEED_INCHES_EXPECTED = 0.12 LEAVE_BLEED_INCHES = 0.04 CORNER_RADIUS_INCHES = 0.12 BASE_DIR = Path(__file__).parent def gather_images(directory: Path, midfix : Optional[str] = None) -> List[Path]: files_to_crop = [] for f in os.listdir(directory): possible_image_path = directory / f if not possible_image_path.is_file(): # Skip directory continue # TODO: Check if is image if midfix is None or any(s == f'.{midfix}' for s in possible_image_path.suffixes): files_to_crop.append(possible_image_path) return files_to_crop def gather_input_cards() -> List[Path]: return gather_images(directory=BASE_DIR / 'input_cards', midfix=None) def gather_cropped_cards() -> List[Path]: return gather_images(directory=BASE_DIR / 'cropped_cards', midfix='crop') def get_inputs_without_crop(all_inputs: List[Path], already_cropped: List[Path]) -> List[Path]: already_cropped_set = set(already_cropped) inputs_without_crop = [] for input_filepath in all_inputs: possible_cropped_filepath = input_to_crop_filepath(input_filepath) if possible_cropped_filepath.name not in already_cropped_set: inputs_without_crop.append(input_filepath) return inputs_without_crop def add_midfix(filepath: Path, midfix: str) -> Path: return filepath.with_stem(f"{filepath.stem}.{midfix}") def input_to_crop_filepath(input_filepath: Path) -> Path: return add_midfix(input_filepath, 'crop') def crop(input_filepath: Path) -> Path: out_dir = BASE_DIR / 'cropped_cards' out_dir.mkdir(parents=True, exist_ok=True) cropped_filepath = input_to_crop_filepath(input_filepath) try: with Image.open(input_filepath) as im: # print(im.info) rgb_im = im.convert('RGB') w, h = rgb_im.width, rgb_im.height dpi_by_w = w / W_INCHES_EXPECTED dpi_by_h = h / H_INCHES_EXPECTED print(f'By width dpi is {dpi_by_w} dpi') if dpi_by_h != dpi_by_w: print(f'dpi by height is different: {dpi_by_h} dpi') crop_pixels = (BLEED_INCHES_EXPECTED - LEAVE_BLEED_INCHES) * dpi_by_w crop_pixels_int = round(crop_pixels) print(f'Image is {w} x {h}, will trim {crop_pixels} pixels (rounded to {crop_pixels_int})') # Crop crop_box = (crop_pixels_int, crop_pixels_int, w - crop_pixels_int, h - crop_pixels_int) cropped_part = rgb_im.crop(crop_box) cropped_w, cropped_h = cropped_part.width, cropped_part.height # Draw where cuts should go cut_pixels = LEAVE_BLEED_INCHES * dpi_by_w cut_pixels_int = round(cut_pixels) draw = ImageDraw.Draw(cropped_part) LENGTH_CUT_GUIDE_INCHES = 0.06 CUT_GUIDE_WIDTH_INCHES = 0.004 guide_len = round(LENGTH_CUT_GUIDE_INCHES * dpi_by_w) guide_thickness = round(CUT_GUIDE_WIDTH_INCHES * dpi_by_w) cut_x_l = cut_pixels_int cut_x_r = cropped_w - cut_pixels_int cut_y_t = cut_pixels_int cut_y_b = cropped_h - cut_pixels_int color = 'magenta' # Top left draw.line([(cut_x_l, cut_y_t), (cut_x_l, cut_y_t + guide_len)], fill = color, width = guide_thickness) draw.line([(cut_x_l, cut_y_t), (cut_x_l + guide_len, cut_y_t)], fill = color, width = guide_thickness) # Bottom left draw.line([(cut_x_l, cut_y_b), (cut_x_l, cut_y_b - guide_len)], fill = color, width = guide_thickness) draw.line([(cut_x_l, cut_y_b), (cut_x_l + guide_len, cut_y_b)], fill = color, width = guide_thickness) # Top right draw.line([(cut_x_r, cut_y_t), (cut_x_r, cut_y_t + guide_len)], fill = color, width = guide_thickness) draw.line([(cut_x_r, cut_y_t), (cut_x_r - guide_len, cut_y_t)], fill = color, width = guide_thickness) # Bottom right draw.line([(cut_x_r, cut_y_b), (cut_x_r, cut_y_b - guide_len)], fill = color, width = guide_thickness) draw.line([(cut_x_r, cut_y_b), (cut_x_r - guide_len, cut_y_b)], fill = color, width = guide_thickness) cropped_part.save(cropped_filepath, 'JPEG', quality=100, subsampling=0) return cropped_filepath except OSError as e: print(e) return None def crop_all_inputs(files_to_crop: List[Path]): num_files_to_crop = len(files_to_crop) for i, input_filepath in enumerate(files_to_crop, 1): print(f'[{i}/{num_files_to_crop}] Cropping {input_filepath}') crop(input_filepath) def create_sheet(cropped_filepaths: List[Path]) -> Path: # Create sheet LETTER_PAGE_W_INCHES = 11 LETTER_PAGE_H_INCHES = 8.5 CARD_W_INCHES = 2.48031 # 63mm CARD_H_INCHES = 3.46457 # 88mm DPI = 1200 PAGE_W = round(LETTER_PAGE_W_INCHES * DPI) PAGE_H = round(LETTER_PAGE_H_INCHES * DPI) CARD_W = round(CARD_W_INCHES * DPI) CARD_H = round(CARD_H_INCHES * DPI) CROPPED_IMAGE_DEST_W = round((CARD_W_INCHES + LEAVE_BLEED_INCHES) * DPI) CROPPED_IMAGE_DEST_H = round((CARD_H_INCHES + LEAVE_BLEED_INCHES) * DPI) SPACING_INCHES = 0.05 SPACING = round(SPACING_INCHES * DPI) CARD_COLUMNS = 4 CARD_ROWS = 2 TOTAL_W_CONTENT = CROPPED_IMAGE_DEST_W * CARD_COLUMNS + SPACING * (CARD_COLUMNS - 1) TOTAL_H_CONTENT = CROPPED_IMAGE_DEST_H * CARD_ROWS + SPACING * (CARD_ROWS - 1) BLANK_SPACE_W = (PAGE_W - TOTAL_W_CONTENT) // 2 BLANK_SPACE_H = (PAGE_H - TOTAL_H_CONTENT) // 2 sheet = Image.new('RGB', (PAGE_W, PAGE_H), 'white') # Draw cards assert len(cropped_filepaths) == 8 filenames_iter = cropped_filepaths.__iter__() for y in range(CARD_ROWS): for x in range(CARD_COLUMNS): filepath = filenames_iter.__next__() with Image.open(filepath) as card_im: card_im = card_im.resize((CROPPED_IMAGE_DEST_W, CROPPED_IMAGE_DEST_H), resample = Image.Resampling.LANCZOS) sheet.paste(card_im, (BLANK_SPACE_W + (CROPPED_IMAGE_DEST_W + SPACING) * x, BLANK_SPACE_H + (CROPPED_IMAGE_DEST_H + SPACING) * y)) # Save sheet out_dir = BASE_DIR / 'sheets' out_dir.mkdir(parents=True, exist_ok=True) outfile = out_dir / 'sheet.jpg' sheet.save(outfile, 'JPEG', dpi=(DPI, DPI), quality=100, subsampling=0) return outfile def main(): all_input_cards = gather_input_cards() print(f'Found {len(all_input_cards)} input cards:') print(all_input_cards) already_cropped_cards = gather_cropped_cards() print(f'Found {len(already_cropped_cards)} already cropped cards:') print(already_cropped_cards) new_inputs_to_crop = get_inputs_without_crop(all_input_cards, already_cropped_cards) print(f'Found {len(new_inputs_to_crop)} new cards to crop:') print(new_inputs_to_crop) crop_all_inputs(new_inputs_to_crop) all_cropped_cards = gather_cropped_cards() print(f'Now there are {len(already_cropped_cards)} cropped cards:') print(already_cropped_cards) cards_in_first_sheet = all_cropped_cards[:8] # cards = [ # 'Svella, Ice Shaper (Extended Andrew Mar).crop.jpg', # 'Icon of Ancestry.crop.png', # 'Condescend.crop.png', # 'Sulfur Falls (Cliff Childs).crop.png', # 'Giant Killer.crop.png', # 'Magda, Brazen Outlaw.crop.png', # 'Rhox.crop.png', # 'Visionary Augmenter.crop.png', # ] first_sheet_filepath = create_sheet(cards_in_first_sheet) print(f'Output {first_sheet_filepath}') if __name__ == '__main__': main()