5077 lines
183 KiB
Python
5077 lines
183 KiB
Python
#!/usr/bin/python3
|
|
# -*- coding: utf8 -*-
|
|
|
|
from __future__ import print_function
|
|
from __future__ import unicode_literals
|
|
|
|
|
|
# version 3.1.2 : bug fix : autoscale didn't work for a mix of portrait / landscape pages.
|
|
# version 3.1.1 : workaround for a bug appeared in Ubuntu 19 (see the OnDraw function)
|
|
# version 3.1
|
|
# fix a serious bug which prevented auto-scale to work.
|
|
|
|
# version 3.0.6, 09/05/2018
|
|
# Bug fixes
|
|
# Better Linux support
|
|
# No longer uses a temporary file, preview data now in memory
|
|
|
|
# version 3.0.5, 30 / 07 / 2017
|
|
# German Translation added
|
|
|
|
# version 3.0.4
|
|
# New feature : add page numbers (still experimental)
|
|
# Gui : dotted line in the middle of a booklet - Still to be improved
|
|
# No longer uses the tempfiles/preview.pdf temporary file.
|
|
# this is now handled in Memory. Bugs to fix on that feature.
|
|
# To understand the reason for which we have used new_from_bytes and not new_from_data, see here :
|
|
# https://stackoverflow.com/questions/45838863/gio-memoryinputstream-does-not-free-memory-when-closed
|
|
# Fix bug for display of red rectangles when the output page is rotated 90° or 270°
|
|
|
|
PB_version = "3.1.2"
|
|
|
|
|
|
"""
|
|
|
|
website : pdfbooklet.sourceforge.net
|
|
|
|
This software is a computer program whose purpose is to manipulate pdf files.
|
|
|
|
This software is governed by the CeCILL license under French law and
|
|
abiding by the rules of distribution of free software. You can use,
|
|
modify and/ or redistribute the software under the terms of the CeCILL
|
|
license as circulated by CEA, CNRS and INRIA at the following URL
|
|
"http://www.cecill.info".
|
|
|
|
As a counterpart to the access to the source code and rights to copy,
|
|
modify and redistribute granted by the license, users are provided only
|
|
with a limited warranty and the software's author, the holder of the
|
|
economic rights, and the successive licensors have only limited
|
|
liability.
|
|
|
|
In this respect, the user's attention is drawn to the risks associated
|
|
with loading, using, modifying and/or developing or reproducing the
|
|
software by the user in light of its specific status of free software,
|
|
that may mean that it is complicated to manipulate, and that also
|
|
therefore means that it is reserved for developers and experienced
|
|
professionals having in-depth computer knowledge. Users are therefore
|
|
encouraged to load and test the software's suitability as regards their
|
|
requirements in conditions enabling the security of their systems and/or
|
|
data to be ensured and, more generally, to use and operate it in the
|
|
same conditions as regards security.
|
|
|
|
The fact that you are presently reading this means that you have had
|
|
knowledge of the CeCILL license and that you accept its terms.
|
|
|
|
==========================================================================
|
|
"""
|
|
|
|
|
|
"""
|
|
TODO : enregistrer un projet dans un répertoire avec caractères unicode
|
|
vérifier menuAdd
|
|
|
|
selection_s : L'usage de cette variable serait à vérifier. Est-ce que cela ne crée pas de la confusion ?
|
|
Pourquoi ne pas utiliser directement config["options"]["pageSelection"] qu'elle remplace ? Un peu plus long, mais plus facile à déboguer.
|
|
Problème surtout à mettre au point :
|
|
Supposons une liste de fichiers ouverts dans lesquels on a fait une sélection.
|
|
Si on ouvre le gestionnaire de fichier a ajoute un fichier à la liste, la sélection est remise à zéro.
|
|
Ce n'est pas bon, il faudrait seulement ajouter les pages du nouveau fichier.
|
|
C'est assez compliqué à gérer si des fichiers ont été supprimés. Une routine de comparaison
|
|
avant et après avoir ouvert le gesionnaire devrait faire le travail.
|
|
|
|
TODO :
|
|
|
|
Autoscale : Distinguer les options : pour les pages et global ? Pas sûr que ce soit utile.
|
|
Quand un répertoire est sélectionné, avertir
|
|
quand on ouvre un fichier, et puis ensuite un projet qui a plusieurs fichiers, pdfshuffler n'est pas bien mis à jour
|
|
popumenu rotate : les valeurs de la fenêtre transformations ne sont pas mises à jour.
|
|
|
|
bugs
|
|
fichier ini : ouvrir un fichier, ouvrir le fichier ini correspondant. Ne rien changer, fermer,
|
|
le fichier ini est mis à jour à un moment quelconque et souvent toutes les transformations sont remises à zéro.
|
|
Dans la même manipulation , quand on ouvre, il arrive que les modifications d'une page soient conservées
|
|
et pas celle de l'autre page (en cahier)
|
|
|
|
slow mode : si la première feuille est faite entièrement de pages blanches générées (par exemple 2 pour
|
|
pages blanches au début dans une configuraiton à deux pages),
|
|
line 4285, in createNewPdf
|
|
pages[i - 1].append(dataz)
|
|
IndexError: list index out of range
|
|
|
|
petits défauts
|
|
|
|
quand on clique sur les boutons de global rotation, double update du preview (problème des boutons radio)
|
|
|
|
améliorations
|
|
Le tooltip pour le nom de fichier pourrait afficher les valeurs réelles que donneront les différents paramètres
|
|
|
|
"""
|
|
|
|
|
|
|
|
"""
|
|
EXPLANATIONS OF THE WORKFLOW
|
|
|
|
The structure of the program is easy to understand.
|
|
Everything runs around the "config" dictionary which defines how the source pdf files
|
|
must be placed in the output file.
|
|
The content of this dictionary may be viewd at any time by the command "Save project"
|
|
which builds an ini file from this dictionary.
|
|
|
|
The program has two parts :
|
|
1) The PdfRenderer class : It receives the config dictionary,
|
|
and from its content builds the output file, applying the necessary
|
|
transormations to the source pages. These transformations, in Pdf,
|
|
are always handled byt transformation matrices. See Pdf specifications
|
|
for details.
|
|
2) The gui, whose only purpose is to build the config dictionary in an easy way.
|
|
|
|
Workflow :
|
|
1) Normal process is : - the gui creates and updates the config dict.
|
|
- When the Go button is pressed, the config dictionary
|
|
is sent to PdfRenderer which builds the output file
|
|
2) Preview process : To create the preview, a similar process is used.
|
|
- the config dictionary is sent to PdfRenderer, with an
|
|
additional parameter which indicates a page number
|
|
- PdfRenderer creates a pdf file in memory which contains a single page.
|
|
- This page is displayed in the gui by Poppler.
|
|
|
|
Inside config, the pages are named in two different ways :
|
|
- Absolute : 2:25 designates a single page, page 25 of the second file.
|
|
- Positional : 2,1 (line, column) designates any page which is placed
|
|
on line 2, column 1
|
|
|
|
What renders things complicated is that pdf counts pages from the bottom
|
|
left, starting by 0, which is not user friendly. So the program has to convert
|
|
data in a readable format.
|
|
|
|
Another complication is that Pdf defines the center of rotation at the lower left corner,
|
|
which is not user friendly. The rotate function handles this and shifts the image to create
|
|
a centered rotation.
|
|
|
|
Transformations
|
|
|
|
1) When the user clicks on the preview, the selectPage function is launched.
|
|
a) From the mouse coordinates, it determines the page clicked, and builds a page identifier
|
|
which is a list of six values :
|
|
- row and column (Pdf format)
|
|
- file number and page number
|
|
- row and column if the output page is rotated
|
|
then it updates the selected pages list.
|
|
b) it launchs area_expose to update the display
|
|
c) it extracts from config the transformations already defined for this page
|
|
and fills in the gtkEntry widgets which contains the transformations
|
|
|
|
2) When the user changes a value in these widgets, the transformationApply function is launched.
|
|
It reads the values in the gui and updates the config dictionary
|
|
Then it launchs the preview function which will update the preview
|
|
|
|
|
|
|
|
HOWTO
|
|
|
|
to add a parameter, three steps are necessary :
|
|
1) add the code which will use the parameter
|
|
2) add a control in Glade
|
|
3) add a line in makeinifile to write the parameter in the project file
|
|
4) add a line in setupGui to setup the gui from the ini file.
|
|
|
|
|
|
"""
|
|
|
|
"""
|
|
Ubuntu 2016 - dépendences
|
|
|
|
python3-gi
|
|
python3-gi-cairo
|
|
gir1.2-gtk-3.0
|
|
gir1.2-poppler-0.18
|
|
|
|
Installation de pyinstaller
|
|
|
|
sudo pip3 install pyinstaller
|
|
le paquet python-dev est aussi nécessaire (mais pip le trouve)
|
|
|
|
|
|
"""
|
|
|
|
import time, math, string, os, sys, re, shutil, site
|
|
#print(sys.version)
|
|
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
|
|
|
|
try :
|
|
import configparser # Python 3
|
|
from configparser import ConfigParser, RawConfigParser
|
|
except :
|
|
from ConfigParser import ConfigParser, RawConfigParser
|
|
import io
|
|
from collections import defaultdict, OrderedDict
|
|
import subprocess
|
|
from subprocess import Popen, PIPE
|
|
from ctypes import *
|
|
import threading
|
|
import tempfile, io
|
|
import copy
|
|
##import urllib
|
|
##from urllib.parse import urlparse
|
|
##from urllib.request import urljoin
|
|
|
|
|
|
from optparse import OptionParser
|
|
import traceback
|
|
|
|
|
|
import gi
|
|
gi.require_version('Gtk', '3.0')
|
|
gi.require_version('Poppler', '0.18')
|
|
|
|
from gi.repository import Gtk
|
|
from gi.repository import Gdk
|
|
from gi.repository import Poppler
|
|
from gi.repository import Pango
|
|
from gi.repository import Gio, GLib
|
|
from gi.repository import cairo
|
|
|
|
|
|
Gtk.rc_parse("./gtkrc")
|
|
|
|
from pdfbooklet.PyPDF2_G import PdfFileReader, PdfFileWriter
|
|
import pdfbooklet.PyPDF2_G.generic as generic
|
|
|
|
# from pdfbooklet import *
|
|
from pdfbooklet.files_chooser import Chooser
|
|
|
|
import locale #for multilanguage support
|
|
import gettext
|
|
import pdfbooklet.elib_intl3 as elib_intl3
|
|
elib_intl3.install("pdfbooklet", "share/locale")
|
|
|
|
debug_b = 0
|
|
|
|
|
|
|
|
def join_list(my_list, separator) :
|
|
mydata = ""
|
|
if isinstance(my_list, list) :
|
|
for s in my_list :
|
|
mydata += s + separator
|
|
elif isinstance(my_list, dict) :
|
|
for s in my_list :
|
|
try :
|
|
item1 = unicode(my_list[s], "utf-8")
|
|
except :
|
|
item1 = my_list[s]
|
|
mydata += item1 + separator
|
|
crop = len(separator) * -1
|
|
mydata = mydata[0:crop]
|
|
return mydata
|
|
|
|
def get_value(dictionary, key, default = 0) :
|
|
|
|
if not key in dictionary :
|
|
dictionary[key] = default
|
|
return default
|
|
else :
|
|
return dictionary[key]
|
|
|
|
|
|
def unicode2(string, dummy = "") :
|
|
|
|
if sys.version_info[0] == 2 :
|
|
if isinstance(string,unicode) :
|
|
return string
|
|
|
|
try :
|
|
return unicode(string,"utf_8")
|
|
except :
|
|
try :
|
|
# print string, " est ecrit en cp1252"
|
|
return unicode(string,"cp1252")
|
|
except :
|
|
return string # Is this the good option ? Return False or an empty string ?
|
|
#return "inconnu"
|
|
|
|
def printExcept() :
|
|
a,b,c = sys.exc_info()
|
|
for d in traceback.format_exception(a,b,c) :
|
|
print(d, end=' ')
|
|
|
|
def bool_test(value) :
|
|
if isinstance(value, str) :
|
|
try :
|
|
value = int(value)
|
|
except :
|
|
if value.strip().lower() == "true" :
|
|
return True
|
|
else :
|
|
return False
|
|
|
|
return bool(value)
|
|
|
|
def alert(message, type = 0) :
|
|
|
|
dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING,
|
|
Gtk.ButtonsType.CLOSE , message)
|
|
dialog.run()
|
|
dialog.destroy()
|
|
|
|
def showwarning(title, message) :
|
|
"""
|
|
GTK_MESSAGE_INFO,
|
|
GTK_MESSAGE_WARNING,
|
|
GTK_MESSAGE_QUESTION,
|
|
GTK_MESSAGE_ERROR,
|
|
GTK_MESSAGE_OTHER
|
|
"""
|
|
|
|
resetTransform_b = False
|
|
|
|
dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL , Gtk.MessageType.WARNING,
|
|
Gtk.ButtonsType.CLOSE , title)
|
|
|
|
dialog.format_secondary_text(message)
|
|
if "transformWindow" in app.arw :
|
|
app.arw["transformWindow"].set_keep_above(False)
|
|
resetTransform_b = True
|
|
dialog.set_keep_above(True)
|
|
dialog.run()
|
|
dialog.destroy()
|
|
if resetTransform_b == True :
|
|
app.arw["transformWindow"].set_keep_above(True)
|
|
|
|
|
|
def askyesno(title, string) :
|
|
|
|
|
|
dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL , Gtk.MessageType.QUESTION,
|
|
Gtk.ButtonsType.NONE, title)
|
|
dialog.add_button(Gtk.STOCK_YES, True)
|
|
dialog.add_button(Gtk.STOCK_NO, False)
|
|
dialog.format_secondary_text(string)
|
|
dialog.set_keep_above(True)
|
|
rep = dialog.run()
|
|
dialog.destroy()
|
|
return rep
|
|
|
|
def ask_text(parent, message, default=''):
|
|
"""
|
|
Display a dialog with a text entry.
|
|
Returns the text, or None if canceled.
|
|
"""
|
|
d = Gtk.MessageDialog(parent,
|
|
Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
|
|
Gtk.MessageType.QUESTION,
|
|
Gtk.ButtonsType.OK_CANCEL,
|
|
message)
|
|
entry = Gtk.Entry()
|
|
entry.set_text(default)
|
|
entry.show()
|
|
d.vbox.pack_end(entry, True, True, 0)
|
|
entry.connect('activate', lambda _: d.response(Gtk.ResponseType.OK))
|
|
d.set_default_response(Gtk.ResponseType.OK)
|
|
|
|
r = d.run()
|
|
text = entry.get_text()
|
|
if sys.version_info[0] == 2 :
|
|
text = text.decode('utf8')
|
|
d.destroy()
|
|
if r == Gtk.ResponseType.OK:
|
|
return text
|
|
else:
|
|
return None
|
|
|
|
|
|
|
|
|
|
class myConfigParser() :
|
|
def __init__(self) :
|
|
pass
|
|
|
|
def read(self,
|
|
iniFile_s,
|
|
encoding = "utf8",
|
|
comments = False) :
|
|
# ----
|
|
# read ini file and creates a dictionary
|
|
# @param iniFile_s : ini file path
|
|
# @param comments : if True, comments are included in the config. Otherwise they are skipped
|
|
# @return : True if successful, False otherwise
|
|
|
|
|
|
myconfig = OrderedDict()
|
|
if not os.path.isfile(iniFile_s) :
|
|
return False
|
|
|
|
try : # if pdfbooklet.cfg is invalid, don't block the program
|
|
if sys.version_info[0] == 3 :
|
|
fileIni = open(iniFile_s, "r", encoding = "utf8")
|
|
else :
|
|
fileIni = open(iniFile_s, "r")
|
|
# If BOM present, skip the first three bytes
|
|
isBOM_s = fileIni.read(3)
|
|
if isBOM_s == chr(239) + chr(187) + chr(191) : # There is a BOM, skips it
|
|
pass
|
|
else :
|
|
fileIni.seek(0) # No BOM, come back to beginning of file
|
|
|
|
except :
|
|
myconfig = OrderedDict()
|
|
myconfig["mru"] = OrderedDict()
|
|
myconfig["mru2"] = OrderedDict()
|
|
myconfig["options"] = OrderedDict()
|
|
return myconfig
|
|
|
|
section_s = ""
|
|
while True :
|
|
record_s = fileIni.readline()
|
|
if record_s == "" : # end of file
|
|
break
|
|
# format line : strip and replace possible \ by /
|
|
record_s = record_s.strip()
|
|
if sys.version_info[0] == 2 :
|
|
record_s = record_s.decode("utf8")
|
|
record_s = record_s.replace("\\", "/") # TODO : or better : formatPath()
|
|
# If the line is a section
|
|
if record_s[0:1] == "[" and record_s[-1:] == "]" : # section
|
|
section_s = record_s[1:-1]
|
|
myconfig[section_s] = OrderedDict()
|
|
else :
|
|
# Skip useless lines
|
|
if section_s == "" : # comment in the beginning of the file
|
|
continue
|
|
if len(record_s) == 0 : # empty line
|
|
continue
|
|
if record_s[0:1] == "#" : # comment
|
|
comment_b = True
|
|
else :
|
|
comment_b = False
|
|
if comments == False : # Skip comments
|
|
if comment_b == True :
|
|
continue
|
|
|
|
|
|
# otherwise, store data in section
|
|
# TODO : comments
|
|
record_data = record_s.split("=")
|
|
if len(record_data) > 1 :
|
|
key = record_data[0].strip()
|
|
linedata = record_data[1].strip()
|
|
if linedata == "False" :
|
|
linedata = False
|
|
if linedata == "True" :
|
|
linedata = True
|
|
myconfig[section_s][key] = linedata
|
|
return myconfig
|
|
|
|
def write(self, myconfig, filename) :
|
|
if sys.version_info[0] == 3 :
|
|
iniFile = open(filename, "w", encoding = "utf8")
|
|
else :
|
|
iniFile = open(filename, "w")
|
|
for a in myconfig :
|
|
iniFile.write("[" + a + "]\n")
|
|
for b in myconfig[a] :
|
|
value = myconfig[a][b]
|
|
if value == True :
|
|
value = '1'
|
|
elif value == False :
|
|
value = '0'
|
|
data1 = (b + " = " + value + "\n").encode("utf8") # En python 3 cette ligne convertit en bytes !!!
|
|
data1 = (b + " = " + value + "\n")
|
|
iniFile.write(data1)
|
|
iniFile.write("\n")
|
|
iniFile.close()
|
|
return True
|
|
|
|
class TxtOnly :
|
|
def __init__(self,
|
|
render,
|
|
pdfList = None,
|
|
pageSelection = None):
|
|
|
|
global config, rows_i, columns_i, step_i, sections, output, input1, adobe_l, inputFiles_a, inputFile_a
|
|
global numfolio, prependPages, appendPages, ref_page, selection, PSSelection
|
|
global numPages, pagesSel, llx_i, lly_i, urx_i, ury_i, mediabox_l
|
|
global ouputFile, optionsDict, selectedIndex_a, selected_page, deletedIndex_a, app
|
|
global arw
|
|
## elib_intl.install("pdfbooklet", "share/locale")
|
|
|
|
if None != pdfList :
|
|
inputFiles_a = pdfList
|
|
self.loadPdfFiles()
|
|
else :
|
|
inputFiles_a = {}
|
|
inputFile_a = {}
|
|
self.permissions_i = -1 # all permissions
|
|
self.password_s = ""
|
|
rows_i = 1
|
|
columns_i = 2
|
|
urx_i = 200
|
|
ury_i = 200
|
|
optionsDict = {}
|
|
adobe_l = 0.3527
|
|
|
|
self.radioSize = 1
|
|
self.radioDisp = 1
|
|
self.repeat = 0
|
|
self.booklet = 1
|
|
self.righttoleft = 0
|
|
self.delete_rectangle = []
|
|
|
|
def openProject2(self, filename_u) :
|
|
# Called by OpenProject and OpenMru (in case the selected item was a project)
|
|
global config, openedProject_u, preview_b, project_b
|
|
|
|
if os.path.isfile(filename_u):
|
|
openedProject_u = filename_u
|
|
## self.arw["window1"].set_title(u"Pdf-Booklet [ " + PB_version + " ] - " + filename_u)
|
|
preview_b = False
|
|
project_b = True
|
|
self.parseIniFile(filename_u)
|
|
preview_b = True
|
|
project_b = False
|
|
return True
|
|
|
|
|
|
def readNumEntry(self, entry, widget_s = "") :
|
|
|
|
if isinstance(entry, int) :
|
|
return float(entry)
|
|
elif isinstance(entry, str) :
|
|
value = entry
|
|
else :
|
|
value = entry.get_text()
|
|
value = value.replace(",", ".")
|
|
if value == "" : value = 0
|
|
try :
|
|
value = float(value)
|
|
except :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
|
|
return None
|
|
|
|
return value
|
|
|
|
|
|
def readmmEntry(self, entry, widget_s = "", default = 0) :
|
|
global adobe_l
|
|
|
|
value = ""
|
|
if isinstance(entry, str) :
|
|
value = entry
|
|
else :
|
|
value = entry.get_text()
|
|
value = value.replace(",", ".")
|
|
if (value == "") :
|
|
value = default
|
|
else :
|
|
try :
|
|
value = float(value) / adobe_l
|
|
except :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
|
|
return None
|
|
return value
|
|
|
|
def readPercentEntry(self, entry, widget_s = "") :
|
|
|
|
value = ""
|
|
if sys.version_info[0] == 2 and isinstance(entry, unicode) :
|
|
value = entry
|
|
elif isinstance(entry, str) :
|
|
value = entry
|
|
else :
|
|
value = entry.get_text()
|
|
value = value.replace(",", ".")
|
|
if (value == "") :
|
|
value = 100
|
|
else :
|
|
value.replace("%", "")
|
|
try :
|
|
value = float(value) / 100
|
|
if value < 0 :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be > 0. Aborting \n") % widget_s)
|
|
return None
|
|
except :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
|
|
return None
|
|
return value
|
|
|
|
def readIntEntry(self, entry, widget_s = "", type_i = 0, default = 0) :
|
|
# type = 0 : accepts all values >= 0
|
|
# type = 1 : accepts all values > 0
|
|
# type = -1 : accepts any integer, positive or negative
|
|
# type = 2 : optional. Don't warn if missing, but warn if invalid (not integer)
|
|
|
|
value = ""
|
|
if isinstance(entry, str) :
|
|
value = entry
|
|
else :
|
|
value = entry.get_text()
|
|
value = value.replace(",", ".")
|
|
|
|
|
|
try :
|
|
value = int(value)
|
|
if type_i == 0 :
|
|
if value < 0 :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be >= 0. Aborting \n") % widget_s)
|
|
return None
|
|
elif type_i == 1 :
|
|
if value < 1 :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be > 0. Aborting \n") % widget_s)
|
|
return None
|
|
except :
|
|
if value == "" :
|
|
if type_i == 2 :
|
|
pass
|
|
else :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
|
|
return None
|
|
else :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
|
|
return None
|
|
if value == "" :
|
|
return default
|
|
elif value == 0 and default > 0 :
|
|
return default
|
|
return value
|
|
|
|
def readBoolean(self, entry) :
|
|
|
|
value = ""
|
|
|
|
if isinstance(entry, str) :
|
|
if entry.strip().lower() == "true" :
|
|
value = True
|
|
else :
|
|
value = False
|
|
elif isinstance(entry, bool) :
|
|
return entry
|
|
else :
|
|
value = entry.get_text()
|
|
try :
|
|
if int(value) < 1 :
|
|
return False
|
|
else :
|
|
return True
|
|
except :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be 0 or 1. Aborting \n") % widget_s)
|
|
|
|
|
|
def parseIniFile(self, inifile = "") :
|
|
|
|
global config, rows_i, columns_i, step_i, cells_i, input1, adobe_l
|
|
global numfolio, prependPages, appendPages, ref_page, selection
|
|
global numPages, pagesSel, llx_i, lly_i, urx_i, ury_i, inputFile, inputFiles_a
|
|
global startup_b
|
|
|
|
|
|
config = parser.read(inifile)
|
|
|
|
|
|
# migration to 2.4 format : copy xxxx1 values to xxxx and delete xxxx1
|
|
for section in config :
|
|
for option in config[section] :
|
|
if option in["htranslate1", "vtranslate1", "scale1", "rotate1", "xscale1", "yscale1"] : # Clean no longer used data
|
|
if option[:-1] in config[section] :
|
|
config[section][option[:-1]] = config[section][option]
|
|
del config[section][option]
|
|
|
|
|
|
|
|
# store in dictionary
|
|
self.pagesTr = config
|
|
if not "options" in config :
|
|
config["options"] = OrderedDict()
|
|
|
|
for section in config :
|
|
if not section in self.pagesTr :
|
|
self.pagesTr[section] = OrderedDict()
|
|
|
|
# inputs
|
|
|
|
if startup_b == 0 :
|
|
if "options" in config:
|
|
if "inputs" in config["options"] :
|
|
|
|
|
|
temp1 = config["options"]["inputs"]
|
|
inputFiles_a = {}
|
|
for filename in temp1.split("|") :
|
|
if os.path.isfile(filename) :
|
|
pdfFile = PdfFileReader(open(filename, "rb"))
|
|
numpages = pdfFile.getNumPages()
|
|
path, shortFileName = os.path.split(filename)
|
|
i = len(inputFiles_a)
|
|
inputFiles_a[i + 1] = filename
|
|
|
|
self.loadPdfFiles()
|
|
if "pageselection" in config["options"] :
|
|
self.selection_s = config["options"]["pageselection"]
|
|
|
|
|
|
|
|
# variables
|
|
if "options" in config:
|
|
if "booklet" in config["options"] :
|
|
self.booklet = int(config["options"]["booklet"])
|
|
|
|
|
|
# multi-line entries
|
|
|
|
if "options" in config:
|
|
if "userLayout" in config["options"] :
|
|
layout_s = config["options"]["userLayout"]
|
|
config["options"]["userLayout"] = layout_s.replace("/", "\n")
|
|
(a,b,c,d) = self.parse_user_layout(layout_s)
|
|
self.imposition = a
|
|
|
|
|
|
|
|
def setOption(self, option, default = "") :
|
|
if option in config["options"] :
|
|
result = config["options"][option]
|
|
else :
|
|
result = default
|
|
if isinstance(default, int) :
|
|
try :
|
|
return int(result)
|
|
except :
|
|
return default
|
|
def parse_user_layout(self, layout_s) :
|
|
|
|
if layout_s.strip() == "" :
|
|
return ([], 0 , 0 , [])
|
|
|
|
layout_s = layout_s.replace("/", "\n")
|
|
lines = layout_s.split("\n")
|
|
imposition = []
|
|
lines2 = []
|
|
for line in lines :
|
|
if line.strip() == "" : # correct errors : ignore blank lines
|
|
continue
|
|
if line[0:1] == "#" : # ignore comments
|
|
continue
|
|
if line[0:4] == "====" : # New sheet
|
|
imposition.append(lines2)
|
|
lines2 = []
|
|
else :
|
|
lines2.append(line)
|
|
if len(lines2) > 0 :
|
|
imposition.append(lines2)
|
|
|
|
numrows = len(lines2)
|
|
cols = lines2[0].split(",")
|
|
numcols = 0
|
|
for a in cols :
|
|
if a.strip() != "" :
|
|
numcols += 1
|
|
|
|
imposition2 = []
|
|
for lines2 in imposition :
|
|
pages = []
|
|
for line in lines2 :
|
|
line = line.split(",")
|
|
for a in line :
|
|
if a.strip() != "" : # correct errors : ignore trailing comma
|
|
pages.append(a.strip())
|
|
imposition2.append(pages)
|
|
self.imposition = imposition2
|
|
|
|
return (imposition2, numrows, numcols, pages)
|
|
|
|
def loadPdfFiles(self) :
|
|
global inputFile_a, inputFiles_a, pagesIndex_a, refPageSize_a
|
|
|
|
i = 1
|
|
inputFile_a = {}
|
|
inputFile_details = {}
|
|
for key in inputFiles_a :
|
|
val = inputFiles_a[key]
|
|
if os.path.isfile(val) :
|
|
inputFile_a[val] = PdfFileReader(open(val, "rb"))
|
|
inputFile_details[val] = {}
|
|
if inputFile_a[val].getIsEncrypted() :
|
|
inputFile_details[val]["encrypt"] = True
|
|
if not hasattr(inputFile_a[val], "_decryption_key") : # if not already decrypted
|
|
password = get_text(None, _("Please, enter the password for this file"))
|
|
if password != None :
|
|
password = password.encode("utf8")
|
|
inputFile_a[val].decrypt(password) # Encrypted file
|
|
if key == 1 : # we get permissions and password from the first file
|
|
(a,b,self.permissions_i) = inputFile_a[val].getPermissions()
|
|
self.password_s = password
|
|
inputFile_details[val]["password"] = password
|
|
selectedIndex_a = {}
|
|
deletedIndex_a = {}
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
def output_page_size(self, radiosize, ref_file = 1, ref_page = 0, logdata = 1) :
|
|
|
|
|
|
global config, rows_i, columns_i, sections, output, adobe_l, inputFiles_a, inputFile_a
|
|
global numPages, pagesSel, llx_i, lly_i, urx_i, ury_i, mediabox_l, outputScale, refPageSize_a
|
|
|
|
# Ouput page size
|
|
if ref_file in inputFiles_a :
|
|
fileName = inputFiles_a[ref_file]
|
|
fileName = unicode2(fileName)
|
|
try :
|
|
page0 = inputFile_a[fileName].getPage(ref_page)
|
|
except :
|
|
print(_("The reference page is invalid. We use the first page"))
|
|
page0 = inputFile_a[fileName].getPage(0)
|
|
|
|
llx_i=page0.mediaBox.getLowerLeft_x()
|
|
lly_i=page0.mediaBox.getLowerLeft_y()
|
|
urx_i=page0.mediaBox.getUpperRight_x()
|
|
ury_i=page0.mediaBox.getUpperRight_y()
|
|
|
|
urx_i=float(urx_i) - float(llx_i)
|
|
ury_i=float(ury_i) - float(lly_i)
|
|
|
|
refPageSize_a = [urx_i, ury_i]
|
|
else :
|
|
alert(_("Reference page invalid, there is no file n°" + str(ref_file)))
|
|
return False
|
|
|
|
|
|
#££self.print2 (_("Size of source file = %s mm x %s mm ") % (int(urx_i * adobe_l), int(ury_i * adobe_l)), 1)
|
|
|
|
oWidth_i = urx_i * columns_i
|
|
oHeight_i = ury_i * rows_i
|
|
|
|
if radiosize == 1 :
|
|
mediabox_l = [oWidth_i, oHeight_i]
|
|
elif radiosize == 2 : # size = no change
|
|
if oWidth_i < oHeight_i : # set orientation
|
|
mediabox_l = [urx_i, ury_i]
|
|
else :
|
|
mediabox_l = [ury_i, urx_i]
|
|
|
|
# calculate the scale factor
|
|
deltaW = mediabox_l[0] / oWidth_i
|
|
deltaH = mediabox_l[1] / oHeight_i
|
|
if deltaW < deltaH :
|
|
outputScale = deltaW
|
|
else :
|
|
outputScale = deltaH
|
|
|
|
|
|
elif radiosize == 3 : # user defined
|
|
|
|
customX = self.readNumEntry(app.arw["outputWidth"], _("Width"))
|
|
if customX == None : return False
|
|
customY = self.readNumEntry(app.arw["outputHeight"], _("Height"))
|
|
if customY == None : return False
|
|
|
|
|
|
mediabox_l = [ customX * (1 / adobe_l), customY * (1 / adobe_l)]
|
|
|
|
|
|
# calculate the scale factor
|
|
deltaW = mediabox_l[0] / oWidth_i
|
|
deltaH = mediabox_l[1] / oHeight_i
|
|
if deltaW < deltaH :
|
|
outputScale = deltaW
|
|
else :
|
|
outputScale = deltaH
|
|
|
|
|
|
outputUrx_i = mediabox_l[0]
|
|
outputUry_i = mediabox_l[1]
|
|
|
|
app.arw["info_fichier_sortie"].set_text(_("%s mm x %s mm ") % (int(outputUrx_i * adobe_l), int(outputUry_i * adobe_l)))
|
|
|
|
|
|
|
|
class dummy:
|
|
def __init__(self) :
|
|
|
|
self.pagesTr = {}
|
|
self.arw = {}
|
|
|
|
|
|
class gtkGui:
|
|
# parameters :
|
|
# render is an instance of pdfRenderer
|
|
# pdfList is a dictionary of path of pdf files : { 1:"...", 2:"...", ... }
|
|
# pageSelection is a list of pages in the form : ["w:x", ... , "y:z"]
|
|
def __init__(self,
|
|
render,
|
|
pdfList = None,
|
|
pageSelection = None):
|
|
|
|
global config, rows_i, columns_i, step_i, sections, output, input1, adobe_l, inputFiles_a, inputFile_a
|
|
global numfolio, prependPages, appendPages, ref_page, selection, PSSelection
|
|
global numPages, pagesSel, llx_i, lly_i, urx_i, ury_i, mediabox_l
|
|
global ouputFile, optionsDict, selectedIndex_a, selected_page, selected_pages_a, selectedeletedIndex_a, app
|
|
|
|
elib_intl3.install("pdfbooklet", "share/locale")
|
|
|
|
if None != pdfList :
|
|
inputFiles_a = pdfList
|
|
ini.loadPdfFiles()
|
|
else :
|
|
inputFiles_a = {}
|
|
inputFile_a = {}
|
|
self.permissions_i = -1 # all permissions
|
|
self.password_s = ""
|
|
rows_i = 1
|
|
columns_i = 2
|
|
step_i = 1
|
|
urx_i = 200
|
|
ury_i = 200
|
|
optionsDict = {}
|
|
adobe_l = 0.3527
|
|
|
|
|
|
areaAllocationH_i = 400
|
|
areaAllocationW_i = 400
|
|
self.freeze_b = False
|
|
self.preview_scale = 1
|
|
self.dev1 = "" # for development needs
|
|
|
|
selectedIndex_a = {}
|
|
selected_page = None
|
|
selected_pages_a = []
|
|
deletedIndex_a = {}
|
|
|
|
|
|
app = self
|
|
self.render = render
|
|
self.ar_pages = []
|
|
self.ar_layout = []
|
|
self.previewPage = 0
|
|
self.clipboard= {}
|
|
self.shuffler = None
|
|
self.imposition = []
|
|
|
|
self.initdrag = []
|
|
self.enddrag = []
|
|
|
|
self.backup = []
|
|
self.backup_index = 0
|
|
self.backup_command = True
|
|
|
|
|
|
self.widgets = Gtk.Builder()
|
|
#self.widgets.set_translation_domain('pdfbooklet')
|
|
self.widgets.add_from_file(sfp2('data/pdfbooklet3.glade'))
|
|
arWidgets = self.widgets.get_objects()
|
|
self.arw = {}
|
|
for z in arWidgets :
|
|
try :
|
|
name = Gtk.Buildable.get_name(z)
|
|
self.arw[name]= z
|
|
z.set_name(name)
|
|
except :
|
|
pass
|
|
|
|
|
|
#autoconnect signals for self functions
|
|
self.widgets.connect_signals(self)
|
|
self.arw["drawingarea1"].connect('draw', self.OnDraw)
|
|
|
|
self.autoscale = self.arw["autoscale"]
|
|
self.area = self.arw["drawingarea1"]
|
|
self.settings = self.arw["settings"]
|
|
self.overwrite = self.arw["overwrite"]
|
|
self.noCompress = self.arw["noCompress"]
|
|
self.slowmode = self.arw["slowMode"]
|
|
self.righttoleft = self.arw["righttoleft"]
|
|
self.status = self.arw["status"]
|
|
|
|
|
|
self.window1 = self.arw["window1"]
|
|
self.window1.show_all()
|
|
self.window1.set_title("Pdf-Booklet [ " + PB_version + " ]")
|
|
## self.window1.connect("destroy", lambda w: Gtk.main_quit())
|
|
self.window1.connect("destroy", self.close_application)
|
|
|
|
"""
|
|
To change the cursor :
|
|
watch_cursor = Gdk.Cursor(Gdk.CursorType.WATCH)
|
|
self.window1.get_window().set_cursor(watch_cursor)
|
|
|
|
## display = self.window1.get_display()
|
|
## watch_cursor = Gdk.Cursor.new_from_name(display, "default")
|
|
|
|
watch_cursor = Gdk.Cursor(Gdk.CursorType.WATCH)
|
|
watch_cursor = Gdk.Cursor(Gdk.CursorType.CROSS)
|
|
self.window1.get_window().set_cursor(watch_cursor)
|
|
"""
|
|
|
|
self.mru_items = {}
|
|
self.menuAdd()
|
|
|
|
self.selection_s = ""
|
|
|
|
# Global transformations
|
|
self.Vtranslate1 = self.arw["vtranslate1"]
|
|
self.scale1 = self.arw["scale1"]
|
|
self.rotation1 = self.arw["rotation1"]
|
|
self.thispage = self.arw["thispage"]
|
|
self.evenpages = self.arw["evenpages"]
|
|
self.oddpages = self.arw["oddpages"]
|
|
|
|
|
|
|
|
self.area.show()
|
|
self.pagesTr = {}
|
|
|
|
# ############ Setup drag motion for drawingarea1 ##############
|
|
|
|
|
|
# setup drag
|
|
## targets = Gtk.TargetList.new([])
|
|
## targets.add_text_targets(0)
|
|
##
|
|
## self.area.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, [],
|
|
## Gdk.DragAction.COPY)
|
|
## self.area.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY)
|
|
## self.area.drag_source_set_target_list(targets)
|
|
## self.area.drag_dest_set_target_list(targets)
|
|
|
|
# self.area is connected to drag_motion in glade
|
|
|
|
|
|
|
|
|
|
# ##################### Themes #################################
|
|
|
|
|
|
menu_themes = Gtk.Menu()
|
|
mem_menu_name = ""
|
|
# Get the list of gtkrc-xxx files in "data", extract the name, and add items to menu
|
|
themes_dict = {}
|
|
|
|
themes_dir = os.path.join(prog_path_u, "share/themes")
|
|
if os.path.isdir(themes_dir) :
|
|
|
|
|
|
for a in os.listdir(themes_dir) : # TODO ££
|
|
rcpath = os.path.join(prog_path_u,a)
|
|
themes_dict[a] = rcpath
|
|
themes_list = themes_dict.keys()
|
|
## themes_list.sort()
|
|
|
|
# Extract similar short names to build submenus
|
|
mem_short_name = ""
|
|
submenus = []
|
|
for menu_name in themes_list :
|
|
short_name = menu_name.split("-")[0]
|
|
if short_name == mem_short_name :
|
|
if short_name not in submenus :
|
|
submenus.append(short_name)
|
|
mem_short_name = short_name
|
|
|
|
# Build first level menu
|
|
sub_dict = {}
|
|
to_del = []
|
|
keys = themes_dict.keys()
|
|
## keys.sort()
|
|
for menu_name in submenus :
|
|
if len(menu_name.strip()) == 0 :
|
|
continue
|
|
sub_dict[menu_name] = Gtk.MenuItem(menu_name)
|
|
menu_themes.append(sub_dict[menu_name])
|
|
sub_dict[menu_name].show()
|
|
|
|
|
|
submenu = Gtk.Menu()
|
|
submenu.show()
|
|
for key in keys :
|
|
rcpath = themes_dict[key]
|
|
short_name = key.split("-")[0]
|
|
if short_name == menu_name :
|
|
commandes = Gtk.MenuItem(key)
|
|
submenu.append(commandes)
|
|
commandes.connect("activate", self.change_theme, rcpath, key)
|
|
commandes.show()
|
|
to_del.append(key)
|
|
|
|
sub_dict[menu_name].set_submenu(submenu)
|
|
|
|
# delete used keys and add the remaining to main menu
|
|
|
|
for key in to_del :
|
|
del themes_dict[key]
|
|
keys = themes_dict.keys()
|
|
## keys.sort()
|
|
|
|
for menu_name in keys :
|
|
if len(menu_name.strip()) == 0 :
|
|
continue
|
|
rcpath = themes_dict[menu_name]
|
|
commandes = Gtk.MenuItem(menu_name)
|
|
menu_themes.append(commandes)
|
|
commandes.connect("activate", self.change_theme, rcpath, menu_name)
|
|
commandes.show()
|
|
|
|
self.arw["themes"].set_submenu(menu_themes)
|
|
aaa = 1
|
|
|
|
|
|
def change_theme(self, widget, path, theme) :
|
|
|
|
try:
|
|
settings_location = os.path.join(site.getsitepackages()[1], "gnome/etc/gtk-3.0/settings.ini")
|
|
except :
|
|
settings_location = os.path.join(prog_path_u, "etc/gtk-3.0/settings.ini")
|
|
f1 = open(settings_location, "w")
|
|
f1.write("[Settings]\n")
|
|
f1.write("gtk-theme-name = " + theme)
|
|
f1.close()
|
|
alert(_("You must restart the program to apply the new theme."))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# this small function returns the type of a widget
|
|
def widget_type(self, widget) :
|
|
try :
|
|
z = widget.class_path()
|
|
z2 = z.split(".")[-1]
|
|
return z2
|
|
except:
|
|
return False
|
|
|
|
|
|
def gtk_delete(self, source=None, event=None):
|
|
Gtk.main_quit()
|
|
|
|
def close_application(self, widget, event=None, mydata=None):
|
|
"""Termination"""
|
|
if self.shuffler != None :
|
|
self.shuffler.close_application("")
|
|
self.shuffler = None
|
|
|
|
if Gtk.main_level():
|
|
self.arw["window1"].destroy()
|
|
Gtk.main_quit()
|
|
Gdk.threads_leave()
|
|
|
|
#os._exit(0)
|
|
return False
|
|
|
|
def file_manager(self,widget):
|
|
global inputFiles_a
|
|
mrudir = self.read_mru2()
|
|
if mrudir == "" :
|
|
mrudir = prog_path_u
|
|
self.chooser = Chooser(inputFiles_a, share_path_u, mrudir)
|
|
inputFiles_a = self.chooser.inputFiles_a
|
|
if len(inputFiles_a) == 0 :
|
|
return
|
|
# add file(s) to most recently used
|
|
self.mru(inputFiles_a)
|
|
self.chooser.chooser.destroy()
|
|
self.chooser = None
|
|
if self.shuffler:
|
|
self.shuffler.model.clear()
|
|
self.shuffler.pdfqueue = []
|
|
self.shuffler.nfile = 0
|
|
for key in inputFiles_a :
|
|
self.shuffler.add_pdf_pages(inputFiles_a[key])
|
|
# TODO : N'est à faire que si la liste des fichiers a changé
|
|
self.shuffler.rendering_thread.pdfqueue = self.shuffler.pdfqueue
|
|
|
|
ini.loadPdfFiles()
|
|
app.selection_s = ""
|
|
|
|
self.previewUpdate()
|
|
|
|
|
|
def FormatPath (
|
|
self,
|
|
path,
|
|
typePath = 0) :
|
|
# Replaces // and \\ by /, but preserves the initial // necessary in urls on a network
|
|
|
|
if path[0:2] == "//" or path[0:2] == ["\\"] :
|
|
prefix_s = "//"
|
|
path = path[2:]
|
|
else :
|
|
prefix_s = ""
|
|
|
|
if typePath == 1 :
|
|
path = path.replace(":", "")
|
|
prefix_s = ""
|
|
path = path.replace("\\", "/")
|
|
path = path.replace("//", "/")
|
|
return(prefix_s + path)
|
|
|
|
|
|
|
|
|
|
def openProject(self, widget, name = "") :
|
|
global config, openedProject_u, preview_b, project_b
|
|
|
|
|
|
|
|
old_dir = self.read_mru2()
|
|
|
|
gtk_chooser = Gtk.FileChooserDialog(title=_('Import...'),
|
|
action=Gtk.FileChooserAction.OPEN,
|
|
buttons=(Gtk.STOCK_CANCEL,
|
|
Gtk.ResponseType.CANCEL,
|
|
Gtk.STOCK_OPEN,
|
|
Gtk.ResponseType.OK))
|
|
gtk_chooser.set_current_folder(old_dir)
|
|
gtk_chooser.set_select_multiple(False)
|
|
|
|
filter_all = Gtk.FileFilter()
|
|
filter_all.set_name(_('All files'))
|
|
filter_all.add_pattern('*')
|
|
gtk_chooser.add_filter(filter_all)
|
|
|
|
filter_ini = Gtk.FileFilter()
|
|
filter_ini.set_name(_('INI files'))
|
|
filter_ini.add_pattern('*.ini')
|
|
gtk_chooser.add_filter(filter_ini)
|
|
gtk_chooser.set_filter(filter_ini)
|
|
|
|
response = gtk_chooser.run()
|
|
if response == Gtk.ResponseType.OK:
|
|
filename = gtk_chooser.get_filename()
|
|
filename_u = unicode2(filename, "utf-8")
|
|
self.mru(filename)
|
|
ini.openProject2(filename_u)
|
|
ini.loadPdfFiles()
|
|
self.setupGui()
|
|
self.arw["previewEntry"].set_text("1")
|
|
self.previewUpdate()
|
|
self.write_mru2(filename_u) # write the location of the opened directory in the cfg file
|
|
|
|
|
|
|
|
## elif response == Gtk.RESPONSE_CANCEL:
|
|
## print(_('Closed, no files selected'))
|
|
gtk_chooser.destroy()
|
|
|
|
def openMru(self, widget) :
|
|
global config, openedProject_u, preview_b, project_b
|
|
global inputFiles_a
|
|
|
|
widget_name = widget.get_name()
|
|
filenames_list_s = self.mru_items[widget_name][1]
|
|
|
|
# are we opening a project file ?
|
|
filename_u = unicode2(filenames_list_s[0], "utf-8")
|
|
extension_s = os.path.splitext(filename_u)[1]
|
|
if extension_s == ".ini" :
|
|
ini.openProject2(filename_u)
|
|
self.selection_s = config["options"]["pageSelection"]
|
|
ini.loadPdfFiles()
|
|
self.setupGui()
|
|
self.arw["previewEntry"].set_text("1")
|
|
self.previewUpdate()
|
|
return
|
|
else :
|
|
ini.parseIniFile(sfp3("pdfbooklet.cfg")) # reset transformations
|
|
self.setupGui(sfp3("pdfbooklet.cfg"))
|
|
|
|
inputFiles_a = {}
|
|
|
|
for filename_s in filenames_list_s :
|
|
filename_u = unicode2(filename_s, "utf-8")
|
|
extension_s = os.path.splitext(filename_u)[1]
|
|
i = len(inputFiles_a)
|
|
inputFiles_a[i + 1] = filename_u
|
|
|
|
|
|
ini.loadPdfFiles()
|
|
app.selection_s = ""
|
|
self.previewUpdate()
|
|
|
|
|
|
if self.shuffler:
|
|
self.shuffler.model.clear()
|
|
self.shuffler.pdfqueue = []
|
|
self.shuffler.nfile = 0
|
|
self.shuffler.npage = 0
|
|
for key in inputFiles_a :
|
|
self.shuffler.add_pdf_pages(inputFiles_a[key])
|
|
# TODO : N'est à faire que si la liste des fichiers a changé
|
|
self.shuffler.rendering_thread.pdfqueue = self.shuffler.pdfqueue
|
|
#for row in self.shuffler.model:
|
|
# row[6] = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def saveProject(self, widget) :
|
|
global openedProject_u
|
|
|
|
if openedProject_u :
|
|
self.saveProjectAs("", openedProject_u)
|
|
else :
|
|
self.saveProjectAs("")
|
|
|
|
|
|
def saveProjectAs(self, widget, filename_u = "") :
|
|
global config, openedProject_u
|
|
|
|
if filename_u == "" :
|
|
|
|
old_dir = self.read_mru2()
|
|
|
|
gtk_chooser = Gtk.FileChooserDialog(title=_('Save project...'),
|
|
action=Gtk.FileChooserAction.SAVE,
|
|
buttons=(Gtk.STOCK_CANCEL,
|
|
Gtk.ResponseType.CANCEL,
|
|
Gtk.STOCK_SAVE,
|
|
Gtk.ResponseType.ACCEPT))
|
|
gtk_chooser.set_do_overwrite_confirmation(True)
|
|
gtk_chooser.set_current_folder(old_dir)
|
|
gtk_chooser.set_current_name("untitled document")
|
|
# or chooser.set_filename("untitled document")
|
|
|
|
response = gtk_chooser.run()
|
|
|
|
if response == Gtk.ResponseType.CANCEL:
|
|
## print(_('Closed, no files selected'))
|
|
gtk_chooser.destroy()
|
|
return
|
|
|
|
elif response == Gtk.ResponseType.ACCEPT:
|
|
filename = gtk_chooser.get_filename()
|
|
filename_u = unicode2(filename, "utf-8")
|
|
if filename_u[-4:] != ".ini" :
|
|
filename_u += ".ini"
|
|
gtk_chooser.destroy()
|
|
self.mru(filename_u)
|
|
|
|
|
|
|
|
openedProject_u = filename_u
|
|
|
|
for section in self.pagesTr :
|
|
if not section in config :
|
|
config[section] = self.pagesTr[section]
|
|
|
|
|
|
|
|
# update with last selections in the gui
|
|
# perhaps we could also update first pagesTr
|
|
self.makeIniFile()
|
|
## for section in out_a :
|
|
## config[section] = out_a[section]
|
|
|
|
|
|
#config.write(iniFile)
|
|
self.write_ordered_config(filename_u)
|
|
self.write_mru2(filename_u) # write the location of the opened directory in the cfg file
|
|
|
|
def write_ordered_config(self, filename_u) :
|
|
global config
|
|
|
|
if sys.version_info[0] == 3 :
|
|
iniFile = open(filename_u, "w", encoding = "utf8")
|
|
else :
|
|
iniFile = open(filename_u, "w")
|
|
|
|
# store data in an ordered dictionary
|
|
out_a = OrderedDict()
|
|
sections_list = list(config.keys())
|
|
for section1 in["options", "mru", "mru2", "output"] :
|
|
if section1 in config :
|
|
out_a[section1] = OrderedDict()
|
|
for option in config[section1] :
|
|
value = config[section1][option]
|
|
if value == 'False' :
|
|
value = False
|
|
elif value == 'True' :
|
|
value = True
|
|
|
|
out_a[section1][option] = value
|
|
sections_list.remove(section1)
|
|
|
|
sections_list.sort()
|
|
for section1 in sections_list :
|
|
out_a[section1] = OrderedDict()
|
|
for option in config[section1] :
|
|
value = config[section1][option]
|
|
if value == 'False' :
|
|
value = False
|
|
elif value == 'True' :
|
|
value = True
|
|
out_a[section1][option] = value
|
|
|
|
# write data
|
|
for section2 in out_a :
|
|
iniFile.write("[" + section2 + "]\n")
|
|
for option2 in out_a[section2] :
|
|
iniFile.write(option2 + " = " + str(out_a[section2][option2]) + "\n")
|
|
|
|
iniFile.close()
|
|
|
|
|
|
|
|
|
|
def mru(self, filenames_a) :
|
|
|
|
mrudir = ""
|
|
if isinstance(filenames_a, dict) :
|
|
filenames = join_list(filenames_a, "|")
|
|
if 1 in filenames_a :
|
|
mrudir = os.path.split(filenames_a[1])[0]
|
|
else :
|
|
filenames = filenames_a
|
|
mrudir = os.path.split(filenames_a)[0]
|
|
|
|
|
|
configtemp = parser.read(sfp3("pdfbooklet.cfg"))
|
|
|
|
#### if configtemp.has_section(section) == False :
|
|
#### configtemp.add_section(section)
|
|
#### configtemp.set(section,option,value)
|
|
##
|
|
##
|
|
if not "mru" in configtemp :
|
|
configtemp["mru"] = {}
|
|
if not "mru2" in configtemp :
|
|
configtemp["mru2"] = {}
|
|
|
|
# cancel if already present
|
|
temp_a = []
|
|
for index in ["mru1", "mru2", "mru3", "mru4"] :
|
|
if index in configtemp["mru"] :
|
|
if filenames == configtemp["mru"][index] :
|
|
return
|
|
|
|
|
|
# shift mru
|
|
if "mru3" in configtemp["mru"] :
|
|
configtemp["mru"]["mru4"] = configtemp["mru"]["mru3"]
|
|
if "mru2" in configtemp["mru"] :
|
|
configtemp["mru"]["mru3"] = configtemp["mru"]["mru2"]
|
|
if "mru1" in configtemp["mru"] :
|
|
configtemp["mru"]["mru2"] = configtemp["mru"]["mru1"]
|
|
# set the new value
|
|
configtemp["mru"]["mru1"] = filenames
|
|
configtemp["mru2"]["mru1"] = mrudir
|
|
## f = open(sfp3("pdfbooklet.cfg"),"w")
|
|
## configtemp.write(f)
|
|
## f.close()
|
|
parser.write(configtemp, sfp3("pdfbooklet.cfg"))
|
|
configtemp = None
|
|
self.menuAdd()
|
|
|
|
|
|
def mru_python2(self, filenames_a) :
|
|
|
|
mrudir = ""
|
|
if isinstance(filenames_a, dict) : # several files selected
|
|
filenames = join_list(filenames_a, "|")
|
|
if 1 in filenames_a : # At least one file
|
|
mrudir = os.path.split(filenames_a[1])[0]
|
|
else :
|
|
filenames = filenames_a
|
|
mrudir = os.path.split(filenames_a)[0]
|
|
|
|
#filenames = filenames.encode('utf-8')
|
|
|
|
configtemp = parser.read(sfp3("pdfbooklet.cfg"))
|
|
|
|
if configtemp.has_section("mru") == False :
|
|
configtemp.add_section("mru")
|
|
if configtemp.has_section("mru2") == False :
|
|
configtemp.add_section("mru2")
|
|
|
|
# cancel if already present
|
|
temp_a = []
|
|
for index in ["mru1", "mru2", "mru3", "mru4"] :
|
|
if configtemp.has_option("mru",index) :
|
|
if filenames == configtemp.get("mru",index) :
|
|
return
|
|
|
|
try :
|
|
# shift mru
|
|
if configtemp.has_option("mru","mru3") :
|
|
configtemp.set("mru","mru4",configtemp.get("mru","mru3"))
|
|
if configtemp.has_option("mru","mru2") :
|
|
configtemp.set("mru","mru3",configtemp.get("mru","mru2"))
|
|
if configtemp.has_option("mru","mru1") :
|
|
configtemp.set("mru","mru2",configtemp.get("mru","mru1"))
|
|
# set the new value
|
|
## filenames_s = filenames.encode("utf-8")
|
|
## mrudir_s = mrudir.encode("utf-8")
|
|
|
|
configtemp.set("mru","mru1",filenames)
|
|
configtemp.set("mru2","mru2",mrudir)
|
|
f = open(sfp3("pdfbooklet.cfg"),"w", encoding = "utf8")
|
|
configtemp.write(f)
|
|
f.close()
|
|
configtemp = None
|
|
self.menuAdd()
|
|
except :
|
|
printExcept()
|
|
alert("problem in mru (line 700)")
|
|
|
|
|
|
|
|
def read_mru2(self) :
|
|
|
|
if os.path.isfile(sfp3("pdfbooklet.cfg")) :
|
|
configtemp = parser.read(sfp3("pdfbooklet.cfg"))
|
|
|
|
mru_dir = ""
|
|
if "mru2" in configtemp :
|
|
if "mru2" in configtemp["mru2"] :
|
|
mru_dir = configtemp["mru2"]["mru2"]
|
|
|
|
configtemp = None
|
|
return mru_dir
|
|
|
|
def read_mru2_python2(self) :
|
|
|
|
if os.path.isfile(sfp3("pdfbooklet.cfg")) :
|
|
configtemp = parser.read(sfp3("pdfbooklet.cfg"))
|
|
|
|
try :
|
|
mru_dir = configtemp.get("mru2","mru2")
|
|
except :
|
|
mru_dir = ""
|
|
|
|
configtemp = None
|
|
return mru_dir
|
|
|
|
def write_mru2(self, filename_u) :
|
|
|
|
if os.path.isfile(sfp3("pdfbooklet.cfg")) :
|
|
configtemp = parser.read(sfp3("pdfbooklet.cfg"))
|
|
|
|
if not "mru2" in configtemp :
|
|
configtemp["mru2"] = OrderedDict()
|
|
|
|
(path_u, file_u) = os.path.split(filename_u)
|
|
configtemp["mru2"]["mru2"] = path_u
|
|
parser.write(configtemp, sfp3("pdfbooklet.cfg"))
|
|
|
|
|
|
def menuAdd(self) :
|
|
# Called by function mru, adds an entry to the menu
|
|
|
|
|
|
configtemp = parser.read(sfp3("pdfbooklet.cfg"))
|
|
if configtemp == False :
|
|
return
|
|
|
|
if "mru" in configtemp :
|
|
for item in ["mru1", "mru2", "mru3", "mru4"] :
|
|
if item in configtemp["mru"] :
|
|
filepath_list_s = configtemp["mru"][item]
|
|
filepath_list = filepath_list_s.split("|")
|
|
temp1 = []
|
|
for filepath_s in filepath_list :
|
|
filepath_s = self.FormatPath(filepath_s)
|
|
path_s,filename_s = os.path.split(filepath_s)
|
|
temp1 += [filename_s]
|
|
menu_entry_s = join_list(temp1, ", ")
|
|
if len(menu_entry_s) > 40 :
|
|
menu_entry_s = menu_entry_s[0:40] + "..."
|
|
|
|
self.mru_items[item] = [menu_entry_s, filepath_list] # contains real path, used to open files
|
|
self.arw[item].set_label(menu_entry_s) # displayed menu text
|
|
|
|
|
|
|
|
|
|
|
|
def pdfBooklet_doc(self, widget) :
|
|
|
|
userGuide_s = "documentation/" + _("Pdf-Booklet_User's_Guide.pdf")
|
|
if 'linux' in sys.platform :
|
|
subprocess.call(["xdg-open", userGuide_s])
|
|
else:
|
|
os.startfile(sfp(userGuide_s))
|
|
|
|
|
|
def popup_rotate(self, widget):
|
|
# Called by popup menu (right clic on preview)
|
|
global selected_page, rows_i
|
|
|
|
|
|
# We get the value from the widget name (the 3 menu options use the same function)
|
|
wname = widget.get_name()
|
|
match = re.match("rotate(\d*)", wname)
|
|
value = match.group(1)
|
|
|
|
# Code below is just an adaptation of function "transormationsApply"
|
|
if selected_page == None :
|
|
showwarning(_("No selection"), _("There is no selected page. \nPlease select a page first. "))
|
|
return
|
|
# selected_page 4 and 5 contain the correct page reference, including the global rotation.
|
|
humanReadableRow_i = rows_i - selected_page[4]
|
|
Id = str(str(humanReadableRow_i) + "," + str(selected_page[5] + 1))
|
|
|
|
pageId = str(selected_page[2]) + ":" + str(selected_page[3])
|
|
|
|
## # if transformation is for this page only, use page ref instead of position ref
|
|
## if self.thispage.get_active() == 1 :
|
|
## Id = pageId
|
|
|
|
if (Id in config) == False :
|
|
config[Id] = {}
|
|
config[Id]["htranslate"] = '0'
|
|
config[Id]["vtranslate"] = '0'
|
|
config[Id]["scale"] = '100'
|
|
config[Id]["xscale"] = '100'
|
|
config[Id]["yscale"] = '100'
|
|
config[Id]["vflip"] = False
|
|
config[Id]["hflip"] = False
|
|
|
|
|
|
|
|
|
|
|
|
config[Id]["rotate"] = str(value)
|
|
|
|
self.preview(self.previewPage, 0)
|
|
|
|
|
|
|
|
def __________________INI_FILE() :
|
|
pass
|
|
|
|
|
|
def saveDefaults(self, dummy) :
|
|
|
|
out_a = self.makeIniFile()
|
|
iniFile = open(sfp3("pdfbooklet.cfg"), "w")
|
|
for a in out_a :
|
|
if not a in ["mru", "mru2", "options"] :
|
|
continue
|
|
iniFile.write("[" + a + "]\n")
|
|
for b in out_a[a] :
|
|
value = out_a[a][b]
|
|
if value == True :
|
|
value = '1'
|
|
elif value == False :
|
|
value = '0'
|
|
iniFile.write(b + " = " + value + "\n")
|
|
iniFile.write("\n")
|
|
iniFile.close()
|
|
|
|
def readNumEntry(self, entry, widget_s = "") :
|
|
|
|
if sys.version_info[0] == 2 and isinstance(entry, unicode) :
|
|
value = entry
|
|
elif isinstance(entry, str) :
|
|
value = entry
|
|
else :
|
|
value = entry.get_text()
|
|
value = value.replace(",", ".")
|
|
if value == "" : value = 0
|
|
try :
|
|
value = float(value)
|
|
except :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
|
|
return None
|
|
|
|
return value
|
|
|
|
|
|
def readmmEntry(self, entry, widget_s = "", default = 0) :
|
|
global adobe_l
|
|
|
|
value = ""
|
|
if sys.version_info[0] == 2 and isinstance(entry, unicode) :
|
|
value = entry
|
|
elif isinstance(entry, str) :
|
|
value = entry
|
|
else :
|
|
value = entry.get_text()
|
|
value = value.replace(",", ".")
|
|
if (value == "") :
|
|
value = default
|
|
else :
|
|
try :
|
|
value = float(value) / adobe_l
|
|
except :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
|
|
return None
|
|
return value
|
|
|
|
def readPercentEntry(self, entry, widget_s = "") :
|
|
|
|
value = ""
|
|
if sys.version_info[0] == 2 and isinstance(entry, unicode) :
|
|
value = entry
|
|
elif isinstance(entry, str) :
|
|
value = entry
|
|
else :
|
|
value = entry.get_text()
|
|
value = value.replace(",", ".")
|
|
if (value == "") :
|
|
value = 100
|
|
else :
|
|
value.replace("%", "")
|
|
try :
|
|
value = float(value) / 100
|
|
if value < 0 :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be > 0. Aborting \n") % widget_s)
|
|
return None
|
|
except :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
|
|
return None
|
|
return value
|
|
|
|
def readIntEntry(self, entry, widget_s = "", type_i = 0, default = "") :
|
|
# type = 0 : accepts all values >= 0
|
|
# type = 1 : accepts all values > 0
|
|
# type = -1 : accepts any integer, positive or negative
|
|
# type = 2 : optional. Don't warn if missing, but warn if invalid (not integer)
|
|
|
|
value = ""
|
|
if isinstance(entry, str) :
|
|
value = entry
|
|
else :
|
|
value = entry.get_text()
|
|
value = value.replace(",", ".")
|
|
|
|
|
|
try :
|
|
value = int(value)
|
|
if type_i == 0 :
|
|
if value < 0 :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be >= 0. Aborting \n") % widget_s)
|
|
return None
|
|
elif type_i == 1 :
|
|
if value < 1 :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be > 0. Aborting \n") % widget_s)
|
|
return None
|
|
except :
|
|
if value == "" :
|
|
if type_i == 2 :
|
|
pass
|
|
else :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
|
|
return None
|
|
else :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
|
|
return None
|
|
if value == "" :
|
|
return default
|
|
return value
|
|
|
|
def readBoolean(self, entry) :
|
|
|
|
value = ""
|
|
if sys.version_info[0] == 2 :
|
|
if isinstance(entry, unicode) :
|
|
value = entry
|
|
elif isinstance(entry, str) :
|
|
value = entry
|
|
else :
|
|
value = entry.get_text()
|
|
try :
|
|
if int(value) < 1 :
|
|
return False
|
|
else :
|
|
return True
|
|
except :
|
|
showwarning(_("Invalid data"), _("Invalid data for %s - must be 0 or 1. Aborting \n") % widget_s)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def readGui(self, logdata = 1) :
|
|
|
|
global config, rows_i, columns_i, step_i, sections, output, input1, input2, adobe_l, inputFiles_a, inputFile_a
|
|
global numfolio, prependPages, appendPages, ref_page, selection
|
|
global numPages, pagesSel, llx_i, lly_i, urx_i, ury_i, mediabox_l, outputScale, refPageSize_a
|
|
|
|
if self.freeze_b == True :
|
|
return
|
|
|
|
# Read the gui and update the config dictionary
|
|
self.makeIniFile()
|
|
|
|
outputFile = config["options"]["output"]
|
|
outputScale = 1
|
|
|
|
rows_i = self.readIntEntry(self.arw["entry11"], _("rows"), 1)
|
|
#if rows_i == None : return False
|
|
columns_i = self.readIntEntry(self.arw["entry12"], _("columns"), 1)
|
|
#if columns_i == None : return False
|
|
if (rows_i < 1) :
|
|
rows_i = 1
|
|
if (columns_i < 1) :
|
|
columns_i = 1
|
|
|
|
|
|
if ini.repeat == 1 :
|
|
step_i = self.readIntEntry(self.arw["entry15"], _("step"), 1)
|
|
if step_i == None : return False
|
|
if (step_i < 1) :
|
|
step_i = 1
|
|
else :
|
|
step_i = rows_i * columns_i
|
|
|
|
|
|
|
|
numfolio = self.readIntEntry(self.arw["entry13"], _("folios"))
|
|
prependPages = self.readIntEntry(self.arw["entry32"], _("Leading blank pages"))
|
|
if prependPages == None : return False
|
|
appendPages = self.readIntEntry(self.arw["entry33"], _("Trailing blank pages"))
|
|
if appendPages == None : return False
|
|
selection = self.selection_s
|
|
|
|
|
|
if self.arw["radiosize1"].get_active() == 1 :
|
|
radiosize = 1
|
|
elif self.arw["radiosize2"].get_active() == 1 :
|
|
radiosize = 2
|
|
if self.arw["radiosize3"].get_active() == 1 :
|
|
radiosize = 3
|
|
|
|
referencePage = config["options"]["referencePage"]
|
|
if referencePage.strip() == "" or referencePage == "0" :
|
|
referencePage = "1"
|
|
temp1 = referencePage.split(":")
|
|
try :
|
|
if len(temp1) == 2 :
|
|
ref_file = int(temp1[0])
|
|
ref_page = int(temp1[1])
|
|
else :
|
|
ref_file = 1
|
|
ref_page = int(temp1[0])
|
|
except :
|
|
alert(_("Invalid value for reference page, please correct and try again"))
|
|
return False
|
|
if ref_page > 0 :
|
|
system_ref_page = ref_page - 1 # system numbering start from 0 and not 1
|
|
ini.output_page_size(radiosize, ref_file, system_ref_page, logdata)
|
|
|
|
return True
|
|
|
|
|
|
|
|
def setOption(self, option, widget, section = "options") :
|
|
global config
|
|
|
|
if section in config :
|
|
if option in config[section] :
|
|
value = config[section][option]
|
|
z = widget.class_path()[1]
|
|
z2 = z.split(".")
|
|
z3 = z2[-1]
|
|
if z3 == "GtkSpinButton" :
|
|
mydata = value
|
|
mydata = mydata.replace(",",".")
|
|
if mydata.strip() != "" :
|
|
widget.set_value(float(mydata))
|
|
elif z3 == "GtkTextView" :
|
|
widget.get_buffer().set_text(value)
|
|
elif z3 == "GtkCheckButton" :
|
|
try :
|
|
value = int(value)
|
|
except :
|
|
pass
|
|
if bool_test(value) == True :
|
|
widget.set_active(True)
|
|
|
|
else :
|
|
widget.set_text(value)
|
|
|
|
|
|
|
|
|
|
def setupGui(self, inifile = "") :
|
|
|
|
global config, rows_i, columns_i, step_i, cells_i, input1, adobe_l
|
|
global numfolio, prependPages, appendPages, ref_page, selection
|
|
global numPages, pagesSel, llx_i, lly_i, urx_i, ury_i, inputFile, inputFiles_a
|
|
global startup_b
|
|
|
|
|
|
self.freeze_b = True # prevent update of the display, which would trigger readGui and corrupt the data
|
|
|
|
# set radio buttons
|
|
|
|
if "presets" in config["options"] :
|
|
temp1 = config["options"]["presets"]
|
|
self.arw[temp1].set_active(True)
|
|
|
|
if "size" in config["options"] :
|
|
temp1 = config["options"]["size"]
|
|
self.arw[temp1].set_active(True)
|
|
|
|
if "presetOrientation" in config["options"] :
|
|
temp1 = config["options"]["presetOrientation"]
|
|
self.arw[temp1].set_active(True)
|
|
|
|
if "globalRotation" in config["options"] :
|
|
temp1 = config["options"]["globalRotation"]
|
|
self.arw[temp1].set_active(True)
|
|
|
|
|
|
self.setOption("rows", self.arw["entry11"])
|
|
self.setOption("columns", self.arw["entry12"])
|
|
self.setOption("step", self.arw["entry15"])
|
|
self.setOption("numfolio", self.arw["entry13"])
|
|
self.setOption("prependPages", self.arw["entry32"])
|
|
self.setOption("appendPages", self.arw["entry33"])
|
|
self.setOption("referencePage", self.arw["entry31"])
|
|
self.setOption("creep", self.arw["creep"])
|
|
self.setOption("output", self.arw["entry2"])
|
|
self.setOption("width", self.arw["outputWidth"])
|
|
self.setOption("height", self.arw["outputHeight"])
|
|
self.setOption("userLayout", self.arw["user_layout"])
|
|
|
|
self.setOption("htranslate", self.arw["htranslate2"], "output")
|
|
self.setOption("vtranslate", self.arw["vtranslate2"], "output")
|
|
self.setOption("scale", self.arw["scale2"], "output")
|
|
self.setOption("rotate", self.arw["rotation2"], "output")
|
|
self.setOption("xscale", self.arw["xscale2"], "output")
|
|
self.setOption("yscale", self.arw["yscale2"], "output")
|
|
self.setOption("vflip", self.arw["vflip2"], "output")
|
|
self.setOption("hflip", self.arw["hflip2"], "output")
|
|
|
|
self.setOption("font_size", self.arw["numbers_font_size"], "page_numbers")
|
|
self.setOption("start_from", self.arw["numbers_start_from"], "page_numbers")
|
|
self.setOption("bottom_margin", self.arw["numbers_bottom_margin"], "page_numbers")
|
|
|
|
|
|
# set check boxes
|
|
|
|
if "advanced" in config["options"] :
|
|
if config.getint("options", "advanced") == 1 : self.advanced.set_active(1)
|
|
else : self.advanced.set_active(0)
|
|
self.guiAdvanced()
|
|
if "autoscale" in config["options"] :
|
|
if bool_test(config["options"]["autoscale"]) == True :
|
|
self.autoscale.set_active(1)
|
|
else :
|
|
self.autoscale.set_active(0)
|
|
if "autoRotate" in config["options"] :
|
|
if int(config["options"]["autoRotate"]) == 1 : self.autorotate.set_active(1)
|
|
else : self.autorotate.set_active(0)
|
|
|
|
if "showPdf" in config["options"] :
|
|
if bool_test(config["options"]["showPdf"]) == True : self.arw["show"].set_active(1)
|
|
else : self.arw["show"].set_active(0)
|
|
if "saveSettings" in config["options"] :
|
|
if bool_test(config["options"]["saveSettings"]) == True : self.settings.set_active(1)
|
|
else : self.settings.set_active(0)
|
|
|
|
if "noCompress" in config["options"] :
|
|
if bool_test(config["options"]["noCompress"]) == True : self.noCompress.set_active(1)
|
|
else : self.noCompress.set_active(0)
|
|
if "overwrite" in config["options"] :
|
|
if bool_test(config["options"]["overwrite"]) == True : self.overwrite.set_active(1)
|
|
else : self.overwrite.set_active(0)
|
|
|
|
if "righttoleft" in config["options"] :
|
|
if bool_test(config["options"]["righttoleft"]) == True : self.righttoleft.set_active(1)
|
|
else : self.righttoleft.set_active(0)
|
|
if "slowmode" in config["options"] :
|
|
if bool_test(config["options"]["slowmode"]) == True : self.slowmode.set_active(1)
|
|
else : self.slowmode.set_active(0)
|
|
|
|
if "page_numbers" in config :
|
|
if "page_numbers" in config["page_numbers"] :
|
|
if bool_test(config["page_numbers"]["page_numbers"]) == True :
|
|
self.arw["page_numbers"].set_active(1)
|
|
else :
|
|
self.arw["page_numbers"].set_active(0)
|
|
|
|
# set TextView
|
|
|
|
buf = self.arw["textview1"].get_buffer()
|
|
if "userLayout" in config["options"] :
|
|
buf.set_text(config["options"]["userLayout"])
|
|
|
|
|
|
self.freeze_b = False
|
|
|
|
|
|
def makeIniFile(self, inifile = "") :
|
|
# Reads the gui and updates config dictionary
|
|
|
|
global config, rows_i, columns_i, step_i, cells_i, input1, adobe_l
|
|
global numfolio, prependPages, appendPages, ref_page, selection
|
|
global numPages, pagesSel, inputFile
|
|
global out_a
|
|
|
|
out_a = config
|
|
|
|
config["options"]["rows"] = self.arw["entry11"].get_text()
|
|
config["options"]["columns"] = self.arw["entry12"].get_text()
|
|
config["options"]["booklet"] = str(ini.booklet)
|
|
config["options"]["step"] = self.arw["entry15"].get_text()
|
|
config["options"]["numfolio"] = self.arw["entry13"].get_text()
|
|
config["options"]["prependPages"] = self.arw["entry32"].get_text()
|
|
config["options"]["appendPages"] = self.arw["entry33"].get_text()
|
|
config["options"]["referencePage"] = self.arw["entry31"].get_text()
|
|
config["options"]["creep"] = self.arw["creep"].get_text()
|
|
config["options"]["pageSelection"] = self.selection_s # TODO : is this a good idea ?
|
|
buf = self.arw["user_layout"].get_buffer()
|
|
start, end = buf.get_bounds()
|
|
layout_s = buf.get_text(start, end, True)
|
|
config["options"]["userLayout"] = layout_s.replace("\n", "/")
|
|
|
|
temp1 = ""
|
|
for key in inputFiles_a :
|
|
val = inputFiles_a[key]
|
|
temp1 += val + '|'
|
|
|
|
config["options"]["inputs"] = temp1
|
|
config["options"]["output"] = self.arw["entry2"].get_text()
|
|
config["options"]["repeat"] = str(self.arw["entry15"].get_text())
|
|
config["options"]["showPdf"] = str(self.arw["show"].get_active())
|
|
config["options"]["saveSettings"] = str(self.settings.get_active())
|
|
config["options"]["autoscale"] = str(self.autoscale.get_active())
|
|
config["options"]["width"] = str(self.arw["outputWidth"].get_text())
|
|
config["options"]["height"] = str(self.arw["outputHeight"].get_text())
|
|
|
|
config["options"]["noCompress"] = str(self.noCompress.get_active())
|
|
config["options"]["righttoleft"] = str(self.righttoleft.get_active())
|
|
config["options"]["overwrite"] = str(self.overwrite.get_active())
|
|
config["options"]["slowmode"] = str(self.slowmode.get_active())
|
|
|
|
if not "output" in config :
|
|
config["output"] = OrderedDict()
|
|
config["output"]["htranslate"] = self.arw["htranslate2"].get_text()
|
|
config["output"]["vtranslate"] = self.arw["vtranslate2"].get_text()
|
|
config["output"]["scale"] = self.arw["scale2"].get_text()
|
|
config["output"]["rotate"] = self.arw["rotation2"].get_text()
|
|
config["output"]["xscale"] = self.arw["xscale2"].get_text()
|
|
config["output"]["yscale"] = self.arw["yscale2"].get_text()
|
|
config["output"]["vflip"] = self.arw["vflip2"].get_active()
|
|
config["output"]["hflip"] = self.arw["hflip2"].get_active()
|
|
|
|
if not "page_numbers" in config :
|
|
config["page_numbers"] = OrderedDict()
|
|
config["page_numbers"]["page_numbers"] = self.arw["page_numbers"].get_active()
|
|
config["page_numbers"]["font_size"] = self.arw["numbers_font_size"].get_text()
|
|
config["page_numbers"]["start_from"] = self.arw["numbers_start_from"].get_text()
|
|
config["page_numbers"]["bottom_margin"] = self.arw["numbers_bottom_margin"].get_text()
|
|
## config["output"]["yscale"] = self.arw["yscale2"].get_text()
|
|
|
|
# radio buttons
|
|
|
|
group = self.arw["radiopreset1"].get_group()
|
|
for a in group :
|
|
if a.get_active() == True :
|
|
config["options"]["presets"] = a.get_name()
|
|
|
|
group = self.arw["radiosize1"].get_group()
|
|
for a in group :
|
|
if a.get_active() == True :
|
|
config["options"]["size"] = a.get_name()
|
|
|
|
group = self.arw["presetOrientation1"].get_group()
|
|
for a in group :
|
|
if a.get_active() == True :
|
|
config["options"]["presetOrientation"] = a.get_name()
|
|
|
|
group = self.arw["globalRotation0"].get_group()
|
|
for a in group :
|
|
if a.get_active() == True :
|
|
config["options"]["globalRotation"] = a.get_name()
|
|
|
|
# most recently used
|
|
|
|
# TODO : if file exists (ici et ailleurs)
|
|
configtemp = parser.read(sfp3("pdfbooklet.cfg"))
|
|
|
|
if "mru" in configtemp :
|
|
for option in configtemp["mru"] :
|
|
if "mru" in config :
|
|
config["mru"][option] = configtemp["mru"][option]
|
|
|
|
|
|
|
|
|
|
|
|
return config
|
|
|
|
|
|
def _______________________PRESETS() :
|
|
pass
|
|
|
|
|
|
def guiPresets(self, radiobutton = 0, event = None) :
|
|
|
|
global startup_b, project_b, preview_b
|
|
|
|
if radiobutton != 0 :
|
|
if radiobutton.get_active() == 0 : # signal is sent twice, ignore one of them
|
|
return
|
|
if project_b == True : # Don't change values if we are loading a project
|
|
return
|
|
|
|
preview_b = False # prevent multiple preview commands due to signals emitted by controls
|
|
presetOrientation_i = self.arw["presetOrientation1"].get_active()
|
|
|
|
if self.arw["radiopreset1"].get_active() == 1 : # single booklet
|
|
if presetOrientation_i == 1 :
|
|
self.presetBooklet(0,0)
|
|
else :
|
|
self.presetBooklet(0,1)
|
|
self.guiPresetsShow("booklet")
|
|
|
|
|
|
elif self.arw["radiopreset2"].get_active() == 1 : # Multiple booklets
|
|
if presetOrientation_i == 1 :
|
|
self.presetBooklet(5,0)
|
|
else :
|
|
self.presetBooklet(5,1)
|
|
self.guiPresetsShow("booklet")
|
|
|
|
elif self.arw["radiopreset3"].get_active() == 1 : # 2-up
|
|
if presetOrientation_i == 1 :
|
|
self.presetUp(1,2)
|
|
else :
|
|
self.presetUp(2,1)
|
|
self.guiPresetsShow("copies")
|
|
|
|
elif self.arw["radiopreset4"].get_active() == 1 : # 4-up in lines
|
|
if presetOrientation_i == 1 :
|
|
self.presetUp(2,2,1)
|
|
else :
|
|
self.presetUp(2,2,1)
|
|
self.guiPresetsShow("")
|
|
|
|
elif self.arw["radiopreset5"].get_active() == 1 : # 4-up in columns
|
|
if presetOrientation_i == 1 :
|
|
self.presetUp(2,2,2)
|
|
else :
|
|
self.presetUp(2,2,2)
|
|
self.guiPresetsShow("")
|
|
|
|
elif self.arw["radiopreset6"].get_active() == 1 : # x copies
|
|
if presetOrientation_i == 1 :
|
|
self.presetCopies(1,2)
|
|
else :
|
|
self.presetCopies(2,1)
|
|
self.guiPresetsShow("copies")
|
|
|
|
elif self.arw["radiopreset7"].get_active() == 1 : # 1 page
|
|
if presetOrientation_i == 1 :
|
|
self.presetMerge()
|
|
else :
|
|
self.presetMerge()
|
|
self.guiPresetsShow("copies")
|
|
|
|
elif self.arw["radiopreset8"].get_active() == 1 : # User defined
|
|
return # This button launchs the function "user_defined" which will handle the request
|
|
|
|
|
|
preview_b = True
|
|
if radiobutton != 0 and startup_b == False :
|
|
self.preview(self.previewPage)
|
|
|
|
def user_defined(self, widget) :
|
|
# Called by the "user defined" option button in the main window
|
|
# Gets data from the dialog where user enters the user defined layout
|
|
# Process the text from the TextView and sets controls and variables
|
|
# then update the preview.
|
|
# @widget : if this parameter is False, the dialog is not shown (used by OpenProject)
|
|
|
|
global startup_b, project_b, preview_b
|
|
|
|
preview_b = False # prevent multiple preview commands due to signals emitted by controls
|
|
if widget == False :
|
|
response = 1
|
|
else :
|
|
dialog = self.arw["dialog2"]
|
|
response = dialog.run()
|
|
dialog.hide()
|
|
|
|
if response == 0 :
|
|
return
|
|
if response == 1 :
|
|
buf = self.arw["user_layout"].get_buffer()
|
|
start, end = buf.get_bounds()
|
|
layout_s = buf.get_text(start, end, False)
|
|
|
|
imposition2 = ini.parse_user_layout(layout_s)
|
|
|
|
self.userpages = imposition2[0]
|
|
(self.imposition, numrows, numcols, pages) = imposition2
|
|
|
|
# set step
|
|
if self.arw["step_defined"].get_active() == True :
|
|
step_s = self.arw["step_value"].get_text()
|
|
self.presetCopies(numrows,numcols,step_s)
|
|
self.guiPresetsShow("copies")
|
|
else :
|
|
self.presetUp(numrows,numcols)
|
|
self.guiPresetsShow("")
|
|
|
|
total_pages = numrows * numcols
|
|
if len(pages) != total_pages :
|
|
message =_("Expected page number was : %d. Only %d found. \nThere is an error in your layout, please correct") % (total_pages, len(pages))
|
|
alert(message)
|
|
|
|
|
|
|
|
# Update gui before updating preview, since preview takes its data from the gui
|
|
while Gtk.events_pending():
|
|
Gtk.main_iteration()
|
|
|
|
preview_b = True
|
|
if startup_b == False :
|
|
self.preview(self.previewPage)
|
|
|
|
def select_step_value(self, widget, event) :
|
|
# launched when user types something in "step_value" entry
|
|
# Selects the appropriate radio button
|
|
self.arw["step_defined"].set_active(True)
|
|
|
|
def guiPresetsShow(self, action_s) :
|
|
|
|
StepWidgets = [self.arw["label15"], self.arw["entry15"]]
|
|
LeafsWidgets = [self.arw["label13"], self.arw["entry13"]]
|
|
OrientationWidgets = [self.arw["label11"], self.arw["presetOrientation1"],
|
|
self.arw["label12"], self.arw["presetOrientation2"]]
|
|
|
|
|
|
for a in StepWidgets + LeafsWidgets + OrientationWidgets :
|
|
a.hide()
|
|
|
|
if action_s == "booklet" :
|
|
for a in LeafsWidgets + OrientationWidgets :
|
|
a.show()
|
|
|
|
if action_s == "copies" :
|
|
for a in StepWidgets + OrientationWidgets :
|
|
a.show()
|
|
|
|
|
|
def presetBooklet(self, leafs_i, orientation) :
|
|
if orientation == 0 :
|
|
self.arw["entry11"].set_value(1) # rows
|
|
self.arw["entry12"].set_value(2) # columns
|
|
else :
|
|
self.arw["entry11"].set_value(2) # rows
|
|
self.arw["entry12"].set_value(1) # columns
|
|
self.arw["entry13"].set_text(str(leafs_i)) # leafs in booklet
|
|
ini.repeat = 0
|
|
ini.booklet = 1
|
|
|
|
def presetUp(self, r,c,l=1) :
|
|
self.arw["entry11"].set_value(r) # rows
|
|
self.arw["entry12"].set_value(c) # columns
|
|
ini.booklet = 0 # checkerboard
|
|
ini.radioDisp = l # lines / columns
|
|
ini.repeat = 0
|
|
|
|
def presetCopies(self, r,c, step="1") :
|
|
global step_i
|
|
self.arw["entry11"].set_value(r) # rows
|
|
self.arw["entry12"].set_value(c) # columns
|
|
ini.booklet = 0 # checkerboard
|
|
ini.repeat = 1
|
|
self.arw["entry15"].set_text(step) # step
|
|
|
|
def presetCopies2(self, r,c,l=1) :
|
|
self.arw["entry11"].set_value(r) # rows
|
|
self.arw["entry12"].set_value(c) # columns
|
|
ini.booklet = 0 # checkerboard
|
|
ini.radioDisp = l # lines / columns
|
|
ini.repeat = 0
|
|
|
|
|
|
def presetMerge(self) : # One page
|
|
global outputFile, inputFile_a
|
|
|
|
self.arw["entry11"].set_value(1) # rows
|
|
self.arw["entry12"].set_value(1) # columns
|
|
ini.booklet = 0 # checkerboard
|
|
ini.repeat = 0
|
|
|
|
def _________________________PREVIEW() :
|
|
pass
|
|
|
|
def OnDraw(self, area, cr):
|
|
global page
|
|
global areaAllocationW_i, areaAllocationH_i
|
|
global previewColPos_a, previewRowPos_a, pageContent_a
|
|
global refPageSize_a, numPages
|
|
global outputStream, outputStream_mem
|
|
|
|
# Was the size changed ?
|
|
|
|
if (areaAllocationW_i == self.area.get_allocated_width()
|
|
and areaAllocationH_i == self.area.get_allocated_height()) :
|
|
nochange_b = True
|
|
else :
|
|
nochange_b = False
|
|
areaAllocationW_i = self.area.get_allocated_width()
|
|
areaAllocationH_i = self.area.get_allocated_height()
|
|
|
|
|
|
# If nothing has not yet been loaded, show nofile.pdf
|
|
try :
|
|
data1 = outputStream.getvalue()
|
|
except :
|
|
f1 = open(sfp2("data/nofile.pdf"),"rb")
|
|
data1 = f1.read()
|
|
f1.close()
|
|
|
|
|
|
# TODO : When two calls are too close, it may create a strange stack resulting in an empty OutputStream
|
|
# This has to be redesigned.
|
|
if len(data1) == 0 :
|
|
#print("......... OutputStream is empty")
|
|
return
|
|
|
|
# If the preview has not changed, don't render
|
|
# TODO : This must be redesigned, because with the image in memory, it may be necessary to render again with the same data
|
|
## if data1 == outputStream_mem :
|
|
## return
|
|
## else :
|
|
## outputStream_mem = data1
|
|
|
|
try :
|
|
bytes_data = GLib.Bytes(data1)
|
|
input_stream = Gio.MemoryInputStream.new_from_bytes(bytes_data)
|
|
# Take care that you need to call .close() on the Gio.MemoryInputStream once you're done with your pdf document.
|
|
document = Poppler.Document.new_from_stream(input_stream, -1, None, None)
|
|
self.document= document
|
|
|
|
except :
|
|
# bug workaround
|
|
"""
|
|
Hi there, I installed the deb on Ubuntu 19.04. It installed fine but gives this message:
|
|
|
|
Traceback (most recent call last):
|
|
File "/usr/local/lib/python3.7/dist-packages/pdfbooklet/pdfbooklet.py", line 2342, in OnDraw
|
|
document = Poppler.Document.new_from_stream(input_stream, -1, None, None)
|
|
gi.repository.GLib.GError: poppler-quark: Failed to load document (0)
|
|
|
|
The preview panel remains blank but when I press the Go button the .bklt file is produced correctly. I get the same if I download the tar and install manually. Also the same with many different input pdf files.
|
|
"""
|
|
try:
|
|
filepath = os.path.join(tempfile.gettempdir(), "preview.pdf")
|
|
with open(filepath, "wb") as f1:
|
|
f1.write(data1)
|
|
document = Poppler.Document.new_from_file("file:///" + filepath, None)
|
|
self.document= document
|
|
except:
|
|
print("Error rendering document in poppler")
|
|
printExcept()
|
|
return False
|
|
|
|
page = document.get_page(0)
|
|
self.page = page
|
|
|
|
x = document.get_n_pages()
|
|
if x > 1 :
|
|
self.page1 = document.get_page(1)
|
|
else :
|
|
self.page1 = None
|
|
|
|
# calculate the preview size
|
|
|
|
pix_w, pix_h = page.get_size()
|
|
|
|
A = int((areaAllocationH_i * pix_w) / pix_h) # width of preview if full height
|
|
B = int((areaAllocationW_i * pix_h) / pix_w) # height of preview if full width
|
|
|
|
|
|
if A < areaAllocationW_i : # if full height is OK
|
|
heightPoints = areaAllocationH_i
|
|
widthPoints = A
|
|
scale = areaAllocationH_i / pix_h
|
|
else :
|
|
widthPoints = areaAllocationW_i
|
|
heightPoints = B
|
|
scale = areaAllocationW_i / pix_w
|
|
|
|
self.preview_scale = scale # this will be needed for drag (see the end_drag function)
|
|
Hoffset_i = int((areaAllocationW_i - widthPoints) /2)
|
|
Voffset_i = int((areaAllocationH_i - heightPoints)/2)
|
|
|
|
# set background.
|
|
cr.set_source_rgb(0.7, 0.6, 0.5)
|
|
cr.paint()
|
|
|
|
# set page background
|
|
cr.set_source_rgb(1, 1, 1)
|
|
cr.rectangle(Hoffset_i, Voffset_i, widthPoints, heightPoints)
|
|
|
|
cr.fill()
|
|
|
|
# render page
|
|
|
|
cr.save()
|
|
cr.translate(Hoffset_i, Voffset_i)
|
|
cr.scale(scale,scale)
|
|
self.page.render(cr)
|
|
cr.restore()
|
|
|
|
# If there are two pages in the preview, the second will be printed over the other
|
|
# to help fine tuning the margins for duplex printing
|
|
|
|
if self.page1 :
|
|
cr.save()
|
|
cr.translate(Hoffset_i, Voffset_i)
|
|
cr.scale(scale,scale)
|
|
self.page1.render(cr)
|
|
cr.restore()
|
|
|
|
# clear memory
|
|
input_stream.close()
|
|
|
|
|
|
# draw middle line
|
|
|
|
if ((ini.booklet > 0 and len(inputFiles_a) > 0)
|
|
or self.arw["draw_middle_line"].get_active() == True) :
|
|
cr.save()
|
|
cr.set_line_width(1)
|
|
cr.set_dash((10,8))
|
|
cr.set_source_rgb(0.5, 0.5, 1)
|
|
line_position = (areaAllocationW_i/2) + 0.5 # The reason for + 0.5 is explained here : https://www.cairographics.org/FAQ/#sharp_lines
|
|
# under the title : How do I draw a sharp, single-pixel-wide line?
|
|
cr.move_to(line_position, Voffset_i)
|
|
cr.line_to(line_position, (areaAllocationH_i - Voffset_i ))
|
|
cr.stroke()
|
|
cr.restore()
|
|
|
|
|
|
# if the output is turned, swap the numbers of rows and columns
|
|
if app.arw["globalRotation90"].get_active() == 1 \
|
|
or app.arw["globalRotation270"].get_active() == 1:
|
|
preview_cols = rows_i
|
|
preview_rows = columns_i
|
|
else :
|
|
preview_cols = columns_i
|
|
preview_rows = rows_i
|
|
|
|
|
|
columnWidth = widthPoints / preview_cols
|
|
rowHeight = heightPoints / preview_rows
|
|
|
|
#store position of columns and rows
|
|
# previewColPos_a will contain the position of the left of all columns (from left to right)
|
|
# previewRowPos_a will contain the position of the top of all rows (from bottom to top)
|
|
previewColPos_a = []
|
|
previewRowPos_a = []
|
|
previewPagePos_a = {}
|
|
|
|
|
|
for a1 in range(preview_cols) :
|
|
#left of the column
|
|
previewColPos_a += [int((a1 * columnWidth) + Hoffset_i)]
|
|
for a2 in range(preview_rows) :
|
|
#top of the row
|
|
# rows count starts from bottom => areaAllocationH_i - ...
|
|
previewRowPos_a += [areaAllocationH_i - (int((a2 * rowHeight) + Voffset_i ))]
|
|
# add the right pos of the last col
|
|
previewColPos_a += [int(previewColPos_a[a1] + columnWidth)]
|
|
previewRowPos_a += [int(previewRowPos_a[a2] - rowHeight)]
|
|
|
|
|
|
# show page numbers
|
|
i = 0
|
|
pageContent_a = {}
|
|
|
|
|
|
for a in self.rotate_layout() : # returns the layout, rotated if necessary
|
|
|
|
# human readable page number
|
|
pageRef_s = self.ar_pages[0][i]
|
|
file_number, page_number = pageRef_s.split(":")
|
|
page_number = int(page_number) + 1
|
|
if file_number == "1" :
|
|
pageNumber = str(page_number)
|
|
else :
|
|
pageNumber = str(file_number) + ":" + str(page_number)
|
|
|
|
|
|
fontsize_i = int(columnWidth / 4)
|
|
if fontsize_i < 10 :
|
|
fontsize_i = 10
|
|
colpos_i = previewColPos_a[a[1]] + (columnWidth / 2)
|
|
rowpos_i = previewRowPos_a[a[0]] - (rowHeight / 2)
|
|
x1,x2 = colpos_i,rowpos_i
|
|
|
|
# draw page number
|
|
if self.arw["hide_numbers"].get_active() == 0 :
|
|
# TODO cr.select_font_face("Georgia",
|
|
## cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
|
|
## AttributeError: 'gi.repository.cairo' object has no attribute 'FONT_SLANT_NORMAL'
|
|
## Remplacé par 0 et 1 ci-dessous
|
|
cr.select_font_face("Georgia", 0, 1)
|
|
cr.set_font_size(fontsize_i)
|
|
x_bearing, y_bearing, txtWidth, txtHeight = cr.text_extents(pageNumber)[:4]
|
|
|
|
colpos_i -= int(txtWidth / 4)
|
|
rowpos_i -= int(txtHeight / 4)
|
|
|
|
|
|
cr.move_to(colpos_i, rowpos_i - y_bearing)
|
|
cr.set_source_rgba(1, 0, 0, 0.6)
|
|
cr.show_text(pageNumber)
|
|
|
|
pageId = str(a[0]) + ":" + str(a[1])
|
|
pageContent_a[pageId] = [file_number, page_number]
|
|
|
|
# page position
|
|
bottom = previewRowPos_a[a[0]]
|
|
top = previewRowPos_a[a[0]] - rowHeight
|
|
left = previewColPos_a[a[1]]
|
|
right = previewColPos_a[a[1]] + columnWidth
|
|
|
|
|
|
|
|
# draw rectangle if selected
|
|
humanReadableRow_i = (preview_rows - a[0])
|
|
pagePosition_a = [humanReadableRow_i, a[1] + 1]
|
|
pagePosition_s = self.rotate_position(pagePosition_a)
|
|
pageNumber_s = str(file_number) + ":" + str(page_number)
|
|
draw_page_b = False
|
|
type_s = ""
|
|
|
|
# is this page odd or even ?
|
|
if self.arw["evenpages"].get_active() == 1 :
|
|
if int(pageNumber) % 2 == 0 :
|
|
cr.set_source_rgb(0, 0, 1)
|
|
draw_page_b = True
|
|
type_s = "even"
|
|
|
|
elif self.arw["oddpages"].get_active() == 1 :
|
|
if int(pageNumber) % 2 == 1 :
|
|
cr.set_source_rgb(0, 0, 1)
|
|
draw_page_b = True
|
|
type_s = "odd"
|
|
|
|
# if page is selected
|
|
elif pagePosition_s in selectedIndex_a :
|
|
# Select the rectangle color
|
|
cr.set_source_rgb(1, 0, 0)
|
|
draw_page_b = True
|
|
type_s = "select"
|
|
|
|
|
|
elif pageNumber_s in selectedIndex_a :
|
|
# Select the rectangle color
|
|
cr.set_source_rgb(0, 0.7, 0)
|
|
draw_page_b = True
|
|
type_s = "select"
|
|
|
|
|
|
|
|
|
|
# draw rectangle
|
|
if draw_page_b == True :
|
|
|
|
file_number, page_number = self.myselectedpage.split(":")
|
|
selected_ref = file_number + ":" + str(int(page_number) - 1)
|
|
|
|
|
|
coord = [left, top]
|
|
cr.set_line_width(3)
|
|
cr.rectangle(coord[0], coord[1], columnWidth, rowHeight)
|
|
cr.stroke()
|
|
|
|
|
|
i += 1
|
|
|
|
|
|
# Show page size and total pages number
|
|
## cr.select_font_face("Arial", 0, 1)
|
|
# TODO Anciennement cr.select_font_face("Arial", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
|
|
# mais les attributs ne marche plus.
|
|
## cr.set_font_size(12)
|
|
try :
|
|
message = str(round(refPageSize_a[0] * adobe_l))
|
|
message += " x " + str(round(refPageSize_a[1] * adobe_l))
|
|
message += " - " + str(numPages) + " pages"
|
|
## x_bearing, y_bearing, txtWidth, txtHeight = cr.text_extents(message)[:4]
|
|
## cr.move_to(20,20)
|
|
## cr.set_source_rgb(0, 0.6, 0)
|
|
## cr.show_text(message)
|
|
self.arw["info_fichier"].set_text(message)
|
|
except :
|
|
pass # if refPageSize is not defined, error
|
|
|
|
|
|
|
|
def rotate_layout(self) :
|
|
rotated_layout= []
|
|
for i in range (len(self.ar_layout)) :
|
|
r, c = self.ar_layout[i]
|
|
|
|
# Flip columns or rows if vertical or horizontal flip is selected
|
|
if app.arw["vflip2"].get_active() == 1 :
|
|
r = (rows_i - 1) - r
|
|
if app.arw["hflip2"].get_active() == 1 :
|
|
c = (columns_i - 1) - c
|
|
|
|
# if output page is rotated (global rotation)
|
|
if app.arw["globalRotation270"].get_active() == 1 :
|
|
# invert row; We use columns_i because when rotated 90°,
|
|
# the numbers of rows of the preview is the number of columns of the page
|
|
r = (rows_i - 1) - r
|
|
r,c = c,r # swap
|
|
|
|
elif app.arw["globalRotation90"].get_active() == 1 :
|
|
# invert column; ; We use rows_i because when rotated 90°,
|
|
# the numbers of columns of the preview is the number of rows of the page
|
|
c = (columns_i - 1) - c
|
|
r,c = c,r # swap
|
|
|
|
elif app.arw["globalRotation180"].get_active() == 1 :
|
|
# invert row and column
|
|
r = (rows_i - 1) - r
|
|
c = (columns_i - 1) - c
|
|
|
|
|
|
rotated_layout.append([r,c])
|
|
return rotated_layout
|
|
|
|
|
|
|
|
def rotate_position(self, position) :
|
|
|
|
## print( "source : ", position)
|
|
r, c = position
|
|
# if output page is rotated (global rotation)
|
|
if app.arw["globalRotation270"].get_active() == 1 :
|
|
# invert row; We use columns_i because when rotated 90°,
|
|
# the numbers of rows of the preview is the number of columns of the page
|
|
r = (columns_i + 1) - r
|
|
r,c = c,r # swap
|
|
|
|
elif app.arw["globalRotation90"].get_active() == 1 :
|
|
# invert column; ; We use rows_i because when rotated 90°,
|
|
# the numbers of columns of the preview is the number of rows of the page
|
|
c = (rows_i + 1) - c
|
|
r,c = c,r # swap
|
|
|
|
elif app.arw["globalRotation180"].get_active() == 1 :
|
|
# invert row and column
|
|
r = (rows_i + 1) - r
|
|
c = (columns_i +1) - c
|
|
|
|
## print ("dest : ", str(r) + "," + str(c))
|
|
|
|
return str(r) + "," + str(c)
|
|
|
|
|
|
|
|
def selectPage (self, widget, event=None):
|
|
# Called by a click on the preview or on the radio buttons, this function will :
|
|
# - Select the appropriate Id
|
|
# - launch area_expose to update the display
|
|
# - fill in the gtkEntry widgets which contains the transformations
|
|
#
|
|
# The selected_page list contains a list of six values :
|
|
# - row and column (Pdf format)
|
|
# - file number and page number
|
|
# - row and column if the output page is rotated
|
|
|
|
global previewColPos_a, previewRowPos_a, canvasId20, pageContent_a
|
|
global selectedIndex_a, selected_page, selected_pages_a, pageId
|
|
|
|
|
|
if event == None : # Click on a radio button
|
|
if self.thispage.get_active() == 1 :
|
|
pageId = str(selected_page[2]) + ":" + str(selected_page[3])
|
|
Id = pageId
|
|
elif self.evenpages.get_active() == 1 :
|
|
Id = "even"
|
|
elif self.oddpages.get_active() == 1 :
|
|
Id = "odd"
|
|
else : # first button, pages in this position
|
|
humanReadableRow_i = rows_i - selected_page[4]
|
|
Id = str(str(humanReadableRow_i) + "," + str(selected_page[5] + 1))
|
|
Id = str(str(selected_page[5] + 1) + "," + str(humanReadableRow_i))
|
|
|
|
elif event.type == Gdk.EventType.BUTTON_PRESS:
|
|
if event.button == 3 : # right click, runs the context menu
|
|
self.arw["contextmenu1"].popup(None, None, None, None, event.button, event.time)
|
|
return
|
|
|
|
else :
|
|
# get preview area
|
|
left_limit = previewColPos_a[0]
|
|
bottom_limit = previewRowPos_a[-1]
|
|
right_limit = previewColPos_a[-1]
|
|
top_limit = previewRowPos_a[0]
|
|
|
|
xpos = event.x
|
|
ypos = event.y
|
|
|
|
self.initdrag = [xpos, ypos] # Will be used for moving page with the mouse
|
|
self.preview_limits = [left_limit, right_limit, top_limit, bottom_limit]
|
|
|
|
# check if click is inside preview
|
|
if (xpos < left_limit
|
|
or xpos > right_limit
|
|
or ypos < bottom_limit
|
|
or ypos > top_limit) :
|
|
|
|
return
|
|
|
|
# find the row and column
|
|
for c in range(len(previewColPos_a)) :
|
|
if xpos > previewColPos_a[c] and xpos < previewColPos_a[c + 1]:
|
|
leftPos_i = previewColPos_a[c]
|
|
rightPos_i = previewColPos_a[c + 1]
|
|
break
|
|
|
|
|
|
for r in range(len(previewRowPos_a)) :
|
|
if ypos < previewRowPos_a[r] and ypos > previewRowPos_a[r + 1]:
|
|
bottomPos_i = previewRowPos_a[r]
|
|
topPos_i = previewRowPos_a[r + 1]
|
|
break
|
|
|
|
r1 = r
|
|
c1 = c
|
|
|
|
# it should be possible to use rotate_layout
|
|
|
|
# Flip columns or rows if vertical or horizontal flip is selected
|
|
## if app.arw["vflip2"].get_active() == 1 :
|
|
## r1 = (rows_i - 1) - r
|
|
## if app.arw["hflip2"].get_active() == 1 :
|
|
## c1 = (columns_i - 1) - c
|
|
|
|
# if output page is rotated (global rotation)
|
|
if app.arw["globalRotation90"].get_active() == 1 :
|
|
# invert row; We use columns_i because when rotated 90°,
|
|
# the numbers of rows of the preview is the number of columns of the page
|
|
r1 = (columns_i - 1) - r1
|
|
r1,c1 = c1,r1 # swap
|
|
|
|
elif app.arw["globalRotation270"].get_active() == 1 :
|
|
# invert column; ; We use rows_i because when rotated 90°,
|
|
# the numbers of columns of the preview is the number of rows of the page
|
|
c1 = (rows_i - 1) - c1
|
|
r1,c1 = c1,r1 # swap
|
|
|
|
elif app.arw["globalRotation180"].get_active() == 1 :
|
|
# invert row and column
|
|
r1 = (rows_i - 1) - r1
|
|
c1 = (columns_i - 1) - c1
|
|
|
|
selected_page = [r, c]
|
|
selected_page += pageContent_a[str(r) + ":" + str(c)]
|
|
selected_page += [r1, c1]
|
|
|
|
|
|
# Selection information
|
|
self.myselectedpage = str(selected_page[2]) + ":" + str(selected_page[3])
|
|
if self.thispage.get_active() == 1 : # This page
|
|
Id = self.myselectedpage
|
|
elif self.evenpages.get_active() == 1 : # even pages
|
|
Id = "even"
|
|
elif self.oddpages.get_active() == 1 : # odd pages
|
|
Id = "odd"
|
|
else : # first button, pages in this position
|
|
humanReadableRow_i = rows_i - selected_page[4]
|
|
Id = str(str(humanReadableRow_i) + "," + str(selected_page[5] + 1))
|
|
|
|
|
|
|
|
if event.state != Gdk.ModifierType.CONTROL_MASK : # If Control not pressed, delete previous selection
|
|
selectedIndex_a = {}
|
|
selected_pages_a = []
|
|
|
|
# position reference
|
|
selectedIndex_a[Id] = 1
|
|
selected_pages_a.append(selected_page)
|
|
|
|
|
|
# force the "draw" signal to update the display
|
|
self.arw["drawingarea1"].hide()
|
|
self.arw["drawingarea1"].show()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else : # unsupported event
|
|
return
|
|
|
|
# Load settings in transformation dialogs
|
|
if event.state != Gdk.ModifierType.CONTROL_MASK : # but only if CONTROL is not pressed
|
|
self.load_settings_in_dialog(Id)
|
|
|
|
|
|
def load_settings_in_dialog(self, Id) :
|
|
|
|
|
|
# defaults
|
|
self.arw["htranslate1"].set_text("0")
|
|
self.Vtranslate1.set_text("0")
|
|
self.scale1.set_text("100")
|
|
self.rotation1.set_text("0")
|
|
self.arw["xscale1"].set_text("100")
|
|
self.arw["yscale1"].set_text("100")
|
|
self.arw["vflip1"].set_active(False)
|
|
self.arw["hflip1"].set_active(False)
|
|
|
|
|
|
if Id in config :
|
|
if "htranslate" in config[Id] :
|
|
self.arw["htranslate1"].set_text(str(config[Id]["htranslate"]))
|
|
if "vtranslate" in config[Id] :
|
|
self.Vtranslate1.set_text(str(config[Id]["vtranslate"]))
|
|
if "scale" in config[Id] :
|
|
self.scale1.set_text(str(config[Id]["scale"]))
|
|
if "rotate" in config[Id] :
|
|
self.rotation1.set_text(str(config[Id]["rotate"]))
|
|
if "xscale" in config[Id] :
|
|
self.arw["xscale1"].set_text(str(config[Id]["xscale"]))
|
|
if "yscale" in config[Id] :
|
|
self.arw["yscale1"].set_text(str(config[Id]["yscale"]))
|
|
|
|
if "vflip" in config[Id] :
|
|
bool_b = config[Id]["vflip"]
|
|
if bool_b == "True" or bool_b == True or bool_b == "1" : # when parameter comes from the ini file, it is a string
|
|
bool_b = 1
|
|
elif bool_b == "False" or bool_b == False or bool_b == "0" :
|
|
bool_b = 0
|
|
self.arw["vflip1"].set_active(bool_b)
|
|
if "hflip" in config[Id] :
|
|
bool_b = config[Id]["hflip"]
|
|
if bool_b == "True" or bool_b == True or bool_b == "1" : # when parameter comes from the ini file, it is a string
|
|
bool_b = 1
|
|
elif bool_b == "False" or bool_b == False or bool_b == "0" :
|
|
bool_b = 0
|
|
|
|
self.arw["hflip1"].set_active(bool_b)
|
|
|
|
|
|
def drag_motion(self, widget, cr, x, y, time) :
|
|
# unused
|
|
# To activate, connect the signal "drag-motion" of then eventbox parent of drawingarea1
|
|
# to this function
|
|
print (x)
|
|
cr.set_line_width(1)
|
|
cr.rectangle(x, y, 2, 2)
|
|
cr.stroke()
|
|
return False
|
|
|
|
|
|
def set_even_odd_settings(self, widget) : # launched by a clic on the domain buttons
|
|
global selected_page, selectedIndex_a
|
|
#print ("set even odd")
|
|
if self.evenpages.get_active() == 1 :
|
|
Id = "even"
|
|
selected_page = Id
|
|
self.load_settings_in_dialog(Id)
|
|
elif self.oddpages.get_active() == 1 :
|
|
Id = "odd"
|
|
selected_page = Id
|
|
self.load_settings_in_dialog(Id)
|
|
else :
|
|
#print ("reset selection")
|
|
selected_page = None
|
|
selectedIndex_a = {}
|
|
self.previewUpdate()
|
|
|
|
|
|
|
|
def end_drag(self, widget, event) :
|
|
global outputScale
|
|
|
|
# Thisfunction will move the page when the mouse button is released
|
|
x = event.x
|
|
y = event.y
|
|
|
|
# scaling factor
|
|
if self.arw["autoscale"].get_active() == 1 :
|
|
scaling_factor = self.preview_scale * outputScale
|
|
else :
|
|
scaling_factor = self.preview_scale
|
|
|
|
|
|
scaling_factor2 = scaling_factor / adobe_l
|
|
|
|
hmove = ((x - self.initdrag[0]) / scaling_factor2)
|
|
vmove = ((y - self.initdrag[1]) / scaling_factor2)
|
|
|
|
if self.arw["move_with_mouse"].get_active() == 1 :
|
|
|
|
# Calculate the scaling factor
|
|
|
|
temp = self.arw["htranslate1"].get_text()
|
|
temp = temp.replace(",", ".")
|
|
temp = float(temp)
|
|
temp += hmove
|
|
temp = str(temp).split(".")
|
|
temp = temp[0] + "." + temp[1][0:1]
|
|
self.arw["htranslate1"].set_text(temp)
|
|
self.transformationsApply("")
|
|
|
|
temp = self.arw["vtranslate1"].get_text()
|
|
temp = temp.replace(",", ".")
|
|
temp = float(temp)
|
|
temp -= vmove
|
|
temp = str(temp).split(".")
|
|
temp = temp[0] + "." + temp[1][0:1]
|
|
self.arw["vtranslate1"].set_text(temp)
|
|
self.transformationsApply("")
|
|
|
|
elif self.arw["delete_rectangle"].get_active() == 1 :
|
|
# preview_limits gives : left x, right x, bottom y, top y, in pixels.
|
|
#
|
|
# init_drag gives : x, y
|
|
# x = horizontal position
|
|
left = self.preview_limits[0] # left margin
|
|
top = self.preview_limits[3] # top margin
|
|
|
|
|
|
x1 = (self.initdrag[0] - left) # start drag
|
|
y1 = self.initdrag[1]
|
|
y1 = self.preview_limits[2] - y1 # invert the vertical position, because Pdf counts from bottom
|
|
# This corrects also the top margin because bottom y = page height + margin
|
|
x2 = x - left # end drag
|
|
y2 = y
|
|
y2 = self.preview_limits[2] - y2
|
|
width = x2 - x1
|
|
height = y2 - y1
|
|
x1 = x1 / scaling_factor
|
|
y1 = y1 / scaling_factor
|
|
width = width / scaling_factor
|
|
height = height / scaling_factor
|
|
ini.delete_rectangle = [x1,y1,width,height]
|
|
|
|
elif self.arw["divide"].get_active() == 1 :
|
|
# preview_limits gives : left x, right x, bottom y, top y, in pixels.
|
|
#
|
|
# init_drag gives : x, y
|
|
# x = horizontal position
|
|
left = self.preview_limits[0] # left margin
|
|
top = self.preview_limits[3] # top margin
|
|
|
|
|
|
x1 = (self.initdrag[0] - left) # start drag
|
|
y1 = self.initdrag[1]
|
|
y1 = self.preview_limits[2] - y1 # invert the vertical position, because Pdf counts from bottom
|
|
# This corrects also the top margin because bottom y = page height + margin
|
|
x2 = x - left # end drag
|
|
y2 = y
|
|
y2 = self.preview_limits[2] - y2
|
|
width = x2 - x1
|
|
height = y2 - y1
|
|
x1 = x1 / scaling_factor
|
|
y1 = y1 / scaling_factor
|
|
width = width / scaling_factor
|
|
height = height / scaling_factor
|
|
ini.delete_rectangle = [x1,y1,width,height]
|
|
|
|
|
|
|
|
def createSelection(self) :
|
|
global inputFiles_a
|
|
i = 1
|
|
x = []
|
|
for f in inputFiles_a :
|
|
fileName = inputFiles_a[f]
|
|
numPages = inputFile_a[fileName].getNumPages()
|
|
for z in range(numPages) :
|
|
pageRef_s = str(i) + ":" + str(z)
|
|
if pageRef_s + "deleted" in deletedIndex_a :
|
|
if deletedIndex_a[pageRef_s + "deleted"] == 1 :
|
|
pass
|
|
else :
|
|
x += [pageRef_s]
|
|
else :
|
|
x += [pageRef_s]
|
|
i += 1
|
|
self.selection_s = self.compressSelection(x)
|
|
|
|
def compressSelection(self, x) :
|
|
i = 0
|
|
temp = {}
|
|
out = ""
|
|
for a in x :
|
|
b = a.split(":")
|
|
if len(b) == 1 :
|
|
npage = b[0]
|
|
nfile = 1
|
|
else :
|
|
npage = b[1]
|
|
nfile = b[0]
|
|
|
|
if i == 0 :
|
|
temp["file"] = nfile
|
|
temp["first"] = npage
|
|
temp["last"] = npage
|
|
else :
|
|
if nfile == temp["file"] and int(npage) == int(temp["last"]) + 1 :
|
|
temp["last"] = npage # on continue
|
|
else : # sinon on écrit
|
|
if temp["first"] == "-1":
|
|
out += "b"
|
|
else :
|
|
out += str(temp["file"]) + ":" + str(temp["first"])
|
|
if temp["last"] != temp["first"] :
|
|
out += "-" + str(temp["last"])
|
|
out += "; "
|
|
# et on mémorise
|
|
temp["file"] = nfile
|
|
temp["first"] = npage
|
|
temp["last"] = npage
|
|
i += 1
|
|
|
|
if temp["first"] == "-1":
|
|
out += "b"
|
|
else :
|
|
out += str(temp["file"]) + ":" + str(temp["first"])
|
|
if temp["last"] != temp["first"] :
|
|
out += "-" + str(temp["last"])
|
|
|
|
# compress blank pages
|
|
temp1 = out.split(";")
|
|
out = ""
|
|
blank_count = 0
|
|
for a in temp1 :
|
|
if a.strip() == "b" :
|
|
blank_count += 1
|
|
else :
|
|
if blank_count > 0 :
|
|
out += str(blank_count) + "b;"
|
|
blank_count = 0
|
|
out += a + ";"
|
|
if blank_count > 0 :
|
|
out += str(blank_count) + "b"
|
|
|
|
return out
|
|
|
|
def edit_selection(self, widget) :
|
|
dialog = self.arw["dialog1"]
|
|
TextBuffer = self.arw["textview1"].get_buffer()
|
|
selection1 = self.selection_s.replace(";","\n")
|
|
TextBuffer.set_text(selection1)
|
|
choice = dialog.run()
|
|
if choice == 1 :
|
|
start_iter = TextBuffer.get_start_iter()
|
|
end_iter = TextBuffer.get_end_iter()
|
|
selection2 = TextBuffer.get_text(start_iter, end_iter, False)
|
|
selection2 = selection2.replace("\n",";")
|
|
self.selection_s = selection2
|
|
if self.shuffler:
|
|
self.shuffler.model.clear()
|
|
self.shuffler.pdfqueue = []
|
|
self.shuffler.nfile = 0
|
|
self.loadShuffler()
|
|
# TODO : N'est à faire que si la liste des fichiers a changé
|
|
self.shuffler.rendering_thread.pdfqueue = self.shuffler.pdfqueue
|
|
dialog.hide()
|
|
self.previewUpdate()
|
|
|
|
|
|
def compare_files_selection(self,widget) :
|
|
# Create a selection to display two or more files side by side
|
|
|
|
# Number of files and pages
|
|
files_data = {}
|
|
index = 1
|
|
maxPages = 0
|
|
numFiles = len(inputFiles_a)
|
|
for f in inputFiles_a :
|
|
fileName = inputFiles_a[f]
|
|
numPages = inputFile_a[fileName].getNumPages()
|
|
if numPages > maxPages :
|
|
maxPages = numPages
|
|
files_data[index] = numPages
|
|
index += 1
|
|
|
|
# create the selection
|
|
index = 1
|
|
text = ""
|
|
for i in range(maxPages) :
|
|
temp = []
|
|
for j in range(1, numFiles + 1) :
|
|
if files_data[j] > i : # if the file is shorter, insert blank pages
|
|
temp.append(str(j) + ":" + str(i))
|
|
else :
|
|
temp.append("b")
|
|
text += ",".join(temp) + "\n"
|
|
|
|
TextBuffer = self.arw["textview1"].get_buffer()
|
|
TextBuffer.set_text(text)
|
|
|
|
|
|
def preview(self, previewPage, delete = 1) :
|
|
|
|
|
|
global mediabox_l0, columns_i, rows_i, step_i, urx_i, ury_i, mediabox_l
|
|
global outputScale, pageContent_a
|
|
global selectedIndex_a, deletedIndex_a
|
|
global areaAllocationW_i, areaAllocationH_i
|
|
global preview_b
|
|
|
|
|
|
if preview_b == False :
|
|
return
|
|
|
|
|
|
|
|
if self.readGui(0) :
|
|
self.manage_backup()
|
|
if self.render.parsePageSelection() :
|
|
#self.readConditions()
|
|
ar_pages, ar_layout, ar_cahiers = self.render.createPageLayout(0)
|
|
if ar_pages != None :
|
|
if previewPage > len(ar_pages) - 1 :
|
|
previewPage = len(ar_pages) - 1
|
|
self.previewPage = previewPage
|
|
self.arw["previewEntry"].set_text(str(previewPage + 1))
|
|
mem = ar_pages[previewPage]
|
|
try :
|
|
mem1 = ar_pages[previewPage + 1]
|
|
except : # If we are arrived at the last page
|
|
pass
|
|
ar_pages = {}
|
|
ar_pages[0] = mem
|
|
|
|
# If previewing of two pages one over the other is requested
|
|
if 1 == 2 :
|
|
ar_pages[1] = mem1
|
|
|
|
self.ar_pages = ar_pages
|
|
self.ar_layout = ar_layout
|
|
|
|
if self.render.createNewPdf(ar_pages, ar_layout, ar_cahiers, "", previewPage) :
|
|
|
|
# force the "draw" signal to update the display
|
|
self.arw["drawingarea1"].hide()
|
|
self.arw["drawingarea1"].show()
|
|
|
|
|
|
|
|
def preview_keys(self, widget, event = None) :
|
|
print ( "==========>>", event)
|
|
|
|
def previewNext(self, dummy, event=None) :
|
|
global selected_page, selectedIndex_a
|
|
if event.state != Gdk.ModifierType.CONTROL_MASK :
|
|
selected_page = None
|
|
selectedIndex_a = {}
|
|
self.previewPage += 1
|
|
self.arw["previewEntry"].set_text(str(self.previewPage + 1))
|
|
if self.arw["automaticUpdate"].get_active() == 0 : # If automatic update is not disabled
|
|
self.preview(self.previewPage) # update the preview
|
|
|
|
def previewPrevious(self, dummy, event = None) :
|
|
global selected_page, selectedIndex_a
|
|
if event.state != Gdk.ModifierType.CONTROL_MASK :
|
|
selected_page = None
|
|
selectedIndex_a = {}
|
|
self.previewPage -= 1
|
|
if self.previewPage < 0 :
|
|
self.previewPage = 0
|
|
self.arw["previewEntry"].set_text(str(self.previewPage + 1))
|
|
if self.arw["automaticUpdate"].get_active() == 0 : # If automatic update is not disabled
|
|
self.preview(self.previewPage) # update the preview
|
|
|
|
def previewFirst(self, widget) :
|
|
global selected_page, selectedIndex_a
|
|
selected_page = None
|
|
selectedIndex_a = {}
|
|
self.previewPage = 0
|
|
self.arw["previewEntry"].set_text(str(self.previewPage + 1))
|
|
if self.arw["automaticUpdate"].get_active() == 0 : # If automatic update is not disabled
|
|
self.preview(self.previewPage) # update the preview
|
|
|
|
def previewLast(self, widget) :
|
|
global selected_page, selectedIndex_a
|
|
selected_page = None
|
|
selectedIndex_a = {}
|
|
self.previewPage = 1000000 # CreatePageLayout will substitute the right number
|
|
self.arw["previewEntry"].set_text(str(self.previewPage + 1))
|
|
if self.arw["automaticUpdate"].get_active() == 0 : # If automatic update is not disabled
|
|
self.preview(self.previewPage) # update the preview
|
|
|
|
def previewUpdate(self, Event = None, mydata = None) :
|
|
global inputFiles_a
|
|
|
|
if len(inputFiles_a) == 0 :
|
|
#showwarning(_("No file loaded"), _("Please select a file first"))
|
|
return False
|
|
if Event != None :
|
|
value_s = self.arw["entry11"].get_text()
|
|
value2_s = self.arw["entry12"].get_text()
|
|
if value_s != "" and value2_s != "" :
|
|
previewPage = self.arw["previewEntry"].get_text()
|
|
if previewPage != "" :
|
|
self.preview(int(previewPage) - 1)
|
|
return
|
|
else :
|
|
return
|
|
## self.preview(self.previewPage)
|
|
if self.arw["automaticUpdate"].get_active() == 0 : # If automatic update is not disabled
|
|
self.preview(self.previewPage) # update the preview
|
|
|
|
|
|
|
|
|
|
def previewDelayedUpdate(self, event) : # Unused : does not work
|
|
return
|
|
print("previewUpdateDelayed")
|
|
try :
|
|
self.t1.cancel()
|
|
except :
|
|
pass
|
|
self.t1 = threading.Timer(1, self.previewUpdate, [event])
|
|
self.t1.start()
|
|
print("timer démarré")
|
|
|
|
def preview2(self, widget) :
|
|
global selected_page
|
|
selected_page = None
|
|
previewPage = int(widget.get_text())
|
|
self.previewPage = previewPage - 1
|
|
self.arw["previewEntry"].set_text(str(self.previewPage + 1))
|
|
if self.arw["automaticUpdate"].get_active() == 0 : # If automatic update is not disabled
|
|
self.preview(self.previewPage) # update the preview
|
|
|
|
def manage_backup(self) :
|
|
return
|
|
if self.backup_command == False :
|
|
self.backup_command = True
|
|
return
|
|
if len(self.backup) == 0 :
|
|
self.backup.append(copy.deepcopy(config))
|
|
self.backup_index = len(self.backup)
|
|
else :
|
|
last = self.backup[-1]
|
|
a = repr(config)
|
|
b = repr(last)
|
|
if a == b :
|
|
return
|
|
else :
|
|
self.backup.append(copy.deepcopy(config))
|
|
#print (len(self.backup))
|
|
self.backup_index = len(self.backup)
|
|
|
|
def go_back(self, widget) :
|
|
config = copy.deepcopy(self.backup[self.backup_index - 2])
|
|
self.setupGui()
|
|
self.backup_command = False
|
|
|
|
def _____________________SHUFFLER() :
|
|
pass
|
|
|
|
|
|
def runPS(self, widget=None) :
|
|
global inputFiles_a, pagesSel
|
|
|
|
if len(inputFiles_a) == 0 :
|
|
showwarning(_("No selection"), _("There is no selected file. \nPlease select a file first. "))
|
|
return
|
|
if self.shuffler == None :
|
|
self.shuffler = PdfShuffler()
|
|
self.shuffler_window = self.shuffler.uiXML.get_object('main_window')
|
|
self.shuffler.uiXML.get_object('menubar').hide()
|
|
self.shuffler.uiXML.get_object('toolbar1').hide()
|
|
#self.shuffler.uiXML.get_object('menu1_RR').hide()
|
|
#self.shuffler.uiXML.get_object('menu1_RL').hide()
|
|
self.shuffler.uiXML.get_object('menu1_crop').hide()
|
|
self.shuffler.window.set_deletable(False)
|
|
shufflerBB = self.arw['shufflerbuttonbox']
|
|
shufflerBB.unparent()
|
|
vbox = self.shuffler.uiXML.get_object('vbox1')
|
|
vbox.pack_start(shufflerBB, False, True, 0)
|
|
|
|
self.loadShuffler()
|
|
|
|
else :
|
|
self.shuffler_window.show()
|
|
|
|
|
|
def loadShuffler(self) :
|
|
render.parsePageSelection("", 0)
|
|
|
|
for key in inputFiles_a :
|
|
pdfdoc = PDF_Doc(inputFiles_a[key], self.shuffler.nfile, self.shuffler.tmp_dir)
|
|
if pdfdoc.nfile != 0 and pdfdoc != []:
|
|
self.shuffler.nfile = pdfdoc.nfile
|
|
self.shuffler.pdfqueue.append(pdfdoc)
|
|
|
|
angle=0
|
|
crop=[0.,0.,0.,0.]
|
|
for page in pagesSel :
|
|
file1, page1 = page.split(":")
|
|
npage = int(page1) + 1
|
|
filenumber = int(file1) - 1
|
|
pdfdoc = self.shuffler.pdfqueue[filenumber]
|
|
if npage > 0 :
|
|
docPage = pdfdoc.document.get_page(npage-1)
|
|
else :
|
|
docPage = pdfdoc.document.get_page(0)
|
|
w, h = docPage.get_size()
|
|
|
|
# blank page
|
|
if npage == 0 :
|
|
descriptor = 'Blank'
|
|
width = self.shuffler.iv_col_width
|
|
row =(descriptor, # 0
|
|
None, # 1
|
|
1, # 2
|
|
-1, # 3
|
|
self.zoom_scale, # 4
|
|
"", # 5
|
|
0, # 6
|
|
0,0, # 7-8
|
|
0,0, # 9-10
|
|
w,h, # 11-12
|
|
2. ) # 13 FIXME
|
|
|
|
self.shuffler.model.append(row)
|
|
else :
|
|
|
|
|
|
|
|
|
|
|
|
descriptor = ''.join([pdfdoc.shortname, '\n', _('page'), ' ', str(npage)])
|
|
iter = self.shuffler.model.append((descriptor, # 0
|
|
None, # 1
|
|
pdfdoc.nfile, # 2
|
|
npage, # 3
|
|
self.shuffler.zoom_scale, # 4
|
|
pdfdoc.filename, # 5
|
|
angle, # 6
|
|
crop[0],crop[1], # 7-8
|
|
crop[2],crop[3], # 9-10
|
|
w,h, # 11-12
|
|
2. )) # 13 FIXME
|
|
self.shuffler.update_geometry(iter)
|
|
res = True
|
|
|
|
self.shuffler.reset_iv_width()
|
|
if res:
|
|
self.shuffler.render()
|
|
return res
|
|
|
|
|
|
|
|
def closePS(self) :
|
|
if self.shuffler :
|
|
self.shuffler.rendering_thread.quit = True
|
|
Gdk.threads_enter()
|
|
# TODO ££
|
|
## if self.shuffler.rendering_thread.paused == True:
|
|
## self.shuffler.rendering_thread.evnt.set()
|
|
## self.shuffler.rendering_thread.evnt.clear()
|
|
self.shuffler_window.destroy()
|
|
self.shuffler= None
|
|
self.shuffler
|
|
self.runPS()
|
|
|
|
def getShufflerSel(self, widget):
|
|
|
|
selection = []
|
|
for row in self.shuffler.model:
|
|
Id = str(row[2]) + ":" + str(row[3])
|
|
selection += [Id]
|
|
angle = row[7]
|
|
|
|
if angle != 0 :
|
|
if angle == 90 : # In Pdf format, global rotation rotates clockwise,
|
|
angle = 270 # fine rotation (used by PdfBooklet) rotates counterclockwise.
|
|
|
|
elif angle == -90 :
|
|
angle = 90
|
|
|
|
|
|
if not Id in config and angle != 0 :
|
|
config[Id] = {}
|
|
# defaults
|
|
config[Id]["htranslate"] = 0
|
|
config[Id]["vtranslate"] = 0
|
|
config[Id]["scale"] = 1
|
|
config[Id]["rotate"] = 0
|
|
|
|
if angle != 0 :
|
|
config[Id]["shuffler_rotate"] = angle
|
|
## if angle == 270 :
|
|
## config[Id]["vtranslate"] = pix_w
|
|
## elif angle == 90 :
|
|
## config[Id]["htranslate"] = pix_h
|
|
|
|
|
|
|
|
|
|
|
|
self.selection_s = self.compressSelection(selection)
|
|
self.shuffler_window.hide()
|
|
self.previewUpdate()
|
|
|
|
|
|
def closeShuffler(self, widget) :
|
|
self.shuffler_window.hide()
|
|
|
|
|
|
def ________________TRANSFORMATIONS() :
|
|
pass
|
|
|
|
|
|
def ta2(self, widget, event = "") :
|
|
print("ta2", event)
|
|
self.transformationsApply("")
|
|
|
|
def transformationsApply(self, widget, event="", force_update = False) :
|
|
# Reads the values in the gui and updates the config dictionary
|
|
|
|
global selected_page, selected_pages_a, rows_i
|
|
|
|
for this_selected_page in selected_pages_a :
|
|
|
|
if this_selected_page == None :
|
|
showwarning(_("No selection"), _("There is no selected page. \nPlease select a page first. "))
|
|
return
|
|
# this_selected_page 4 and 5 contain the correct page reference, including the global rotation.
|
|
humanReadableRow_i = rows_i - this_selected_page[4]
|
|
Id = str(str(humanReadableRow_i) + "," + str(this_selected_page[5] + 1))
|
|
|
|
pageId = str(this_selected_page[2]) + ":" + str(this_selected_page[3])
|
|
|
|
# if transformation is for this page only, use page ref instead of position ref
|
|
if self.thispage.get_active() == 1 :
|
|
Id = pageId
|
|
if self.evenpages.get_active() == 1 :
|
|
Id = "even"
|
|
if self.oddpages.get_active() == 1 :
|
|
Id = "odd"
|
|
|
|
config[Id] = {}
|
|
|
|
config[Id]["htranslate"] = self.arw["htranslate1"].get_text() # data from the gui unmodified
|
|
config[Id]["vtranslate"] = self.Vtranslate1.get_text()
|
|
config[Id]["scale"] = self.scale1.get_text()
|
|
config[Id]["rotate"] = self.rotation1.get_text()
|
|
config[Id]["xscale"] = self.arw["xscale1"].get_text()
|
|
config[Id]["yscale"] = self.arw["yscale1"].get_text()
|
|
config[Id]["vflip"] = self.arw["vflip1"].get_active()
|
|
config[Id]["hflip"] = self.arw["hflip1"].get_active()
|
|
#if not force_update :
|
|
# prevent useless update. If no change, return.
|
|
# TODO : à débuguer
|
|
## a = repr(config[Id])
|
|
## if not "memory1" in self :
|
|
## memory1 = {}
|
|
## b = self.memory1["memory1"]
|
|
## if a == b :
|
|
## return
|
|
## else :
|
|
## self.memory1["memory2"] = 0
|
|
## self.memory1["memory1"] = a
|
|
## self.memory1["memory2"] += 1
|
|
|
|
if self.arw["automaticUpdate"].get_active() == 0 : # If automatic update is not disabled
|
|
self.preview(self.previewPage) # update the preview
|
|
|
|
|
|
def resetTransformations(self, event = 0) :
|
|
self.arw["htranslate1"].set_value(0)
|
|
self.arw["vtranslate1"].set_value(0)
|
|
self.arw["scale1"].set_value(100)
|
|
self.arw["xscale1"].set_value(100)
|
|
self.arw["yscale1"].set_value(100)
|
|
self.arw["rotation1"].set_value(0)
|
|
self.arw["vflip1"].set_active(False)
|
|
self.arw["hflip1"].set_active(False)
|
|
|
|
if event != 0 : self.transformationsApply("dummy", force_update = True)
|
|
pass
|
|
|
|
def resetTransformations2(self, event = 0) :
|
|
# reset default values for global transformations
|
|
self.arw["htranslate2"].set_value(0)
|
|
self.arw["vtranslate2"].set_value(0)
|
|
self.arw["scale2"].set_value(100)
|
|
self.arw["xscale2"].set_value(100)
|
|
self.arw["yscale2"].set_value(100)
|
|
self.arw["rotation2"].set_value(0)
|
|
self.arw["vflip2"].set_active(False)
|
|
self.arw["hflip2"].set_active(False)
|
|
if event != 0 :
|
|
self.arw["globalRotation0"].set_active(1)
|
|
self.previewUpdate()
|
|
pass
|
|
|
|
def copy_transformations(self, event) : # called by context menu
|
|
self.clipboard["htranslate1"] = self.arw["htranslate1"].get_text() # data from the gui unmodified
|
|
self.clipboard["vtranslate1"] = self.Vtranslate1.get_text()
|
|
self.clipboard["scale1"] = self.scale1.get_text()
|
|
self.clipboard["rotate1"] = self.rotation1.get_text()
|
|
self.clipboard["this_page"] = self.thispage.get_active()
|
|
self.clipboard["xscale1"] = self.arw["xscale1"].get_text()
|
|
self.clipboard["yscale1"] = self.arw["yscale1"].get_text()
|
|
self.clipboard["vflip1"] = self.arw["vflip1"].get_active()
|
|
self.clipboard["hflip1"] = self.arw["hflip1"].get_active()
|
|
|
|
def paste_transformations(self, event) : # called by context menu
|
|
self.arw["htranslate1"].set_text(self.clipboard["htranslate1"])
|
|
self.Vtranslate1.set_text(self.clipboard["vtranslate1"])
|
|
self.scale1.set_text(self.clipboard["scale1"])
|
|
self.rotation1.set_text(self.clipboard["rotate1"])
|
|
self.thispage.set_active(self.clipboard["this_page"])
|
|
self.arw["xscale1"].set_text(self.clipboard["xscale1"])
|
|
self.arw["yscale1"].set_text(self.clipboard["yscale1"])
|
|
self.arw["vflip1"].set_active(self.clipboard["vflip1"])
|
|
self.arw["hflip1"].set_active(self.clipboard["hflip1"])
|
|
|
|
self.transformationsApply("", force_update = True)
|
|
|
|
|
|
|
|
def version21(self, widget) :
|
|
showwarning("Not yet implemented", "This feature will be implemented in version 2.2")
|
|
|
|
def aboutPdfbooklet(self, widget) :
|
|
self.arw["Pdf-Booklet"].show()
|
|
|
|
def aboutdialog1_close(self, widget,event) :
|
|
self.arw["Pdf-Booklet"].hide()
|
|
|
|
def print2(self, string, cr=0) : # no longer used
|
|
global editIniFile
|
|
|
|
return
|
|
editIniFile = 0
|
|
enditer = self.text1.get_end_iter()
|
|
self.text1.insert(enditer, string)
|
|
if cr == 1 :
|
|
self.text1.insert(enditer, chr(10))
|
|
iter0 = self.text1.get_end_iter()
|
|
self.arw["text1"].scroll_to_iter(iter0,0)
|
|
# TODO : bug
|
|
# This command hangs the program (the interface remains blank) when :
|
|
# a file has been loaded
|
|
# The page selector has been used
|
|
# a second file is loaded.
|
|
#while Gtk.events_pending():
|
|
# Gtk.main_iteration()
|
|
|
|
def test(self,event, dummy = None) :
|
|
print("test")
|
|
|
|
def destroyWindow() :
|
|
pass # commande encore présente dans Glade, à supprimer
|
|
|
|
def go(self, button, preview = -1) :
|
|
|
|
if self.readGui() :
|
|
if self.render.parsePageSelection() :
|
|
self.readConditions()
|
|
ar_pages, ar_layout, ar_cahiers = self.render.createPageLayout()
|
|
if ar_pages != None :
|
|
# Verify that the output file may be written to
|
|
|
|
if app.arw["entry2"].get_text() == "" :
|
|
inputFile = inputFiles_a[1]
|
|
inputFile_name = os.path.splitext(inputFile)[0]
|
|
outputFile = inputFile_name + "-bklt.pdf"
|
|
else :
|
|
outputFile = app.arw["entry2"].get_text()
|
|
inputFile = inputFiles_a[1]
|
|
inputFile_wo_ext = os.path.splitext(inputFile)[0]
|
|
(inputFile_path, inputFile_name) = os.path.split(inputFile)
|
|
(inputFile_basename, inputFile_ext) = os.path.splitext(inputFile_name)
|
|
inputFile_path += "/"
|
|
inputFile_ext = inputFile_ext[1:]
|
|
|
|
|
|
outputFile = outputFile.replace("%F", inputFile_wo_ext)
|
|
outputFile = outputFile.replace("%N", inputFile_name)
|
|
outputFile = outputFile.replace("%B", inputFile_basename)
|
|
outputFile = outputFile.replace("%P", inputFile_path)
|
|
outputFile = outputFile.replace("%E", inputFile_ext)
|
|
|
|
if preview == -1 :
|
|
if os.path.isfile(outputFile) :
|
|
if app.overwrite.get_active() == 0 :
|
|
answer_b = askyesno(_("File existing"), _("The outputfile already exists \n" \
|
|
"overWrite ? " ))
|
|
if False == answer_b :
|
|
return False
|
|
|
|
|
|
if self.render.createNewPdf(ar_pages, ar_layout, ar_cahiers, outputFile, preview) :
|
|
if self.arw["show"].get_active() == 1 :
|
|
|
|
if 'linux' in sys.platform :
|
|
subprocess.call(["xdg-open", outputFile])
|
|
else:
|
|
os.startfile(outputFile)
|
|
|
|
return [ar_pages, ar_layout, ar_cahiers]
|
|
|
|
|
|
|
|
|
|
def readConditions(self) :
|
|
global optionsDict, config
|
|
# Used by the go function above
|
|
return
|
|
if app.arw["entry3"].get() == "" :
|
|
return
|
|
else :
|
|
inifile = app.arw["entry3"].get()
|
|
config = parser.read(inifile)
|
|
optionsDict = {}
|
|
optionsDict["pages"] = {}
|
|
optionsDict["conditions"] = {}
|
|
|
|
if config.has_section("pages") :
|
|
for a in config.options("pages") :
|
|
optionsDict["pages"][a] = config["pages"][a]
|
|
|
|
if config.has_section("conditions") :
|
|
for a in config.options("conditions") :
|
|
optionsDict["conditions"][a] = config["conditions"][a]
|
|
|
|
|
|
def test1(self, widget) :
|
|
print("input")
|
|
def test2(self, widget) :
|
|
print("output")
|
|
def test3(self, widget) :
|
|
print("value_changed")
|
|
def test4(self, widget) :
|
|
print("change value")
|
|
def test5(self, widget) :
|
|
print("test5")
|
|
|
|
|
|
|
|
|
|
class pdfRender():
|
|
|
|
def transform(self, row, column, page_number, output_page_number, file_number) :
|
|
global optionsDict, config, rows_i
|
|
|
|
# init variables
|
|
|
|
V_offset = row * ury_i
|
|
H_offset = column * urx_i
|
|
cos_l = 1
|
|
sin_l = 0
|
|
|
|
transform_s = " %s %s %s %s %s %s cm \n" % (cos_l , sin_l, -sin_l, cos_l , H_offset , V_offset)
|
|
transformations = []
|
|
|
|
# Transformations defined in gui
|
|
|
|
# Transformations for page in position (row, col)
|
|
section_s = str(rows_i - row) + "," + str(column + 1)
|
|
if section_s in config :
|
|
transform_s += self.transform2(section_s)
|
|
|
|
# Transformations for page #:#
|
|
pageId = str(file_number) + ":" + str(page_number + 1)
|
|
if pageId in config :
|
|
transform_s += self.transform2(pageId)
|
|
|
|
# Debug !!!
|
|
""" Je ne comprends pas le sens de ce bloc, qui refait une deuxième fois la transformation,
|
|
si bien que quand, par exemple, on demande une transformation de 45, on obtient 90 !
|
|
ht = ini.readmmEntry(config[pageId]["htranslate"])
|
|
vt = ini.readmmEntry(config[pageId]["vtranslate"])
|
|
sc = ini.readPercentEntry(config[pageId]["scale"])
|
|
ro = ini.readNumEntry(config[pageId]["rotate"])
|
|
try :
|
|
pdfrotate = ini.readNumEntry(config[pageId]["pdfrotate"])
|
|
except :
|
|
pdfrotate = 0
|
|
xs = ini.readPercentEntry(config[pageId]["xscale"])
|
|
ys = ini.readPercentEntry(config[pageId]["yscale"])
|
|
|
|
|
|
|
|
matrix_s = self.calcMatrix2(ht, vt,
|
|
cScale = sc,
|
|
xscale = xs,
|
|
yscale = ys,
|
|
cRotate = ro,
|
|
Rotate = pdfrotate,
|
|
vflip = config[pageId]["vflip"],
|
|
hflip = config[pageId]["hflip"])
|
|
|
|
|
|
|
|
## if "shuffler_rotate" in config[pageId] :
|
|
## # we need the page size
|
|
## pdfDoc = app.shuffler.pdfqueue[file_number-1]
|
|
## page = pdfDoc.document.get_page(page_number)
|
|
## pix_w, pix_h = page.get_size()
|
|
##
|
|
## angle = int(config[pageId]["shuffler_rotate"])
|
|
## pdfrotate += angle
|
|
## if angle == 270 :
|
|
## vtranslate = float(vtranslate) + float(pix_w)
|
|
## elif angle == 90 :
|
|
## htranslate = float(htranslate) + float(pix_h)
|
|
## elif angle == 180 :
|
|
## htranslate = float(htranslate) + float(pix_w)
|
|
## vtranslate = float(vtranslate) + float(pix_h)
|
|
|
|
|
|
transform_s += self.calcMatrix2(ht, vt,
|
|
cScale = sc,
|
|
cRotate = ro,
|
|
Rotate = pdfrotate)
|
|
"""
|
|
# Transformations for even and odd pages
|
|
if page_number % 2 == 1 :
|
|
transform_s += self.transform2("even")
|
|
if page_number % 2 == 0 :
|
|
transform_s += self.transform2("odd")
|
|
|
|
|
|
|
|
|
|
# Transformations defined in ini file
|
|
|
|
if "pages" in config :
|
|
pages_a = config["pages"].keys()
|
|
if section_s in pages_a : # If the layout page presently treated is referenced in [pages]
|
|
temp1 = config["pages"][section_s]
|
|
transformations += temp1.split(", ")
|
|
|
|
if str(page_number + 1) in pages_a : # If the page presently treated is referenced in [pages]
|
|
temp1 = config["pages"][str(page_number + 1)]
|
|
transformations += temp1.split(", ")
|
|
|
|
|
|
if "conditions" in config :
|
|
conditions_a = config["conditions"].keys()
|
|
for line1 in conditions_a :
|
|
condition_s = config["conditions"][line1]
|
|
command_s, filters_s = condition_s.split("=>")
|
|
if (eval(command_s)) :
|
|
transformations += filters_s.split(", ")
|
|
|
|
for a in transformations :
|
|
|
|
transform_s += self.calcMatrix(a)
|
|
return (transform_s)
|
|
|
|
def transform2(self, Id) :
|
|
# Calculates matrix for a given section
|
|
matrix_s = ""
|
|
if Id in config :
|
|
if not "pdfRotate" in config[Id] :
|
|
config[Id]["pdfRotate"] = 0
|
|
if not "rotate" in config[Id] :
|
|
config[Id]["rotate"] = 0
|
|
|
|
if "shuffler_rotate" in config[Id] :
|
|
# we need the page size
|
|
pdfDoc = app.shuffler.pdfqueue[file_number-1]
|
|
page = pdfDoc.document.get_page(page_number)
|
|
pix_w, pix_h = page.get_size()
|
|
|
|
angle = int(config[Id]["shuffler_rotate"])
|
|
pdfrotate += angle
|
|
if angle == 270 :
|
|
vtranslate = float(vtranslate) + float(pix_w)
|
|
elif angle == 90 :
|
|
htranslate = float(htranslate) + float(pix_h)
|
|
elif angle == 180 :
|
|
htranslate = float(htranslate) + float(pix_w)
|
|
vtranslate = float(vtranslate) + float(pix_h)
|
|
|
|
ht = ini.readmmEntry(config[Id]["htranslate"])
|
|
vt = ini.readmmEntry(config[Id]["vtranslate"])
|
|
sc = ini.readPercentEntry(config[Id]["scale"])
|
|
ro = ini.readNumEntry(config[Id]["rotate"])
|
|
xs = ini.readPercentEntry(config[Id]["xscale"])
|
|
ys = ini.readPercentEntry(config[Id]["yscale"])
|
|
|
|
|
|
|
|
matrix_s = self.calcMatrix2(ht, vt,
|
|
cScale = sc,
|
|
xscale = xs,
|
|
yscale = ys,
|
|
cRotate = ro,
|
|
Rotate = ini.readNumEntry(config[Id]["pdfRotate"]),
|
|
vflip = ini.readBoolean(config[Id]["vflip"]),
|
|
hflip = ini.readBoolean(config[Id]["hflip"]))
|
|
|
|
|
|
|
|
return matrix_s
|
|
|
|
|
|
def calcMatrix(self, mydata, myrows_i = 1, mycolumns_i = 1) :
|
|
# Calculate matrix for transformations defined in the configuration
|
|
global config
|
|
|
|
trans = mydata.strip()
|
|
cos_l = 1
|
|
cos2_l = 1
|
|
sin_l = 0
|
|
Htranslate = 0
|
|
Vtranslate = 0
|
|
|
|
|
|
if config.has_option(trans, "PdfRotate") :
|
|
Rotate = config.getint(trans, "PdfRotate")
|
|
sin_l = math.sin(math.radians(Rotate))
|
|
cos_l = math.cos(math.radians(Rotate))
|
|
cos2_l = cos_l
|
|
|
|
if config.has_option(trans, "Rotate") :
|
|
try :
|
|
Rotate = config.getfloat(trans, "Rotate")
|
|
except :
|
|
pass
|
|
|
|
sin_l, cos_l, HCorr, VCorr = self.centeredRotation(Rotate, myrows_i, mycolumns_i)
|
|
cos2_l = cos_l
|
|
|
|
Htranslate += HCorr
|
|
Vtranslate += VCorr
|
|
|
|
|
|
if config.has_option(trans, "Scale") :
|
|
Scale_f = ini.readPercentEntry(config[trans]["Scale"])
|
|
cos_l = cos_l * Scale_f
|
|
cos2_l = cos_l
|
|
|
|
HCorr = (urx_i * mycolumns_i * (Scale_f - 1)) / 2
|
|
VCorr = (ury_i * myrows_i * (Scale_f - 1)) / 2
|
|
Htranslate -= HCorr
|
|
Vtranslate -= VCorr
|
|
|
|
if config.has_option(trans, "xscale") :
|
|
Scale_f = ini.readPercentEntry(config[trans]["xscale"])
|
|
cos_l = cos_l * Scale_f
|
|
|
|
HCorr = (urx_i * mycolumns_i * (Scale_f - 1)) / 2
|
|
Htranslate -= HCorr
|
|
|
|
if config.has_option(trans, "yScale") :
|
|
Scale_f = ini.readPercentEntry(config[trans]["yScale"])
|
|
cos2_l = cos2_l * Scale_f
|
|
|
|
VCorr = (ury_i * myrows_i * (Scale_f - 1)) / 2
|
|
Vtranslate -= VCorr
|
|
|
|
# Vertical flip : 1 0 0 -1 0 <height>
|
|
if config.has_option(trans, "vflip") :
|
|
value = config[trans]["vflip"].lower()
|
|
if value == "true" or value == "1" :
|
|
cos2_l = cos2_l * (-1)
|
|
Vtranslate += ury_i * myrows_i
|
|
|
|
# Horizontal flip : -1 0 0 1 0 <width>
|
|
if config.has_option(trans, "hflip") :
|
|
value = config[trans]["hflip"].lower()
|
|
if value == "true" or value == "1" :
|
|
cos_l = cos_l * (-1)
|
|
Htranslate += urx_i * mycolumns_i
|
|
|
|
|
|
if config.has_option(trans, "htranslate") :
|
|
ht = ini.readNumEntry(config[trans]["htranslate"])
|
|
Htranslate += ht / adobe_l
|
|
|
|
if config.has_option(trans, "vtranslate") :
|
|
vt = ini.readNumEntry(config[trans]["vtranslate"])
|
|
Vtranslate += vt / adobe_l
|
|
|
|
|
|
|
|
if abs(sin_l) < 0.00001 : sin_l = 0 # contournement d'un petit problème : sin 180 ne renvoie pas 0 mais 1.22460635382e-16
|
|
if abs(cos_l) < 0.00001 : cos_l = 0
|
|
|
|
|
|
|
|
transform_s = " %s %s %s %s %s %s cm \n" % (cos_l , sin_l, -sin_l, cos2_l , Htranslate , Vtranslate)
|
|
|
|
|
|
if "Matrix" in config[trans] :
|
|
Matrix = config[trans]["Matrix"]
|
|
transform_s = " %s cm \n" % (Matrix)
|
|
|
|
|
|
return transform_s
|
|
|
|
|
|
def calcMatrix2(self, Htranslate, Vtranslate,
|
|
cScale = 1, Scale = 1,
|
|
Rotate = 0, cRotate = 0,
|
|
vflip = 0, hflip = 0,
|
|
xscale = 1, yscale = 1,
|
|
global_b = False) :
|
|
# calculate matrix for transformations defined in parameters
|
|
|
|
Htranslate = float(Htranslate)
|
|
Vtranslate = float(Vtranslate)
|
|
cos_l = 1
|
|
sin_l = 0
|
|
|
|
if global_b == True : # for global transformations, reference for centered scale, rotation and flip is the output page
|
|
myrows_i = rows_i
|
|
mycolumns_i = columns_i
|
|
else : # for page transformations, reference is the active source page
|
|
myrows_i = 1
|
|
mycolumns_i = 1
|
|
|
|
if Scale != 1:
|
|
Scale_f = float(Scale)
|
|
elif cScale != 1 :
|
|
Scale_f = float(cScale)
|
|
else :
|
|
Scale_f = 1
|
|
|
|
if Rotate != 0 :
|
|
sin_l = math.sin(math.radians(float(Rotate)))
|
|
cos_l = math.cos(math.radians(float(Rotate)))
|
|
# TODO Rotate and cRotate are not compatible.
|
|
elif cRotate != 0 :
|
|
sin_l, cos_l, HCorr, VCorr = self.centeredRotation(float(cRotate), myrows_i, mycolumns_i)
|
|
Htranslate += (HCorr * Scale_f)
|
|
Vtranslate += (VCorr * Scale_f)
|
|
|
|
if Scale != 1 :
|
|
sin_l = sin_l * Scale_f
|
|
cos_l = cos_l * Scale_f
|
|
HCorr = (urx_i * (Scale_f - 1)) / 2
|
|
VCorr = (ury_i * (Scale_f - 1)) / 2
|
|
|
|
if cScale != 1 :
|
|
sin_l = sin_l* Scale_f
|
|
cos_l = cos_l * Scale_f
|
|
HCorr = (urx_i * mycolumns_i * (Scale_f - 1)) / 2
|
|
VCorr = (ury_i * myrows_i * (Scale_f - 1)) / 2
|
|
|
|
Htranslate -= HCorr
|
|
Vtranslate -= VCorr
|
|
|
|
|
|
if abs(sin_l) < 0.00001 : sin_l = 0 # contournement d'un petit problème : sin 180 ne renvoie pas 0 mais 1.22460635382e-16
|
|
if abs(cos_l) < 0.00001 : cos_l = 0
|
|
|
|
transform_s = " %s %s %s %s %s %s cm \n" % (cos_l , sin_l, -sin_l, cos_l , Htranslate , Vtranslate)
|
|
|
|
|
|
Htranslate = 0
|
|
Vtranslate = 0
|
|
cos_l = 1
|
|
cos2_l = 1
|
|
sin_l = 0
|
|
|
|
|
|
if xscale != '1' and xscale != 1 :
|
|
xscale = float(xscale)
|
|
cos_l = cos_l * xscale
|
|
|
|
HCorr = (urx_i * mycolumns_i * (xscale - 1)) / 2
|
|
Htranslate -= HCorr
|
|
|
|
if yscale != '1' and yscale != 1:
|
|
yscale = float(yscale)
|
|
cos2_l = cos2_l * yscale
|
|
|
|
VCorr = (ury_i * myrows_i * (yscale - 1)) / 2
|
|
Vtranslate -= VCorr
|
|
|
|
if abs(sin_l) < 0.00001 : sin_l = 0 # contournement d'un petit problème : sin 180 ne renvoie pas 0 mais 1.22460635382e-16
|
|
if abs(cos_l) < 0.00001 : cos_l = 0
|
|
|
|
transform_s += " %s %s %s %s %s %s cm \n" % (cos_l , sin_l, -sin_l, cos2_l , Htranslate , Vtranslate)
|
|
|
|
|
|
Htranslate = 0
|
|
Vtranslate = 0
|
|
cos_l = 1
|
|
cos2_l = 1
|
|
sin_l = 0
|
|
|
|
# Vertical flip : 1 0 0 -1 0 <height>
|
|
if vflip != 0 and vflip != False :
|
|
cos2_l = cos2_l * (-1)
|
|
Vtranslate += ury_i * myrows_i
|
|
|
|
# Horizontal flip : -1 0 0 1 0 <width>
|
|
if hflip != 0 and hflip != False :
|
|
cos_l = cos_l * (-1)
|
|
Htranslate += urx_i * mycolumns_i
|
|
|
|
|
|
|
|
if abs(sin_l) < 0.00001 : sin_l = 0 # contournement d'un petit problème : sin 180 ne renvoie pas 0 mais 1.22460635382e-16
|
|
if abs(cos_l) < 0.00001 : cos_l = 0
|
|
if abs(cos2_l) < 0.00001 : cos2_l = 0
|
|
|
|
transform_s += " %s %s %s %s %s %s cm \n" % (cos_l , sin_l, -sin_l, cos2_l , Htranslate , Vtranslate)
|
|
return transform_s
|
|
|
|
def centeredRotation_old(self, Rotate) :
|
|
|
|
Rotate = math.radians(Rotate)
|
|
sin_l = math.sin(Rotate)
|
|
cos_l = math.cos(Rotate)
|
|
|
|
# If a is the angle of the diagonale, and R the rotation angle, the center of the rectangle moves like this :
|
|
# Horizontal move = sin(a + R) - sin(a)
|
|
# Vertical move = cos(a + R) - cos(a)
|
|
#Hence, corrections are sin(a) - sin(a+R) and cos(a) - cos(a-R)
|
|
|
|
diag = math.pow((urx_i * urx_i) + (ury_i * ury_i), 0.5)
|
|
alpha = math.atan2(ury_i, urx_i)
|
|
|
|
S1 = math.sin(alpha)
|
|
S2 = math.sin(alpha + Rotate)
|
|
|
|
C1 = math.cos(alpha)
|
|
C2 = math.cos(alpha + Rotate)
|
|
|
|
Vcorr = (S1 - S2) * diag / 2
|
|
Hcorr = (C1 - C2) * diag / 2
|
|
|
|
return (sin_l, cos_l, Hcorr, Vcorr)
|
|
|
|
def centeredRotation(self, Rotate, myrows_i = 1, mycolumns_i = 1) :
|
|
|
|
Rotate = math.radians(Rotate)
|
|
sin_l = math.sin(Rotate)
|
|
cos_l = math.cos(Rotate)
|
|
|
|
# If a is the angle of the diagonale, and R the rotation angle, the center of the rectangle moves like this :
|
|
# Horizontal move = sin(a + R) - sin(a)
|
|
# Vertical move = cos(a + R) - cos(a)
|
|
#Hence, corrections are sin(a) - sin(a+R) and cos(a) - cos(a-R)
|
|
|
|
oWidth_i = urx_i * mycolumns_i
|
|
oHeight_i = ury_i * myrows_i
|
|
|
|
diag = math.pow((oWidth_i * oWidth_i) + (oHeight_i * oHeight_i), 0.5)
|
|
alpha = math.atan2(oHeight_i, oWidth_i)
|
|
|
|
S1 = math.sin(alpha)
|
|
S2 = math.sin(alpha + Rotate)
|
|
|
|
C1 = math.cos(alpha)
|
|
C2 = math.cos(alpha + Rotate)
|
|
|
|
Vcorr = (S1 - S2) * diag / 2
|
|
Hcorr = (C1 - C2) * diag / 2
|
|
|
|
return (sin_l, cos_l, Hcorr, Vcorr)
|
|
|
|
def CalcAutoScale(self, fileNum, page) :
|
|
global inputFiles_a, inputFile_a, refPageSize_a
|
|
|
|
fileName = inputFiles_a[fileNum]
|
|
page0 = inputFile_a[fileName].getPage(page)
|
|
llx_i=page0.mediaBox.getLowerLeft_x()
|
|
lly_i=page0.mediaBox.getLowerLeft_y()
|
|
urx_i=page0.mediaBox.getUpperRight_x()
|
|
ury_i=page0.mediaBox.getUpperRight_y()
|
|
|
|
page_width = float(urx_i) - float(llx_i)
|
|
page_height = float(ury_i) - float(lly_i)
|
|
|
|
(ref_width, ref_height) = refPageSize_a
|
|
|
|
delta1 = ref_height / page_height
|
|
delta2 = ref_width / page_width
|
|
Vdiff = ref_height - (page_height * delta2)
|
|
Hdiff = ref_width - (page_width * delta1)
|
|
|
|
# Choose the lower factor, and calculate the translation for centering the image
|
|
if delta1 < delta2 :
|
|
Scale = delta1
|
|
Vtranslate = 0
|
|
Htranslate = Hdiff/2
|
|
else:
|
|
Scale = delta2
|
|
Vtranslate = Vdiff/2
|
|
Htranslate = 0
|
|
|
|
return (Scale, Htranslate, Vtranslate)
|
|
|
|
|
|
def parsePageSelection(self, selection = "", append_prepend = 1) :
|
|
global pagesSel, totalPages, pgcount, input1, numPages, step_i, blankPages
|
|
global inputFiles_a, inputFile_a
|
|
|
|
if len(inputFiles_a) == 0 :
|
|
showwarning(_("No file loaded"), _("Please select a file first"))
|
|
return False
|
|
|
|
if selection == "" :
|
|
selection = app.selection_s
|
|
|
|
if selection.strip() == "" : # if there is no selection, then we create the default selection
|
|
# which contains all pages of all documents
|
|
i = 1
|
|
for f in inputFiles_a :
|
|
fileName = inputFiles_a[f]
|
|
numPages = inputFile_a[fileName].getNumPages()
|
|
selection += str(i) + ":1-%s;" % (numPages)
|
|
i += 1
|
|
app.selection_s = selection
|
|
|
|
if selection == "" : # This should not happen...
|
|
showwarning(_("No selection"), _("There is no selection"))
|
|
return False
|
|
|
|
selection = re.sub("[;,]{2,}", ";", selection) # we replace successive ; or , by a single ;
|
|
syntax_s = re.sub("[0-9,;:b\-\s]*", "", selection) # To verify all characters are valid, we remove all them and there should remain nothing
|
|
syntax_s = syntax_s.strip()
|
|
if syntax_s != "" : # If something remains, it is not valid
|
|
showwarning(_("Invalid data"), _("Invalid data for Selection : %s. Aborting \n") % syntax_s)
|
|
return False
|
|
|
|
if append_prepend == 1 :
|
|
pagesSel = prependPages * ["1:-1"]
|
|
else :
|
|
pagesSel = []
|
|
|
|
|
|
selection = selection.replace(";", ",")
|
|
selection = selection.strip()
|
|
if selection[-1:] == "," : # remove the trailing ,
|
|
selection = selection[0:-1]
|
|
list1 = selection.split(",")
|
|
|
|
for a in list1 :
|
|
a = a.strip()
|
|
b = a.split(":")
|
|
if (len(b) == 1) :
|
|
docId_s = "1:"
|
|
else :
|
|
docId_s = b[0] + ":"
|
|
a = b[1]
|
|
|
|
if a.count("-") > 0:
|
|
list2 = a.split("-")
|
|
serie = list(range(int(list2[0]) - 1, int(list2[1])))
|
|
for x in serie :
|
|
page_s = docId_s + str(x)
|
|
pagesSel = pagesSel + [page_s]
|
|
elif a[-1:] == "b" :
|
|
if a[0:-1].strip() == "" :
|
|
blank_pages_i = 1
|
|
else :
|
|
blank_pages_i = int(a[0:-1])
|
|
pagesSel = pagesSel + (["1:-1"] * blank_pages_i)
|
|
else :
|
|
try :
|
|
a = str(int(a) - 1)
|
|
pagesSel = pagesSel + [docId_s + a]
|
|
except :
|
|
alert("Invalid selection ", a)
|
|
if append_prepend == 1 :
|
|
pagesSel = pagesSel + appendPages * ["1:-1"]
|
|
|
|
if ini.booklet > 0 :
|
|
step_i = 2
|
|
blankPages = (len(pagesSel) % -4) * -1
|
|
#app.print2(_("Blank pages to be added : %s") % (blankPages) , 1)
|
|
|
|
else :
|
|
if step_i < 1 :
|
|
step_i = 1
|
|
blankPages = (len(pagesSel) % (step_i * -1)) * -1
|
|
|
|
pagesSel += ["1:-1"] * blankPages
|
|
|
|
|
|
|
|
totalPages = len(pagesSel)
|
|
pgcount = totalPages
|
|
|
|
return True
|
|
|
|
|
|
def createPageLayout(self, logdata = 1) :
|
|
global config, rows_i, columns_i, cells_i, step_i, sections, output, input1, adobe_l
|
|
global numfolio,prependPages, appendPages, ref_page, selection
|
|
global numPages, blankPages, pagesSel, llx_i, lly_i, urx_i, ury_i, mediabox_l, pgcount
|
|
|
|
ar_pages = {}
|
|
ar_cahiers = {}
|
|
index=0
|
|
last=0
|
|
cells_i = columns_i * rows_i
|
|
|
|
|
|
# Create booklets
|
|
if ini.booklet > 0 :
|
|
|
|
multiple_bkt = int(cells_i / 2)
|
|
ini.radioDisp= 1
|
|
|
|
|
|
folios = pgcount / 4
|
|
if numfolio == 0 : # single booklet
|
|
numcahiers = 1
|
|
else : # multiple booklets
|
|
numcahiers = int(folios / numfolio)
|
|
if (folios % numfolio > 0) :
|
|
numcahiers = numcahiers + 1
|
|
|
|
# equilibrate booklets size
|
|
minfolios = int(folios / numcahiers)
|
|
restefolios = folios - (minfolios * numcahiers)
|
|
|
|
for k in range(numcahiers) :
|
|
if (k < restefolios) : # number of folios in each booklet
|
|
ar_cahiers[k] = minfolios + 1
|
|
else :
|
|
ar_cahiers[k] = minfolios
|
|
|
|
first = last + 1 # num of first page of the booklet
|
|
last = first + (ar_cahiers[k] * 4) - 1 # num of the last page of the booklet
|
|
|
|
if logdata == 1 :
|
|
#app.print2(_( "Booklet %s : pages %s - %s") % (k + 1, first, last), 1)
|
|
pass
|
|
|
|
bkltPages = (last - first) + 1 # number of pages in the booklet
|
|
for i in range (bkltPages // 2) : # page nums for each sheet
|
|
if ((i % 2) == 0) : # Page paire à gauche
|
|
pg2 = (i + first)
|
|
pg1 = (last - i)
|
|
else :
|
|
pg1 = (i + first)
|
|
pg2 = (last - i)
|
|
|
|
ar_pages[index] = [pagesSel[pg1 - 1], pagesSel[pg2 - 1]] * multiple_bkt
|
|
index += 1
|
|
|
|
else :
|
|
while index < (pgcount / step_i) :
|
|
start = last
|
|
last = last + step_i
|
|
pages = []
|
|
for a in range(start, start + cells_i) :
|
|
PageX = start + (a % step_i)
|
|
if PageX > totalPages :
|
|
pages = pages + [-1]
|
|
else :
|
|
pages = pages + [pagesSel[PageX]]
|
|
ar_pages[index] = pages
|
|
index += 1
|
|
|
|
|
|
# create layout
|
|
ar_layout = []
|
|
if ini.radioDisp == 1 :
|
|
for r in range(rows_i) :
|
|
r2 = rows_i - (r + 1) # rows are counted from bottom because reference is lower left in pdf so we must invert
|
|
for c in range (columns_i) :
|
|
ar_layout += [[r2, c]]
|
|
elif ini.radioDisp == 2 :
|
|
for c in range(columns_i) :
|
|
for r in range(rows_i) :
|
|
r2 = rows_i - (r + 1) # rows are counted from bottom so we must invert
|
|
ar_layout += [[r2, c]]
|
|
|
|
# If option "Right to left" has been selected creates inverted layout (which will overwrite the previous one)
|
|
|
|
if bool_test(config["options"]["righttoleft"]) == True :
|
|
|
|
# create inverted layout
|
|
ar_layout = []
|
|
if ini.radioDisp == 1 :
|
|
for r in range(rows_i) :
|
|
r2 = rows_i - (r + 1) # rows are counted from bottom because reference is lower left in pdf so we must invert
|
|
for c in range (columns_i) :
|
|
c2 = columns_i - (c + 1) # Invert for right to left option
|
|
ar_layout += [[r2, c2]]
|
|
elif ini.radioDisp == 2 :
|
|
for c in range(columns_i) :
|
|
c2 = columns_i - (c + 1) # Invert for right to left optionInvert for right to left option Invert for right to left option
|
|
for r in range(rows_i) :
|
|
r2 = rows_i - (r + 1) # rows are counted from bottom so we must invert
|
|
ar_layout += [[r2, c2]]
|
|
|
|
# End of inverted layout
|
|
|
|
|
|
# User defined layout
|
|
if "radiopreset8" in app.arw and app.arw["radiopreset8"].get_active() == 1 :
|
|
|
|
# number of sheets of this layout
|
|
sheets = len(ini.imposition)
|
|
|
|
# create blank pages if necessary
|
|
rest = len(ar_pages) % sheets
|
|
|
|
if rest > 0 :
|
|
blank = []
|
|
for i in range(len(ar_pages[0])) :
|
|
blank.append(["1:-1"])
|
|
for i in range(rest) :
|
|
ar_pages[len(ar_pages) + i] = blank
|
|
|
|
|
|
# create a copy of ar_pages
|
|
|
|
ar_pages1 = {}
|
|
for key in ar_pages :
|
|
pages1 = ar_pages[key]
|
|
temp = []
|
|
for a in pages1 :
|
|
temp.append(a)
|
|
ar_pages1[key] = temp
|
|
|
|
|
|
if sheets == 1 :
|
|
userpages = ini.imposition[0]
|
|
|
|
for key in ar_pages :
|
|
pages = ar_pages[key]
|
|
pages1 = ar_pages1[key]
|
|
|
|
i = 0
|
|
# we reorder the pages following the order given in userpages
|
|
for value in userpages :
|
|
if value == "b" :
|
|
pages[i] = "1:-1"
|
|
else :
|
|
row = int(value) - 1
|
|
pages[i] = pages1[row]
|
|
i += 1
|
|
ar_pages[key] = pages
|
|
|
|
elif sheets == 2 : # more complicated...
|
|
|
|
userpagesA = app.imposition[0]
|
|
userpagesB = app.imposition[1]
|
|
step = len(userpagesA)
|
|
|
|
for key in range(0,len(ar_pages),2) :
|
|
|
|
pagesA = ar_pages[key]
|
|
pages1A = ar_pages1[key]
|
|
pagesB = ar_pages[key + 1]
|
|
pages1B = ar_pages1[key + 1]
|
|
|
|
|
|
i = 0
|
|
# we reorder the pages following the order given in userpages
|
|
for value in userpagesA :
|
|
if value == "b" :
|
|
pagesA[i] = "1:-1"
|
|
else :
|
|
row = int(value) - 1
|
|
if row < step :
|
|
index = pages1A[row]
|
|
else :
|
|
rowB = row % step
|
|
index = pages1B[rowB]
|
|
pagesA[i] = index
|
|
i += 1
|
|
i = 0
|
|
for value in userpagesB :
|
|
if value == "b" :
|
|
pagesB[i] = "1:-1"
|
|
else :
|
|
row = int(value) - 1
|
|
if row < step :
|
|
index = pages1A[row]
|
|
else :
|
|
rowB = row % step
|
|
index = pages1B[rowB]
|
|
pagesB[i] = index
|
|
i += 1
|
|
ar_pages[key] = pagesA
|
|
ar_pages[key + 1] = pagesB
|
|
|
|
return (ar_pages, ar_layout, ar_cahiers)
|
|
|
|
|
|
|
|
def createNewPdf(self, ar_pages, ar_layout, ar_cahiers, outputFile, preview = -1) :
|
|
global debug_b, inputFile_a, inputFiles_a, result
|
|
global mediabox_l
|
|
global outputStream
|
|
|
|
|
|
if debug_b == 1 :
|
|
logfile_f = open ("log.txt", "wb")
|
|
# status
|
|
statusTotal_i = len(ar_pages)
|
|
statusValue_i = 1
|
|
|
|
|
|
time_s=time.time()
|
|
output = PdfFileWriter()
|
|
|
|
|
|
|
|
if preview >= 0 : # if this is a preview
|
|
outputStream = io.BytesIO()
|
|
|
|
else :
|
|
try :
|
|
outputStream = open(outputFile, "wb")
|
|
except :
|
|
showwarning(_("File already open"), _("The output file is already opened \n" \
|
|
"probably in Adobe Reader. \n" \
|
|
"Close the file and start again"))
|
|
return
|
|
|
|
if preview >= 0 : # if this is a preview
|
|
output_page_number = preview + 1
|
|
else :
|
|
output_page_number = 1
|
|
## # encryption
|
|
## if app.permissions_i != -1 and app.password_s != "" : # if permissions or password were present in the file
|
|
## output.encrypt("", app.password_s, P = app.permissions_i) # TODO : there may be two passwords (user and owner)
|
|
for a in ar_pages :
|
|
# create the output sheet
|
|
page2 = output.addBlankPage(100,100)
|
|
newSheet = page2.getObject()
|
|
newSheet[generic.NameObject("/Contents")] = generic.ArrayObject([])
|
|
newSheet.mediaBox.upperRight = mediabox_l # output page size
|
|
newSheet.cropBox.upperRight = mediabox_l
|
|
# global rotation
|
|
if ("options" in config) and ("globalRotation" in config["options"]) :
|
|
gr = config["options"]["globalRotation"]
|
|
gr_s = gr.strip()[14:]
|
|
gr_i = int(gr_s)
|
|
if gr_i > 0 :
|
|
newSheet[generic.NameObject("/Rotate")] = generic.NumberObject(gr_i)
|
|
|
|
i = 0
|
|
ar_data = []
|
|
|
|
|
|
if outputScale != 1 and app.autoscale.get_active() == 1 :
|
|
temp1 = "%s 0 0 %s 0 0 cm \n" % (str(outputScale), str(outputScale))
|
|
ar_data.append([temp1])
|
|
|
|
|
|
#Output page transformations
|
|
if "output" in config :
|
|
OHShift = ini.readmmEntry(config["output"]["htranslate"])
|
|
OVShift = ini.readmmEntry(config["output"]["vtranslate"])
|
|
OScale = ini.readPercentEntry(config["output"]["scale"])
|
|
ORotate = ini.readNumEntry(config["output"]["rotate"])
|
|
|
|
Ovflip = ini.readBoolean(config["output"]["vflip"])
|
|
Ohflip = ini.readBoolean(config["output"]["hflip"])
|
|
|
|
Oxscale = ini.readPercentEntry(config["output"]["xscale"])
|
|
if Oxscale == None : return False
|
|
|
|
Oyscale = ini.readPercentEntry(config["output"]["yscale"])
|
|
if Oyscale == None : return False
|
|
|
|
|
|
|
|
temp1 = self.calcMatrix2(OHShift, OVShift,
|
|
cScale = OScale,
|
|
cRotate = ORotate,
|
|
vflip = Ovflip,
|
|
hflip = Ohflip,
|
|
xscale = Oxscale,
|
|
yscale = Oyscale,
|
|
global_b = True)
|
|
ar_data.append([temp1])
|
|
|
|
|
|
|
|
# Transformations defined in ini file
|
|
|
|
|
|
if "pages" in config :
|
|
pages_a = config["pages"].keys()
|
|
if "@" + str(output_page_number) in pages_a : # If the output page presently treated is referenced in [pages]
|
|
temp1 = config["pages"]["@" + str(output_page_number)]
|
|
transformations = temp1.split(", ")
|
|
for name_s in transformations :
|
|
if config.has_option(name_s, "globalRotation") :
|
|
gr_s = config[name_s.strip()]["globalRotation"]
|
|
gr_i = int(gr_s)
|
|
if gr_i == 90 :
|
|
newSheet[generic.NameObject("/Rotate")] = generic.NumberObject(90)
|
|
elif gr_i == 180 :
|
|
newSheet[generic.NameObject("/Rotate")] = generic.NumberObject(180)
|
|
elif gr_i == 270 :
|
|
newSheet[generic.NameObject("/Rotate")] = generic.NumberObject(270)
|
|
else :
|
|
transform_s = self.calcMatrix(name_s, rows_i, columns_i)
|
|
ar_data.append([transform_s])
|
|
|
|
|
|
|
|
|
|
if "output_conditions" in config :
|
|
conditions_a = config["output_conditions"].keys()
|
|
for line1 in conditions_a :
|
|
condition_s = config["output_conditions"][line1]
|
|
command_s, filters_s = condition_s.split("=>")
|
|
if (eval(command_s)) :
|
|
transformations = filters_s.split(", ")
|
|
for name_s in transformations :
|
|
if "globalRotation" in config[name_s.strip()] :
|
|
gr_s = config[name_s.strip()]["globalRotation"]
|
|
gr_i = int(gr_s)
|
|
if gr_i == 90 :
|
|
newSheet[generic.NameObject("/Rotate")] = generic.NumberObject(90)
|
|
elif gr_i == 180 :
|
|
newSheet[generic.NameObject("/Rotate")] = generic.NumberObject(180)
|
|
elif gr_i == 270 :
|
|
newSheet[generic.NameObject("/Rotate")] = generic.NumberObject(270)
|
|
else :
|
|
transform_s = self.calcMatrix(name_s, rows_i, columns_i)
|
|
ar_data.append([transform_s])
|
|
|
|
|
|
oString = ""
|
|
|
|
# Ready to create the page
|
|
for r, c in ar_layout : # We are creating the page in row r and column c
|
|
data_x = []
|
|
if ar_pages[0] == [] : # system not yet initialised
|
|
return
|
|
file_number, page_number = ar_pages[a][i].split(":")
|
|
file_number = int(file_number)
|
|
page_number = int(page_number)
|
|
if (page_number < 0) :
|
|
i += 1
|
|
continue # blank page
|
|
|
|
|
|
data_x.append("q\n")
|
|
# Create the transformation matrix for the page
|
|
matrix_s = self.transform(r, c, page_number, output_page_number, file_number)
|
|
if matrix_s == False :
|
|
return False
|
|
data_x.append(matrix_s)
|
|
|
|
# Booklets : option "creep"
|
|
if ini.booklet > 0 : # This option makes sense only for booklets
|
|
creep_f1 = app.readmmEntry(app.arw["creep"])
|
|
if creep_f1 > 0 :
|
|
# calculate creep for each booklet (number of folios may vary)
|
|
for key, value in ar_cahiers.items() :
|
|
creep_f = creep_f1 / value
|
|
|
|
# reset to 0 for each booklet
|
|
# Starting page of each booklet
|
|
start_pages = [0]
|
|
mem = 0
|
|
for key, value in ar_cahiers.items() :
|
|
page_value = value * 2 # ar_cahiers gives the number of folios, not pages
|
|
start_pages.append(page_value + mem)
|
|
mem += page_value
|
|
|
|
# Calculate creep for a given page
|
|
|
|
for start_page in start_pages :
|
|
if output_page_number <= start_page :
|
|
page_in_booklet = output_page_number - start_page # page number inside a given booklet
|
|
Htrans = creep_f * (int(page_in_booklet/2) - (page_in_booklet % 2)) # increment every two pages.
|
|
# It is a bit difficult to explain...
|
|
# We must get this :
|
|
# page -5 => -3 External page
|
|
# page -4 => -2
|
|
# page -3 => -2
|
|
# page -2 => -1
|
|
# page -1 => -1
|
|
# page 0 => 0 Internal page
|
|
|
|
if c == 0 :
|
|
data_x.append(self.calcMatrix2(Htrans , 0)) # shift left
|
|
else :
|
|
data_x.append(self.calcMatrix2((Htrans * -1), 0)) # shift right
|
|
break
|
|
|
|
|
|
# scale the page to fit the output sheet, if required
|
|
if"autoscale" in config["options"]:
|
|
if ini.readBoolean(config["options"]["autoscale"]) == True :
|
|
(scaleFactor_f, Htranslate, Vtranslate) = self.CalcAutoScale(file_number, page_number)
|
|
matrix1_s = self.calcMatrix2(Htranslate, Vtranslate, Scale = scaleFactor_f)
|
|
data_x.append(matrix1_s)
|
|
|
|
file_name = inputFiles_a[file_number]
|
|
newPage = inputFile_a[file_name].getPage(page_number)
|
|
|
|
data_x.append(newPage)
|
|
|
|
# Add page number if required
|
|
# Choose font
|
|
try:
|
|
temp1 = newPage['/Resources'].getObject()
|
|
if isinstance(temp1, dict) :
|
|
temp2 = temp1['/Font'].getObject()
|
|
temp3 = temp2.keys()
|
|
if '/F1' in temp3 :
|
|
font = '/F1'
|
|
else : # we get the first font in the list
|
|
for k in temp3 :
|
|
font = k
|
|
break
|
|
except :
|
|
pass # Not critical. Default font will be used, but it does not show in the preview, that's why it is better to have an available font number.
|
|
|
|
|
|
if app.arw["page_numbers"].get_active() == True :
|
|
font_size = ini.readIntEntry(app.arw["numbers_font_size"], default = 18)
|
|
bottom_margin = ini.readIntEntry(app.arw["numbers_bottom_margin"], default = 20)
|
|
start_from = ini.readIntEntry(app.arw["numbers_start_from"])
|
|
position = urx_i / 2
|
|
if start_from <= page_number + 1 :
|
|
data_x.append(" q BT %s %d Tf 1 0 0 1 %d %d Tm (%d) Tj ET Q\n" % (font, font_size, position, bottom_margin, page_number + 1))
|
|
data_x.append("Q\n")
|
|
if len(ini.delete_rectangle) > 0 :
|
|
(x1,y1,w1,h1) = ini.delete_rectangle
|
|
data_x.append("q n 1 1 1 rg \n") # Couleur RGB ; 1 1 1 = white; 0,0,0 = black
|
|
data_x.append(" n %d %d %d %d re f* Q\n" % (x1,y1,w1,h1)) # rectangle : x, y, width, height
|
|
ar_data.append(data_x)
|
|
|
|
i += 1
|
|
|
|
aa = urx_i
|
|
bb = ury_i
|
|
|
|
|
|
datay = []
|
|
for datax in ar_data :
|
|
datay += datax + ["\n"]
|
|
|
|
## datay += ["q n 10 10 m 10 122 l S \n"]
|
|
## datay += [" n 10 10 m 72 10 l S \n"]
|
|
## datay += ["Q\n"]
|
|
if preview > 0 :
|
|
newSheet.mergePage3(datay) # never use slow mode for preview
|
|
elif ("slowMode" in app.arw
|
|
and app.arw["slowMode"].get_active() == 0) : # normal mode
|
|
newSheet.mergePage3(datay)
|
|
|
|
else : # slow mode (uses mergePage instead of mergePage3)
|
|
|
|
dataz = ""
|
|
pages = []
|
|
end_code = ""
|
|
for data2 in datay :
|
|
if not isinstance(data2, str) :
|
|
|
|
pages.append([data2, dataz])
|
|
dataz = ""
|
|
else :
|
|
dataz += data2
|
|
if not (dataz == "" or len(pages) == 0) : # skip blank pages
|
|
|
|
i = len(pages)
|
|
pages[i - 1].append(dataz)
|
|
|
|
for content in pages :
|
|
if len(content) == 3 :
|
|
end_code == content[2]
|
|
else :
|
|
end_code = ""
|
|
newSheet.mergeModifiedPage(content[0], content[1], end_code)
|
|
|
|
|
|
|
|
|
|
if ( "noCompress" in app.arw
|
|
and app.arw["noCompress"].get_active() == 0) :
|
|
newSheet.compressContentStreams()
|
|
|
|
|
|
if preview == -1 : # if we are creating a real file (not a preview)
|
|
message_s = _("Assembling pages: %s ") % (ar_pages[a])
|
|
app.print2( message_s , 1)
|
|
app.status.set_text("page " + str(statusValue_i) + " / " + str(statusTotal_i))
|
|
statusValue_i += 1
|
|
output_page_number += 1
|
|
while Gtk.events_pending():
|
|
Gtk.main_iteration()
|
|
|
|
time_e=time.time()
|
|
|
|
#app.print2(_("Total length : %s ") % (time_e - time_s), 1)
|
|
|
|
output.write(outputStream)
|
|
if preview == -1 : # if we are creating a real file (not a preview)
|
|
outputStream.close() # We must close the file, otherwise it is not possible to see it in the Reader
|
|
# TODO : after closing the reader, preview should be automatically updated.
|
|
|
|
del output
|
|
|
|
if debug_b == 1 :
|
|
logfile_f.close()
|
|
|
|
# TODO
|
|
"""
|
|
if preview == -1 : # if we are creating a real file (not a preview)
|
|
if app.settings.get_active() == 1 :
|
|
app.saveProjectAs("",inputFile + ".ini")
|
|
"""
|
|
return True
|
|
|
|
|
|
|
|
|
|
def printTree(curPage,out) :
|
|
|
|
#curPage = source.getPage(page)
|
|
keys_a = list(curPage.keys())
|
|
#temp1 = curPage["/Parent"].getObject()
|
|
for j in keys_a :
|
|
#if j in ["/Parent", "/Rotate", "/MediaBox", "/Type", "/Annots", "/Contents"] :
|
|
if j in ["/Parent"] :
|
|
continue
|
|
temp1 = curPage[j].getObject()
|
|
print("======> page " + str(page) + " " + j, file=out)
|
|
print(temp1, file=out)
|
|
if isinstance(temp1, dict) :
|
|
for k in temp1 :
|
|
temp2 = temp1[k].getObject()
|
|
print(str(k) + " : ", end=' ', file=out)
|
|
print(temp2, file=out)
|
|
if isinstance(temp2, dict) :
|
|
for l in temp2 :
|
|
temp3 = temp2[l].getObject()
|
|
print(str(l) + " : ", end=' ', file=out)
|
|
print(temp3, file=out)
|
|
if isinstance(temp3, dict) :
|
|
for m in temp3 :
|
|
temp4 = temp3[m].getObject()
|
|
print(str(m) + " : ", end=' ', file=out)
|
|
print(temp4, file=out)
|
|
if isinstance(temp4, dict) :
|
|
for n in temp4 :
|
|
temp5 = temp4[n].getObject()
|
|
print(str(n) + " : ", end=' ', file=out)
|
|
print(temp5, file=out)
|
|
|
|
#out.close()
|
|
|
|
|
|
def parseOptions() :
|
|
global arg_a
|
|
parser = OptionParser()
|
|
parser.add_option("-f", "--file", dest="filename",
|
|
help="write report to FILE", metavar="FILE")
|
|
parser.add_option("-r", "--rows", dest="rows_i",
|
|
help="write report to FILE", metavar="FILE")
|
|
parser.add_option("-c", "--columns", dest="columns_i",
|
|
help="write report to FILE", metavar="FILE")
|
|
parser.add_option("-n", "--numfolio", dest="numfolio",
|
|
help="write report to FILE", metavar="FILE")
|
|
parser.add_option("-b", "--booklet", dest="booklet",
|
|
help="write report to FILE", metavar="FILE")
|
|
parser.add_option("-a", "--appendPages", dest="appendPages",
|
|
help="write report to FILE", metavar="FILE")
|
|
parser.add_option("-p", "--prependPages", dest="prependPages",
|
|
help="write report to FILE", metavar="FILE")
|
|
parser.add_option("-s", "--selection", dest="selection",
|
|
help="write report to FILE", metavar="FILE")
|
|
parser.add_option("-o", "--output", dest="outputFile",
|
|
help="write report to FILE", metavar="FILE")
|
|
parser.add_option("-i", "--iniFile", dest="iniFile",
|
|
help="write report to FILE", metavar="FILE")
|
|
parser.add_option("-e", "--referencePage", dest="referencePage",
|
|
help="write report to FILE", metavar="FILE")
|
|
|
|
parser.add_option("-q", "--quiet",
|
|
action="store_false", dest="verbose", default=True,
|
|
help="don't print status messages to stdout")
|
|
|
|
(option_v, arg_a) = parser.parse_args()
|
|
|
|
|
|
## if None != option_v.iniFile :
|
|
## ini_s = option_v.iniFile
|
|
## parseIniFile(ini_s)
|
|
|
|
|
|
def extractBase() :
|
|
"""
|
|
extract absolute path to script
|
|
@return prog_s : absolute program path
|
|
@return pwd_s : current working dir
|
|
@return base_s : dirname of prog_s
|
|
"""
|
|
|
|
# read current dir
|
|
prog_s = sys.argv[0]
|
|
pwd_s = os.path.abspath(".")
|
|
name_s = os.path.basename(prog_s)
|
|
_sep_s = '\\'
|
|
|
|
# extract program path
|
|
# if path starts with \ or x:\ absolute path
|
|
if _sep_s == prog_s[0] or \
|
|
(2 < len(prog_s) and \
|
|
":" == prog_s[1] and
|
|
_sep_s == prog_s[2]) :
|
|
base_s = os.path.dirname(prog_s)
|
|
# if it starts with ./ , relative path
|
|
elif 1 < len(prog_s) and \
|
|
"." == prog_s[0] and \
|
|
_sep_s == prog_s[1] :
|
|
path_s = os.path.abspath(prog_s)
|
|
base_s = os.path.dirname(path_s)
|
|
# if it is in the active directory
|
|
elif os.path.exists(os.path.join(pwd_s, prog_s)) or \
|
|
os.path.exists(os.path.join(pwd_s, prog_s) + ".exe"): # Necessary if the user starts the program without the extension (maggy, without .exe)
|
|
path_s = os.path.join(pwd_s, prog_s)
|
|
base_s = os.path.dirname(path_s)
|
|
else :
|
|
tab_a = os.environ["PATH"].split(":")
|
|
limit = len(tab_a)
|
|
found = False
|
|
for scan in range(limit) :
|
|
path_s = os.path.join(tab_a[scan], prog_s)
|
|
if os.path.exists(path_s) :
|
|
base_s = os.path.dirname(path_s)
|
|
found = True
|
|
break
|
|
if not found :
|
|
raise ScriptRt("path to program is undefined")
|
|
|
|
# application base import
|
|
return(name_s, pwd_s, base_s)
|
|
|
|
|
|
def sfp(path) :
|
|
# sfp = set full path
|
|
return os.path.join(prog_path_u, path)
|
|
|
|
def sfp2(file1) :
|
|
# sfp2 = set full path, used for temporary directory
|
|
try:
|
|
return os.path.join(share_path_u, file1)
|
|
except :
|
|
time.sleep(1) # Sometimes there was a sort of conflict with another thread
|
|
return os.path.join(share_path_u, file1)
|
|
|
|
def sfp3(file1) :
|
|
# sfp3 = set full path, used for config directory
|
|
try:
|
|
return os.path.join(cfg_path_u, file1)
|
|
except :
|
|
time.sleep(1) # Sometimes there was a sort of conflict with another thread
|
|
|
|
def close_applicationx(self, widget, event=None, mydata=None):
|
|
|
|
if Gtk.main_level():
|
|
app.arw["window1"].destroy()
|
|
Gtk.main_quit()
|
|
else:
|
|
sys.exit(0)
|
|
|
|
return False
|
|
|
|
###########################################################################
|
|
# MAIN ####################################################################
|
|
###########################################################################
|
|
|
|
def main() :
|
|
|
|
global PdfShuffler, PDF_Doc
|
|
|
|
from pdfbooklet.pdfshuffler_g3 import PdfShuffler, PDF_Doc
|
|
|
|
global isExcept
|
|
global startup_b
|
|
global preview_b
|
|
global project_b
|
|
global openedProject_u
|
|
global areaAllocationW_i
|
|
global areaAllocationH_i
|
|
|
|
global base_a
|
|
global prog_path_u
|
|
global temp_path_u
|
|
global cfg_path_u
|
|
global share_path_u
|
|
|
|
global rows_i
|
|
global columns_i
|
|
global step_i
|
|
global outputScale
|
|
global outputStream_mem
|
|
global mem
|
|
|
|
isExcept = False
|
|
startup_b = True
|
|
preview_b = True
|
|
project_b = False
|
|
openedProject_u = ""
|
|
areaAllocationW_i = 1
|
|
areaAllocationH_i = 1
|
|
rows_i = 1
|
|
columns_i = 2
|
|
step_i = 1
|
|
outputScale = 1
|
|
outputStream_mem = ""
|
|
mem = {}
|
|
mem["update"] = time.time()
|
|
|
|
base_a = extractBase()
|
|
prog_path_u = unicode2(base_a[2])
|
|
|
|
|
|
errorLog = sys.argv[0] + ".log"
|
|
argv_a = sys.argv
|
|
sys.argv = [sys.argv[0]] # remove any parameter because they are not supported by PdfShuffler
|
|
if os.path.exists(sfp(errorLog)) :
|
|
try : # Sometimes the file may be locked
|
|
os.remove(sfp(errorLog))
|
|
except :
|
|
pass
|
|
|
|
# set directories for Linux and Windows
|
|
|
|
if 'linux' in sys.platform :
|
|
cfg_path_u = os.path.join(os.environ['HOME'], ".config", "pdfbooklet")
|
|
if os.path.isdir(cfg_path_u) == False :
|
|
os.mkdir(cfg_path_u)
|
|
|
|
share_path_u = "/usr/share/pdfbooklet"
|
|
if os.path.isdir(share_path_u) == False :
|
|
os.mkdir(share_path_u)
|
|
|
|
else:
|
|
#TODO : this is not the recommended situation.
|
|
cfg_path_u = prog_path_u
|
|
share_path_u = prog_path_u
|
|
|
|
|
|
|
|
#parseOptions()
|
|
|
|
try:
|
|
|
|
|
|
global render, app, parser, ini
|
|
global inputFiles_a
|
|
|
|
render = pdfRender()
|
|
parser = myConfigParser()
|
|
ini = TxtOnly(render)
|
|
|
|
|
|
# command line processing
|
|
if len(argv_a) > 2 and argv_a[2].strip() != "" :
|
|
arg1 = argv_a[1]
|
|
(name,ext) = os.path.splitext(arg1) # determine file type from the extension
|
|
# TODO : determine from mimetype for Linux
|
|
if ext == ".ini" :
|
|
startup_b = False
|
|
app = dummy()
|
|
ini.openProject2(arg1)
|
|
ini.output_page_size(1)
|
|
app.pagesTr = copy.deepcopy(config)
|
|
if render.parsePageSelection("1-20", 0) :
|
|
|
|
## self.readConditions()
|
|
ar_pages, ar_layout, ar_cahiers = render.createPageLayout()
|
|
if ar_pages != None :
|
|
render.createNewPdf(ar_pages, ar_layout, ar_cahiers, "test.pdf", -1)
|
|
|
|
return True
|
|
|
|
settings = Gtk.Settings.get_default()
|
|
settings.props.gtk_button_images = True
|
|
app = gtkGui(render)
|
|
|
|
app.guiPresetsShow("booklet")
|
|
app.resetTransformations(0)
|
|
app.resetTransformations2(0)
|
|
app.guiPresets(0)
|
|
|
|
|
|
|
|
if os.path.isfile(sfp3("pdfbooklet.cfg")) == False :
|
|
f1 = open(sfp3("pdfbooklet.cfg"), "w")
|
|
f1.close()
|
|
ini.parseIniFile(sfp3("pdfbooklet.cfg"))
|
|
|
|
|
|
if len(argv_a) > 1 : # a filename has been added in the command line
|
|
if len(argv_a[1]) > 0 : # this is not an empty string
|
|
arg1 = argv_a[1]
|
|
(name,ext) = os.path.splitext(arg1) # determine file type from the extension
|
|
# TODO : determine from mimetype for Linux
|
|
if ext == ".ini" : # If it is a project
|
|
startup_b = False
|
|
if not ini.openProject2(arg1) : # if file not found
|
|
print ("Unknown file on the command line : " + arg1)
|
|
else :
|
|
app.arw["previewEntry"].set_text("1")
|
|
while Gtk.events_pending():
|
|
Gtk.main_iteration()
|
|
app.previewUpdate()
|
|
|
|
if len(argv_a) > 2 :
|
|
if argv_a[2].strip() == "-r" :
|
|
app.go("") # TODO : should be go("", 0) but this creates errors
|
|
app.close_application("")
|
|
|
|
elif ext == ".pdf" :
|
|
inputFiles_a = {}
|
|
inputFiles_a[1] = argv_a[1]
|
|
ini.loadPdfFiles()
|
|
app.selection_s = ""
|
|
app.arw["previewEntry"].set_text("1")
|
|
app.previewUpdate()
|
|
else :
|
|
print("Unknown file type in the command line")
|
|
|
|
|
|
startup_b = False
|
|
|
|
## Gdk.threads_init()
|
|
## Gdk.threads_enter()
|
|
Gtk.main()
|
|
## Gdk.threads_leave()
|
|
|
|
## os._exit(0) # required because pdf-shuffler does not close correctly
|
|
|
|
|
|
|
|
except :
|
|
isExcept = True
|
|
excMsg_s = "unexpected exception"
|
|
(excType, excValue, excTb) = sys.exc_info()
|
|
tb_a = traceback.format_exception(excType, excValue, excTb)
|
|
for a in tb_a :
|
|
print(a)
|
|
|
|
# handle eventual exception
|
|
if isExcept :
|
|
|
|
## if app.shuffler != None :
|
|
## app.shuffler.window.destroy()
|
|
if Gtk.main_level():
|
|
## Gtk.gdk.threads_enter()
|
|
Gtk.main_quit()
|
|
## Gtk.gdk.threads_leave()
|
|
#os._exit(0)
|
|
sys.exit(1)
|
|
else:
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__' :
|
|
main()
|