"""Moteur N-Up : placer plusieurs pages par feuille.""" from pypdf import PdfReader, PdfWriter, PageObject, Transformation from .pdf_utils import get_page_size, create_blank_page, mm_to_pt, smart_crop_marks import io def nup_pages(input_path, output_path, cols=2, rows=2, sheet_size=(841.89, 595.28), margin_top=0, margin_bottom=0, margin_left=0, margin_right=0, gutter_h=0, gutter_v=0, scale_mode="fit", crop_marks=False, background_path=None): """Placer plusieurs pages sur chaque feuille. cols, rows: grille sheet_size: (largeur, hauteur) en points margins: en points gutter_h, gutter_v: espacement horizontal/vertical entre pages scale_mode: "fit" (ajuster) ou "none" (taille reelle) crop_marks: ajouter des reperes de coupe background_path: chemin vers un PDF de fond """ reader = PdfReader(input_path) pages = reader.pages sheet_w, sheet_h = sheet_size # Zone utile usable_w = sheet_w - margin_left - margin_right - gutter_h * (cols - 1) usable_h = sheet_h - margin_top - margin_bottom - gutter_v * (rows - 1) cell_w = usable_w / cols cell_h = usable_h / rows # Fond bg_page = None if background_path: bg_reader = PdfReader(background_path) if bg_reader.pages: bg_page = bg_reader.pages[0] writer = PdfWriter() pages_per_sheet = cols * rows for sheet_idx in range(0, len(pages), pages_per_sheet): sheet = PageObject.create_blank_page(width=sheet_w, height=sheet_h) if bg_page: sheet.merge_page(bg_page) grid_positions = [] for slot in range(pages_per_sheet): page_idx = sheet_idx + slot if page_idx >= len(pages): break page = pages[page_idx] page_w, page_h = get_page_size(page) col = slot % cols row = slot // cols grid_positions.append((col, row)) # Position de la cellule x = margin_left + col * (cell_w + gutter_h) y = sheet_h - margin_top - (row + 1) * cell_h - row * gutter_v # Calcul du scale if scale_mode == "fit": scale_x = cell_w / page_w scale_y = cell_h / page_h scale = min(scale_x, scale_y) else: scale = 1.0 # Centrage dans la cellule scaled_w = page_w * scale scaled_h = page_h * scale offset_x = x + (cell_w - scaled_w) / 2 offset_y = y + (cell_h - scaled_h) / 2 # Placer la page overlay = PageObject.create_blank_page(width=sheet_w, height=sheet_h) overlay.merge_page(page) overlay.add_transformation( Transformation().scale(scale, scale).translate(offset_x, offset_y) ) sheet.merge_page(overlay) # Reperes de coupe if crop_marks and grid_positions: marks = smart_crop_marks( sheet_w, sheet_h, grid_positions, cell_w + gutter_h, cell_h + gutter_v, margin_left, margin_top ) sheet.merge_page(marks) writer.add_page(sheet) with open(output_path, "wb") as f: writer.write(f) return output_path