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:
@@ -40,6 +40,7 @@ from app.engine.crop_marks import add_crop_marks, remove_crop_marks
|
||||
from app.engine.crop import crop_pages
|
||||
from app.engine.add_content import add_text_to_pages, add_image_to_pages
|
||||
from app.engine.unbooklet import unbooklet
|
||||
from app.engine.disassemble import disassemble_pages
|
||||
from app.engine.trim_shift import trim_and_shift
|
||||
from app.engine.extract_content import extract_text_from_page, extract_images_from_page
|
||||
from app.dialogs.trim_shift_dialog import TrimShiftDialog
|
||||
@@ -63,6 +64,7 @@ class MainWindow(QMainWindow):
|
||||
self._result_file = None
|
||||
self._original_file = None
|
||||
self._crop_settings = {}
|
||||
self._bleed_settings = None
|
||||
self._undo_stack = [] # pile de fichiers pour annuler
|
||||
self._redo_stack = []
|
||||
|
||||
@@ -221,11 +223,12 @@ class MainWindow(QMainWindow):
|
||||
self._add_action(impose_menu, "Joindre deux pages", None, self._do_join_pages)
|
||||
impose_menu.addSeparator()
|
||||
self._add_action(impose_menu, "Delivretiser", None, self._do_unbooklet)
|
||||
self._add_action(impose_menu, "Demonter les pages", None, self._do_disassemble)
|
||||
|
||||
# Pages
|
||||
pages_menu = menubar.addMenu("Pages")
|
||||
self._add_action(pages_menu, "Recadrer...", None, self._do_crop)
|
||||
self._add_action(pages_menu, "Trim && Shift...", None, self._do_trim_shift)
|
||||
self._add_action(pages_menu, "Decalage...", None, self._do_trim_shift)
|
||||
pages_menu.addSeparator()
|
||||
self._add_action(pages_menu, "Pivoter +90", None, lambda: self._rotate_current(90))
|
||||
self._add_action(pages_menu, "Pivoter -90", None, lambda: self._rotate_current(-90))
|
||||
@@ -236,6 +239,13 @@ class MainWindow(QMainWindow):
|
||||
self._add_action(pages_menu, "Inserer des pages blanches", None, self._do_insert_blank)
|
||||
self._add_action(pages_menu, "Creer un document test...", None, self._do_create_sample)
|
||||
|
||||
# Affichage
|
||||
view_menu = menubar.addMenu("Affichage")
|
||||
self._rulers_action = QAction("Afficher les regles", self)
|
||||
self._rulers_action.setCheckable(True)
|
||||
self._rulers_action.toggled.connect(self._toggle_rulers)
|
||||
view_menu.addAction(self._rulers_action)
|
||||
|
||||
# Annotations
|
||||
annot_menu = menubar.addMenu("Annotations")
|
||||
self._add_action(annot_menu, "Ajouter du texte...", None, self._do_add_text)
|
||||
@@ -319,6 +329,7 @@ class MainWindow(QMainWindow):
|
||||
rtb.addWidget(self._make_section_label("IMPOSITION"))
|
||||
rtb.addAction("Livret", self._do_booklet)
|
||||
rtb.addAction("Delivretiser", self._do_unbooklet)
|
||||
rtb.addAction("Demonter", self._do_disassemble)
|
||||
rtb.addAction("N-Up", self._do_nup)
|
||||
rtb.addAction("Repetition", self._do_step_repeat)
|
||||
rtb.addAction("Manuel", self._do_manual_impose)
|
||||
@@ -328,7 +339,7 @@ class MainWindow(QMainWindow):
|
||||
# Section Pages
|
||||
rtb.addWidget(self._make_section_label("PAGES"))
|
||||
rtb.addAction("Recadrer", self._do_crop)
|
||||
rtb.addAction("Trim/Shift", self._do_trim_shift)
|
||||
rtb.addAction("Decalage", self._do_trim_shift)
|
||||
rtb.addAction("Pg blanche", self._do_insert_blank)
|
||||
rtb.addAction("Inverser", self._do_reverse_pages)
|
||||
rtb.addAction("Fusionner", self._do_merge)
|
||||
@@ -633,6 +644,22 @@ class MainWindow(QMainWindow):
|
||||
self._hide_progress()
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _do_disassemble(self):
|
||||
if not self._require_file():
|
||||
return
|
||||
self._show_progress()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
disassemble_pages(self._current_file, output)
|
||||
self._show_result(output)
|
||||
self._status_label.setText("Demontage termine — pages coupees en 2")
|
||||
except Exception as e:
|
||||
self._hide_progress()
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def _toggle_rulers(self, checked):
|
||||
self._viewer.set_rulers_visible(checked)
|
||||
|
||||
def _do_trim_shift(self):
|
||||
if not self._require_file():
|
||||
return
|
||||
@@ -768,18 +795,51 @@ class MainWindow(QMainWindow):
|
||||
def _do_masking(self):
|
||||
if not self._require_file():
|
||||
return
|
||||
dlg = MaskingDialog(self)
|
||||
if dlg.exec():
|
||||
from pypdf import PdfReader
|
||||
from app.engine.pdf_utils import get_page_size
|
||||
reader = PdfReader(self._current_file)
|
||||
cur_page = self._viewer.current_page
|
||||
page = reader.pages[cur_page]
|
||||
w, h = get_page_size(page)
|
||||
|
||||
self._mask_dlg = MaskingDialog(
|
||||
page_width_pt=w, page_height_pt=h,
|
||||
total_pages=len(reader.pages),
|
||||
current_page=cur_page,
|
||||
viewer=self._viewer,
|
||||
parent=self
|
||||
)
|
||||
|
||||
def on_mask_accepted():
|
||||
if not hasattr(self, '_mask_dlg') or not self._mask_dlg:
|
||||
return
|
||||
dlg_ref = self._mask_dlg
|
||||
self._mask_dlg = None
|
||||
try:
|
||||
settings = dlg_ref.get_settings()
|
||||
except Exception:
|
||||
return
|
||||
if not settings.get("rectangles"):
|
||||
return
|
||||
self._show_progress()
|
||||
settings = dlg.get_settings()
|
||||
output = self._make_temp()
|
||||
try:
|
||||
apply_masking(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
self._status_label.setText(
|
||||
f"Masquage applique — {len(settings['rectangles'])} zone(s)"
|
||||
)
|
||||
except Exception as e:
|
||||
self._hide_progress()
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
|
||||
def on_mask_rejected():
|
||||
self._mask_dlg = None
|
||||
|
||||
self._mask_dlg.accepted.connect(on_mask_accepted)
|
||||
self._mask_dlg.rejected.connect(on_mask_rejected)
|
||||
self._mask_dlg.show()
|
||||
|
||||
def _do_bleeds(self):
|
||||
if not self._require_file():
|
||||
return
|
||||
@@ -791,6 +851,18 @@ class MainWindow(QMainWindow):
|
||||
try:
|
||||
define_bleeds(self._current_file, output, **settings)
|
||||
self._show_result(output)
|
||||
# Stocker et afficher l'overlay rouge sur le viewer
|
||||
from app.engine.pdf_utils import mm_to_pt
|
||||
self._bleed_settings = {
|
||||
"top": mm_to_pt(settings["bleed_top"]),
|
||||
"bottom": mm_to_pt(settings["bleed_bottom"]),
|
||||
"left": mm_to_pt(settings["bleed_left"]),
|
||||
"right": mm_to_pt(settings["bleed_right"]),
|
||||
}
|
||||
self._viewer.set_bleed_info(self._bleed_settings)
|
||||
self._status_label.setText(
|
||||
f"Fonds perdus definis — zone rouge = bleed"
|
||||
)
|
||||
except Exception as e:
|
||||
self._hide_progress()
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
@@ -804,15 +876,20 @@ class MainWindow(QMainWindow):
|
||||
reader = PdfReader(self._current_file)
|
||||
writer = PdfWriter()
|
||||
for page in reader.pages:
|
||||
if hasattr(page, 'trimbox'):
|
||||
page.trimbox = page.mediabox
|
||||
if hasattr(page, 'bleedbox'):
|
||||
page.bleedbox = page.mediabox
|
||||
# Remettre trimbox et bleedbox a la taille de la mediabox
|
||||
page.trimbox.lower_left = page.mediabox.lower_left
|
||||
page.trimbox.upper_right = page.mediabox.upper_right
|
||||
page.bleedbox.lower_left = page.mediabox.lower_left
|
||||
page.bleedbox.upper_right = page.mediabox.upper_right
|
||||
writer.add_page(page)
|
||||
output = self._make_temp()
|
||||
with open(output, "wb") as f:
|
||||
writer.write(f)
|
||||
self._show_result(output)
|
||||
# Supprimer l'overlay rouge
|
||||
self._bleed_settings = None
|
||||
self._viewer.set_bleed_info(None)
|
||||
self._status_label.setText("Fonds perdus supprimes")
|
||||
except Exception as e:
|
||||
self._hide_progress()
|
||||
QMessageBox.critical(self, "Erreur", str(e))
|
||||
@@ -976,20 +1053,21 @@ class MainWindow(QMainWindow):
|
||||
"crop_right": settings["crop_right"],
|
||||
}
|
||||
|
||||
# Appliquer le crop depuis l'original
|
||||
# Appliquer TOUS les crops cumules depuis l'original
|
||||
self._show_progress()
|
||||
source_file = self._original_file or self._current_file
|
||||
output = self._make_temp()
|
||||
try:
|
||||
crop_pages(source_file, output, **settings)
|
||||
crop_pages(source_file, output, per_page_settings=self._crop_settings)
|
||||
self._result_file = output
|
||||
self._current_file = output
|
||||
self._viewer.load_pdf(output)
|
||||
self._viewer.go_to_page(cur_page)
|
||||
self._page_panel.load_thumbnails(self._viewer)
|
||||
self._hide_progress()
|
||||
total_cropped = len(self._crop_settings)
|
||||
self._status_label.setText(
|
||||
f"Recadrage applique — {len(settings['pages'])} page(s)"
|
||||
f"Recadrage applique — {total_cropped} page(s) recadree(s) au total"
|
||||
)
|
||||
QTimer.singleShot(100, self._viewer.zoom_fit)
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user