V3.6.0 — Demonter pages, masquage visuel, regles, bleeds, corrections
- Demonter les pages : couper en 2 sans reordonner (sous Delivretiser) - Masquage visuel : selection au curseur sur l'apercu + plage de pages - Regles horizontale/verticale en mm (menu Affichage) - Fonds perdus : filigrane rouge pour visualiser les bleeds - Reperes de coupe : marques a l'exterieur (page agrandie), 2 traits/coin - Numerotation : couleur configurable via selecteur - Decalage : Trim & Shift renomme en francais, creep -> chasse - Recadrages multiples cumulatifs (plages differentes) - Fix spinbox format personnalise (fleches aleatoires) - Fix combo livret : option Automatique preservee Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,36 +5,50 @@ from .pdf_utils import get_page_size
|
||||
|
||||
|
||||
def crop_pages(input_path, output_path, crop_top=0, crop_bottom=0,
|
||||
crop_left=0, crop_right=0, pages=None):
|
||||
crop_left=0, crop_right=0, pages=None, per_page_settings=None):
|
||||
"""Recadrer les pages specifiees.
|
||||
|
||||
crop_top/bottom/left/right : en points, quantite a retirer de chaque cote
|
||||
pages : liste d'indices (0-based) des pages a recadrer. None = toutes.
|
||||
per_page_settings : dict {page_index: {"crop_top", "crop_bottom", "crop_left", "crop_right"}}
|
||||
Si fourni, applique des reglages differents par page.
|
||||
"""
|
||||
reader = PdfReader(input_path)
|
||||
writer = PdfWriter()
|
||||
|
||||
if pages is None:
|
||||
if pages is None and per_page_settings is None:
|
||||
pages = list(range(len(reader.pages)))
|
||||
|
||||
for i, page in enumerate(reader.pages):
|
||||
if i in pages:
|
||||
w, h = get_page_size(page)
|
||||
new_left = float(page.mediabox.left) + crop_left
|
||||
new_bottom = float(page.mediabox.bottom) + crop_bottom
|
||||
new_right = float(page.mediabox.right) - crop_right
|
||||
new_top = float(page.mediabox.top) - crop_top
|
||||
# Verifier si on a des reglages specifiques pour cette page
|
||||
if per_page_settings and i in per_page_settings:
|
||||
ps = per_page_settings[i]
|
||||
ct = ps.get("crop_top", 0)
|
||||
cb = ps.get("crop_bottom", 0)
|
||||
cl = ps.get("crop_left", 0)
|
||||
cr = ps.get("crop_right", 0)
|
||||
elif pages is not None and i in pages:
|
||||
ct, cb, cl, cr = crop_top, crop_bottom, crop_left, crop_right
|
||||
else:
|
||||
writer.add_page(page)
|
||||
continue
|
||||
|
||||
# Securite
|
||||
if new_right <= new_left or new_top <= new_bottom:
|
||||
writer.add_page(page)
|
||||
continue
|
||||
w, h = get_page_size(page)
|
||||
new_left = float(page.mediabox.left) + cl
|
||||
new_bottom = float(page.mediabox.bottom) + cb
|
||||
new_right = float(page.mediabox.right) - cr
|
||||
new_top = float(page.mediabox.top) - ct
|
||||
|
||||
page.mediabox.lower_left = (new_left, new_bottom)
|
||||
page.mediabox.upper_right = (new_right, new_top)
|
||||
page.cropbox = page.mediabox
|
||||
if hasattr(page, 'trimbox'):
|
||||
page.trimbox = page.mediabox
|
||||
# Securite
|
||||
if new_right <= new_left or new_top <= new_bottom:
|
||||
writer.add_page(page)
|
||||
continue
|
||||
|
||||
page.mediabox.lower_left = (new_left, new_bottom)
|
||||
page.mediabox.upper_right = (new_right, new_top)
|
||||
page.cropbox = page.mediabox
|
||||
if hasattr(page, 'trimbox'):
|
||||
page.trimbox = page.mediabox
|
||||
|
||||
writer.add_page(page)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Moteur Reperes de coupe autonomes."""
|
||||
|
||||
from pypdf import PdfReader, PdfWriter
|
||||
from pypdf import PdfReader, PdfWriter, PageObject, Transformation
|
||||
from reportlab.pdfgen import canvas
|
||||
from .pdf_utils import get_page_size
|
||||
import io
|
||||
@@ -8,11 +8,18 @@ import io
|
||||
|
||||
def add_crop_marks(input_path, output_path, mark_length=14.17, mark_offset=7.09,
|
||||
line_width=0.25, color=(0, 0, 0), center_marks=False,
|
||||
apply_to="all", current_page=0):
|
||||
"""Ajouter des reperes de coupe sur les pages."""
|
||||
apply_to="all", current_page=0, **_kwargs):
|
||||
"""Ajouter des reperes de coupe sur les pages.
|
||||
|
||||
Les marques sont placees a l'exterieur de la zone d'impression.
|
||||
Chaque coin a 2 traits (1 horizontal + 1 vertical) formant un L.
|
||||
La page est agrandie pour contenir les marques.
|
||||
"""
|
||||
reader = PdfReader(input_path)
|
||||
writer = PdfWriter()
|
||||
|
||||
margin = mark_offset + mark_length
|
||||
|
||||
for i, page in enumerate(reader.pages):
|
||||
page_num = i + 1
|
||||
should_apply = False
|
||||
@@ -27,46 +34,62 @@ def add_crop_marks(input_path, output_path, mark_length=14.17, mark_offset=7.09,
|
||||
|
||||
if should_apply:
|
||||
w, h = get_page_size(page)
|
||||
# Canvas agrandi pour inclure les marques
|
||||
cw = w + 2 * margin
|
||||
ch = h + 2 * margin
|
||||
|
||||
packet = io.BytesIO()
|
||||
c = canvas.Canvas(packet, pagesize=(w, h))
|
||||
c = canvas.Canvas(packet, pagesize=(cw, ch))
|
||||
c.setLineWidth(line_width)
|
||||
c.setStrokeColorRGB(*color)
|
||||
|
||||
# 4 coins
|
||||
corners = [(0, 0), (w, 0), (0, h), (w, h)]
|
||||
for cx, cy in corners:
|
||||
# Traits horizontaux
|
||||
if cx == 0:
|
||||
c.line(cx - mark_offset - mark_length, cy, cx - mark_offset, cy)
|
||||
else:
|
||||
c.line(cx + mark_offset, cy, cx + mark_offset + mark_length, cy)
|
||||
# Traits verticaux
|
||||
if cy == 0:
|
||||
c.line(cx, cy - mark_offset - mark_length, cx, cy - mark_offset)
|
||||
else:
|
||||
c.line(cx, cy + mark_offset, cx, cy + mark_offset + mark_length)
|
||||
# Les coordonnees de la zone d'impression dans le canvas agrandi
|
||||
ox, oy = margin, margin # origine de la page originale
|
||||
|
||||
# 4 coins — 2 traits par coin (L exterieur)
|
||||
# Bas-gauche
|
||||
c.line(ox - mark_offset - mark_length, oy, ox - mark_offset, oy)
|
||||
c.line(ox, oy - mark_offset - mark_length, ox, oy - mark_offset)
|
||||
# Bas-droite
|
||||
c.line(ox + w + mark_offset, oy, ox + w + mark_offset + mark_length, oy)
|
||||
c.line(ox + w, oy - mark_offset - mark_length, ox + w, oy - mark_offset)
|
||||
# Haut-gauche
|
||||
c.line(ox - mark_offset - mark_length, oy + h, ox - mark_offset, oy + h)
|
||||
c.line(ox, oy + h + mark_offset, ox, oy + h + mark_offset + mark_length)
|
||||
# Haut-droite
|
||||
c.line(ox + w + mark_offset, oy + h, ox + w + mark_offset + mark_length, oy + h)
|
||||
c.line(ox + w, oy + h + mark_offset, ox + w, oy + h + mark_offset + mark_length)
|
||||
|
||||
# Reperes de centrage
|
||||
if center_marks:
|
||||
mid_x = w / 2
|
||||
mid_y = h / 2
|
||||
mid_x = ox + w / 2
|
||||
mid_y = oy + h / 2
|
||||
ml = mark_length * 0.7
|
||||
# Haut
|
||||
c.line(mid_x, h + mark_offset, mid_x, h + mark_offset + ml)
|
||||
c.line(mid_x, oy + h + mark_offset, mid_x, oy + h + mark_offset + ml)
|
||||
# Bas
|
||||
c.line(mid_x, -mark_offset, mid_x, -mark_offset - ml)
|
||||
c.line(mid_x, oy - mark_offset, mid_x, oy - mark_offset - ml)
|
||||
# Gauche
|
||||
c.line(-mark_offset, mid_y, -mark_offset - ml, mid_y)
|
||||
c.line(ox - mark_offset, mid_y, ox - mark_offset - ml, mid_y)
|
||||
# Droite
|
||||
c.line(w + mark_offset, mid_y, w + mark_offset + ml, mid_y)
|
||||
c.line(ox + w + mark_offset, mid_y, ox + w + mark_offset + ml, mid_y)
|
||||
|
||||
c.showPage()
|
||||
c.save()
|
||||
packet.seek(0)
|
||||
overlay = PdfReader(packet)
|
||||
page.merge_page(overlay.pages[0])
|
||||
overlay_reader = PdfReader(packet)
|
||||
overlay_page = overlay_reader.pages[0]
|
||||
|
||||
writer.add_page(page)
|
||||
# Creer une nouvelle page agrandie
|
||||
new_page = PageObject.create_blank_page(width=cw, height=ch)
|
||||
# Placer la page originale au centre (decalee de margin)
|
||||
page.add_transformation(Transformation().translate(margin, margin))
|
||||
new_page.merge_page(page)
|
||||
# Ajouter les marques de coupe par-dessus
|
||||
new_page.merge_page(overlay_page)
|
||||
writer.add_page(new_page)
|
||||
else:
|
||||
writer.add_page(page)
|
||||
|
||||
with open(output_path, "wb") as f:
|
||||
writer.write(f)
|
||||
|
||||
39
app/engine/disassemble.py
Normal file
39
app/engine/disassemble.py
Normal file
@@ -0,0 +1,39 @@
|
||||
"""Moteur Demonter les pages — couper en 2 sans reordonner."""
|
||||
|
||||
from pypdf import PdfReader, PdfWriter, PageObject, Transformation
|
||||
from .pdf_utils import get_page_size
|
||||
|
||||
|
||||
def disassemble_pages(input_path, output_path):
|
||||
"""Demonter un PDF : couper chaque page en 2 moities (gauche puis droite).
|
||||
|
||||
Contrairement a Delivretiser, les pages ne sont PAS remises dans l'ordre
|
||||
du livret. On obtient simplement 2x plus de pages dans l'ordre de lecture.
|
||||
"""
|
||||
reader = PdfReader(input_path)
|
||||
writer = PdfWriter()
|
||||
|
||||
for page in reader.pages:
|
||||
w, h = get_page_size(page)
|
||||
half_w = w / 2
|
||||
|
||||
# Moitie gauche
|
||||
left_page = PageObject.create_blank_page(width=half_w, height=h)
|
||||
left_overlay = PageObject.create_blank_page(width=half_w, height=h)
|
||||
left_overlay.merge_page(page)
|
||||
left_overlay.mediabox.upper_right = (half_w, h)
|
||||
left_page.merge_page(left_overlay)
|
||||
writer.add_page(left_page)
|
||||
|
||||
# Moitie droite
|
||||
right_page = PageObject.create_blank_page(width=half_w, height=h)
|
||||
right_overlay = PageObject.create_blank_page(width=half_w, height=h)
|
||||
right_overlay.merge_page(page)
|
||||
right_overlay.add_transformation(Transformation().translate(-half_w, 0))
|
||||
right_overlay.mediabox.upper_right = (half_w, h)
|
||||
right_page.merge_page(right_overlay)
|
||||
writer.add_page(right_page)
|
||||
|
||||
with open(output_path, "wb") as f:
|
||||
writer.write(f)
|
||||
return output_path
|
||||
@@ -10,7 +10,8 @@ def apply_masking(input_path, output_path, rectangles, apply_to="all"):
|
||||
"""Appliquer des rectangles blancs de masquage.
|
||||
|
||||
rectangles: liste de dicts {"x": pt, "y": pt, "width": pt, "height": pt}
|
||||
apply_to: "all", "even", "odd", ou un numero de page (1-based)
|
||||
apply_to: "all", "even", "odd", un numero de page (int, 1-based),
|
||||
ou une liste d'indices (0-based)
|
||||
"""
|
||||
reader = PdfReader(input_path)
|
||||
writer = PdfWriter()
|
||||
@@ -27,6 +28,8 @@ def apply_masking(input_path, output_path, rectangles, apply_to="all"):
|
||||
should_apply = page_num % 2 != 0
|
||||
elif isinstance(apply_to, int):
|
||||
should_apply = page_num == apply_to
|
||||
elif isinstance(apply_to, list):
|
||||
should_apply = i in apply_to
|
||||
|
||||
if should_apply and rectangles:
|
||||
w, h = get_page_size(page)
|
||||
|
||||
Reference in New Issue
Block a user