#!/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 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 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 if vflip != 0 and vflip != False : cos2_l = cos2_l * (-1) Vtranslate += ury_i * myrows_i # Horizontal flip : -1 0 0 1 0 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()