Outil d'imposition PDF desktop (PyQt6) remplacant Quite Imposing Plus. 9 fonctions : livret, n-up, step & repeat, imposition manuelle, jointure pages, pages blanches, numerotation, masquage, fonds perdus. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
342 lines
11 KiB
Python
342 lines
11 KiB
Python
"""Fenetre principale de Copydev Imposing Tool."""
|
|
|
|
import os
|
|
import tempfile
|
|
from PyQt6.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
|
QPushButton, QLabel, QFileDialog, QMessageBox,
|
|
QGroupBox, QStatusBar, QMenuBar, QSplitter,
|
|
QProgressBar)
|
|
from PyQt6.QtCore import Qt
|
|
from PyQt6.QtGui import QAction
|
|
|
|
from app.pdf_viewer import PdfViewer
|
|
from app.dialogs.booklet_dialog import BookletDialog
|
|
from app.dialogs.nup_dialog import NupDialog
|
|
from app.dialogs.step_repeat_dialog import StepRepeatDialog
|
|
from app.dialogs.manual_impose_dialog import ManualImposeDialog
|
|
from app.dialogs.join_pages_dialog import JoinPagesDialog
|
|
from app.dialogs.insert_blank_dialog import InsertBlankDialog
|
|
from app.dialogs.page_numbers_dialog import PageNumbersDialog
|
|
from app.dialogs.masking_dialog import MaskingDialog
|
|
from app.dialogs.bleeds_dialog import BleedsDialog
|
|
|
|
from app.engine.booklet import create_booklet
|
|
from app.engine.nup import nup_pages
|
|
from app.engine.step_repeat import step_and_repeat
|
|
from app.engine.manual_impose import manual_impose
|
|
from app.engine.join_pages import join_pages
|
|
from app.engine.insert_blank import insert_blank_pages
|
|
from app.engine.page_numbers import add_page_numbers
|
|
from app.engine.masking import apply_masking
|
|
from app.engine.bleeds import define_bleeds
|
|
|
|
|
|
class MainWindow(QMainWindow):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.setWindowTitle("Copydev Imposing Tool")
|
|
self.setMinimumSize(1000, 700)
|
|
self._current_file = None
|
|
self._result_file = None
|
|
self._setup_menu()
|
|
self._setup_ui()
|
|
self._setup_statusbar()
|
|
|
|
def _setup_menu(self):
|
|
menubar = self.menuBar()
|
|
|
|
# Menu Fichier
|
|
file_menu = menubar.addMenu("Fichier")
|
|
|
|
open_act = QAction("Ouvrir PDF...", self)
|
|
open_act.setShortcut("Ctrl+O")
|
|
open_act.triggered.connect(self._open_file)
|
|
file_menu.addAction(open_act)
|
|
|
|
save_act = QAction("Enregistrer le resultat...", self)
|
|
save_act.setShortcut("Ctrl+S")
|
|
save_act.triggered.connect(self._save_result)
|
|
file_menu.addAction(save_act)
|
|
|
|
file_menu.addSeparator()
|
|
|
|
quit_act = QAction("Quitter", self)
|
|
quit_act.setShortcut("Ctrl+Q")
|
|
quit_act.triggered.connect(self.close)
|
|
file_menu.addAction(quit_act)
|
|
|
|
# Menu Aide
|
|
help_menu = menubar.addMenu("Aide")
|
|
about_act = QAction("A propos...", self)
|
|
about_act.triggered.connect(self._show_about)
|
|
help_menu.addAction(about_act)
|
|
|
|
def _setup_ui(self):
|
|
central = QWidget()
|
|
self.setCentralWidget(central)
|
|
main_layout = QHBoxLayout(central)
|
|
|
|
# === Panneau gauche : boutons ===
|
|
left_panel = QWidget()
|
|
left_panel.setFixedWidth(200)
|
|
left_layout = QVBoxLayout(left_panel)
|
|
left_layout.setContentsMargins(5, 5, 5, 5)
|
|
|
|
# Ouvrir
|
|
btn_open = QPushButton("Ouvrir PDF...")
|
|
btn_open.clicked.connect(self._open_file)
|
|
left_layout.addWidget(btn_open)
|
|
|
|
btn_save = QPushButton("Enregistrer resultat...")
|
|
btn_save.clicked.connect(self._save_result)
|
|
left_layout.addWidget(btn_save)
|
|
|
|
left_layout.addSpacing(10)
|
|
|
|
# Imposition
|
|
grp_impose = QGroupBox("Imposition")
|
|
il = QVBoxLayout(grp_impose)
|
|
|
|
btn_booklet = QPushButton("Creer un livret")
|
|
btn_booklet.clicked.connect(self._do_booklet)
|
|
il.addWidget(btn_booklet)
|
|
|
|
btn_nup = QPushButton("N pages par feuille")
|
|
btn_nup.clicked.connect(self._do_nup)
|
|
il.addWidget(btn_nup)
|
|
|
|
btn_repeat = QPushButton("Repetition")
|
|
btn_repeat.clicked.connect(self._do_step_repeat)
|
|
il.addWidget(btn_repeat)
|
|
|
|
btn_manual = QPushButton("Imposition manuelle")
|
|
btn_manual.clicked.connect(self._do_manual_impose)
|
|
il.addWidget(btn_manual)
|
|
|
|
btn_join = QPushButton("Joindre deux pages")
|
|
btn_join.clicked.connect(self._do_join_pages)
|
|
il.addWidget(btn_join)
|
|
|
|
left_layout.addWidget(grp_impose)
|
|
|
|
# Pages
|
|
grp_pages = QGroupBox("Pages")
|
|
pl = QVBoxLayout(grp_pages)
|
|
|
|
btn_blank = QPushButton("Pages blanches")
|
|
btn_blank.clicked.connect(self._do_insert_blank)
|
|
pl.addWidget(btn_blank)
|
|
|
|
left_layout.addWidget(grp_pages)
|
|
|
|
# Annotations
|
|
grp_annot = QGroupBox("Annotations")
|
|
al = QVBoxLayout(grp_annot)
|
|
|
|
btn_numbers = QPushButton("Numerotation")
|
|
btn_numbers.clicked.connect(self._do_page_numbers)
|
|
al.addWidget(btn_numbers)
|
|
|
|
btn_mask = QPushButton("Masquage")
|
|
btn_mask.clicked.connect(self._do_masking)
|
|
al.addWidget(btn_mask)
|
|
|
|
btn_bleeds = QPushButton("Fonds perdus")
|
|
btn_bleeds.clicked.connect(self._do_bleeds)
|
|
al.addWidget(btn_bleeds)
|
|
|
|
left_layout.addWidget(grp_annot)
|
|
left_layout.addStretch()
|
|
|
|
main_layout.addWidget(left_panel)
|
|
|
|
# === Zone centrale : viewer PDF ===
|
|
self._viewer = PdfViewer()
|
|
self._viewer.page_changed.connect(self._on_page_changed)
|
|
main_layout.addWidget(self._viewer, 1)
|
|
|
|
def _setup_statusbar(self):
|
|
self._statusbar = QStatusBar()
|
|
self.setStatusBar(self._statusbar)
|
|
self._status_label = QLabel("Pret")
|
|
self._statusbar.addWidget(self._status_label, 1)
|
|
self._file_label = QLabel("")
|
|
self._statusbar.addPermanentWidget(self._file_label)
|
|
|
|
def _on_page_changed(self, page, total):
|
|
pass
|
|
|
|
def _open_file(self):
|
|
path, _ = QFileDialog.getOpenFileName(
|
|
self, "Ouvrir un fichier PDF", "", "PDF (*.pdf)"
|
|
)
|
|
if path:
|
|
self._current_file = path
|
|
self._result_file = None
|
|
self._viewer.load_pdf(path)
|
|
self._file_label.setText(os.path.basename(path))
|
|
self._status_label.setText(f"Fichier charge : {os.path.basename(path)}")
|
|
|
|
def _save_result(self):
|
|
source = self._result_file or self._current_file
|
|
if not source:
|
|
QMessageBox.warning(self, "Erreur", "Aucun fichier a enregistrer.")
|
|
return
|
|
|
|
path, _ = QFileDialog.getSaveFileName(
|
|
self, "Enregistrer le resultat", "", "PDF (*.pdf)"
|
|
)
|
|
if path:
|
|
if not path.lower().endswith(".pdf"):
|
|
path += ".pdf"
|
|
import shutil
|
|
shutil.copy2(source, path)
|
|
self._status_label.setText(f"Enregistre : {os.path.basename(path)}")
|
|
|
|
def _require_file(self):
|
|
if not self._current_file:
|
|
QMessageBox.warning(self, "Erreur", "Veuillez d'abord ouvrir un fichier PDF.")
|
|
return False
|
|
return True
|
|
|
|
def _make_temp(self):
|
|
fd, path = tempfile.mkstemp(suffix=".pdf")
|
|
os.close(fd)
|
|
return path
|
|
|
|
def _show_result(self, output_path):
|
|
self._result_file = output_path
|
|
self._viewer.load_pdf(output_path)
|
|
self._status_label.setText("Traitement termine")
|
|
|
|
def _do_booklet(self):
|
|
if not self._require_file():
|
|
return
|
|
dlg = BookletDialog(self)
|
|
if dlg.exec():
|
|
settings = dlg.get_settings()
|
|
output = self._make_temp()
|
|
try:
|
|
create_booklet(self._current_file, output, **settings)
|
|
self._show_result(output)
|
|
except Exception as e:
|
|
QMessageBox.critical(self, "Erreur", str(e))
|
|
|
|
def _do_nup(self):
|
|
if not self._require_file():
|
|
return
|
|
dlg = NupDialog(self)
|
|
if dlg.exec():
|
|
settings = dlg.get_settings()
|
|
output = self._make_temp()
|
|
try:
|
|
nup_pages(self._current_file, output, **settings)
|
|
self._show_result(output)
|
|
except Exception as e:
|
|
QMessageBox.critical(self, "Erreur", str(e))
|
|
|
|
def _do_step_repeat(self):
|
|
if not self._require_file():
|
|
return
|
|
dlg = StepRepeatDialog(self)
|
|
if dlg.exec():
|
|
settings = dlg.get_settings()
|
|
output = self._make_temp()
|
|
try:
|
|
step_and_repeat(self._current_file, output, **settings)
|
|
self._show_result(output)
|
|
except Exception as e:
|
|
QMessageBox.critical(self, "Erreur", str(e))
|
|
|
|
def _do_manual_impose(self):
|
|
if not self._require_file():
|
|
return
|
|
from pypdf import PdfReader
|
|
reader = PdfReader(self._current_file)
|
|
dlg = ManualImposeDialog(total_pages=len(reader.pages), parent=self)
|
|
if dlg.exec():
|
|
settings = dlg.get_settings()
|
|
output = self._make_temp()
|
|
try:
|
|
manual_impose(self._current_file, output, **settings)
|
|
self._show_result(output)
|
|
except Exception as e:
|
|
QMessageBox.critical(self, "Erreur", str(e))
|
|
|
|
def _do_join_pages(self):
|
|
if not self._require_file():
|
|
return
|
|
dlg = JoinPagesDialog(self)
|
|
if dlg.exec():
|
|
settings = dlg.get_settings()
|
|
output = self._make_temp()
|
|
try:
|
|
join_pages(self._current_file, output, **settings)
|
|
self._show_result(output)
|
|
except Exception as e:
|
|
QMessageBox.critical(self, "Erreur", str(e))
|
|
|
|
def _do_insert_blank(self):
|
|
if not self._require_file():
|
|
return
|
|
from pypdf import PdfReader
|
|
reader = PdfReader(self._current_file)
|
|
dlg = InsertBlankDialog(total_pages=len(reader.pages), parent=self)
|
|
if dlg.exec():
|
|
settings = dlg.get_settings()
|
|
output = self._make_temp()
|
|
try:
|
|
insert_blank_pages(self._current_file, output, **settings)
|
|
self._show_result(output)
|
|
except Exception as e:
|
|
QMessageBox.critical(self, "Erreur", str(e))
|
|
|
|
def _do_page_numbers(self):
|
|
if not self._require_file():
|
|
return
|
|
dlg = PageNumbersDialog(self)
|
|
if dlg.exec():
|
|
settings = dlg.get_settings()
|
|
output = self._make_temp()
|
|
try:
|
|
add_page_numbers(self._current_file, output, **settings)
|
|
self._show_result(output)
|
|
except Exception as e:
|
|
QMessageBox.critical(self, "Erreur", str(e))
|
|
|
|
def _do_masking(self):
|
|
if not self._require_file():
|
|
return
|
|
dlg = MaskingDialog(self)
|
|
if dlg.exec():
|
|
settings = dlg.get_settings()
|
|
output = self._make_temp()
|
|
try:
|
|
apply_masking(self._current_file, output, **settings)
|
|
self._show_result(output)
|
|
except Exception as e:
|
|
QMessageBox.critical(self, "Erreur", str(e))
|
|
|
|
def _do_bleeds(self):
|
|
if not self._require_file():
|
|
return
|
|
dlg = BleedsDialog(self)
|
|
if dlg.exec():
|
|
settings = dlg.get_settings()
|
|
output = self._make_temp()
|
|
try:
|
|
define_bleeds(self._current_file, output, **settings)
|
|
self._show_result(output)
|
|
except Exception as e:
|
|
QMessageBox.critical(self, "Erreur", str(e))
|
|
|
|
def _show_about(self):
|
|
QMessageBox.about(
|
|
self,
|
|
"A propos",
|
|
"Copydev Imposing Tool\n\n"
|
|
"Outil d'imposition PDF\n"
|
|
"Remplace Quite Imposing Plus\n\n"
|
|
"Copydev 2026"
|
|
)
|