"""Utilitaires PDF communs pour Copydev Imposing Tool.""" from pypdf import PdfReader, PdfWriter, PageObject, Transformation from reportlab.pdfgen import canvas from reportlab.lib.units import mm, inch from reportlab.lib.pagesizes import A4, A3, letter import io import math # Tailles de pages predefinies (largeur, hauteur en points) PAGE_SIZES = { "A4 Portrait": (595.28, 841.89), "A4 Paysage": (841.89, 595.28), "A3 Portrait": (841.89, 1190.55), "A3 Paysage": (1190.55, 841.89), "Letter Portrait": (612, 792), "Letter Paysage": (792, 612), "Legal Portrait": (612, 1008), "Legal Paysage": (1008, 612), } # Conversion PT_TO_MM = 25.4 / 72 MM_TO_PT = 72 / 25.4 def mm_to_pt(val_mm): """Convertir millimetres en points.""" return val_mm * MM_TO_PT def pt_to_mm(val_pt): """Convertir points en millimetres.""" return val_pt * PT_TO_MM def get_page_size(page): """Obtenir la taille d'une page (largeur, hauteur) en points.""" box = page.mediabox w = float(box.width) h = float(box.height) return w, h def create_blank_page(width, height): """Creer une page blanche avec les dimensions donnees (en points).""" packet = io.BytesIO() c = canvas.Canvas(packet, pagesize=(width, height)) c.showPage() c.save() packet.seek(0) reader = PdfReader(packet) return reader.pages[0] def merge_page_onto(base_page, overlay_page, x=0, y=0, scale=1.0, rotation=0): """Fusionner une page overlay sur une page de base a une position donnee.""" if rotation: overlay_page = overlay_page.rotate(rotation) transform = Transformation().scale(scale, scale).translate(x, y) overlay_page.add_transformation(transform) base_page.merge_page(overlay_page) return base_page def create_crop_marks_page(width, height, positions, mark_length=14.17, mark_offset=7.09, line_width=0.25): """Creer une page avec des reperes de coupe aux positions donnees. positions: liste de tuples (x, y) ou placer les reperes mark_length: longueur des traits (defaut 5mm = 14.17pt) mark_offset: distance du bord de la page (defaut 2.5mm = 7.09pt) """ packet = io.BytesIO() c = canvas.Canvas(packet, pagesize=(width, height)) c.setLineWidth(line_width) c.setStrokeColorRGB(0, 0, 0) for x, y in positions: # Trait horizontal gauche c.line(x - mark_offset - mark_length, y, x - mark_offset, y) # Trait horizontal droit c.line(x + mark_offset, y, x + mark_offset + mark_length, y) # Trait vertical bas c.line(x, y - mark_offset - mark_length, x, y - mark_offset) # Trait vertical haut c.line(x, y + mark_offset, x, y + mark_offset + mark_length) c.showPage() c.save() packet.seek(0) reader = PdfReader(packet) return reader.pages[0] def smart_crop_marks(sheet_width, sheet_height, grid_positions, cell_width, cell_height, margin_left=0, margin_top=0, mark_length=14.17, mark_offset=7.09): """Generer des reperes de coupe intelligents pour une grille n-up. Evite les chevauchements avec le contenu des pages. """ positions = set() for col, row in grid_positions: x = margin_left + col * cell_width y = sheet_height - margin_top - row * cell_height # 4 coins de chaque cellule corners = [ (x, y), (x + cell_width, y), (x, y - cell_height), (x + cell_width, y - cell_height), ] for cx, cy in corners: positions.add((cx, cy)) return create_crop_marks_page(sheet_width, sheet_height, list(positions), mark_length, mark_offset) def pad_to_multiple(reader, multiple=4): """Ajouter des pages blanches pour atteindre un multiple donne.""" num_pages = len(reader.pages) remainder = num_pages % multiple if remainder == 0: return reader pages_to_add = multiple - remainder writer = PdfWriter() for page in reader.pages: writer.add_page(page) last_page = reader.pages[-1] w, h = get_page_size(last_page) for _ in range(pages_to_add): writer.add_blank_page(w, h) output = io.BytesIO() writer.write(output) output.seek(0) return PdfReader(output)