I updated the PDF Booklet project and removed Python 2 dependencies so that it will run under Ubuntu 22.04.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5076 lines
183 KiB

2 years ago
  1. #!/usr/bin/python3
  2. # -*- coding: utf8 -*-
  3. from __future__ import print_function
  4. from __future__ import unicode_literals
  5. # version 3.1.2 : bug fix : autoscale didn't work for a mix of portrait / landscape pages.
  6. # version 3.1.1 : workaround for a bug appeared in Ubuntu 19 (see the OnDraw function)
  7. # version 3.1
  8. # fix a serious bug which prevented auto-scale to work.
  9. # version 3.0.6, 09/05/2018
  10. # Bug fixes
  11. # Better Linux support
  12. # No longer uses a temporary file, preview data now in memory
  13. # version 3.0.5, 30 / 07 / 2017
  14. # German Translation added
  15. # version 3.0.4
  16. # New feature : add page numbers (still experimental)
  17. # Gui : dotted line in the middle of a booklet - Still to be improved
  18. # No longer uses the tempfiles/preview.pdf temporary file.
  19. # this is now handled in Memory. Bugs to fix on that feature.
  20. # To understand the reason for which we have used new_from_bytes and not new_from_data, see here :
  21. # https://stackoverflow.com/questions/45838863/gio-memoryinputstream-does-not-free-memory-when-closed
  22. # Fix bug for display of red rectangles when the output page is rotated 90° or 270°
  23. PB_version = "3.1.2"
  24. """
  25. website : pdfbooklet.sourceforge.net
  26. This software is a computer program whose purpose is to manipulate pdf files.
  27. This software is governed by the CeCILL license under French law and
  28. abiding by the rules of distribution of free software. You can use,
  29. modify and/ or redistribute the software under the terms of the CeCILL
  30. license as circulated by CEA, CNRS and INRIA at the following URL
  31. "http://www.cecill.info".
  32. As a counterpart to the access to the source code and rights to copy,
  33. modify and redistribute granted by the license, users are provided only
  34. with a limited warranty and the software's author, the holder of the
  35. economic rights, and the successive licensors have only limited
  36. liability.
  37. In this respect, the user's attention is drawn to the risks associated
  38. with loading, using, modifying and/or developing or reproducing the
  39. software by the user in light of its specific status of free software,
  40. that may mean that it is complicated to manipulate, and that also
  41. therefore means that it is reserved for developers and experienced
  42. professionals having in-depth computer knowledge. Users are therefore
  43. encouraged to load and test the software's suitability as regards their
  44. requirements in conditions enabling the security of their systems and/or
  45. data to be ensured and, more generally, to use and operate it in the
  46. same conditions as regards security.
  47. The fact that you are presently reading this means that you have had
  48. knowledge of the CeCILL license and that you accept its terms.
  49. ==========================================================================
  50. """
  51. """
  52. TODO : enregistrer un projet dans un répertoire avec caractères unicode
  53. vérifier menuAdd
  54. selection_s : L'usage de cette variable serait à vérifier. Est-ce que cela ne crée pas de la confusion ?
  55. Pourquoi ne pas utiliser directement config["options"]["pageSelection"] qu'elle remplace ? Un peu plus long, mais plus facile à déboguer.
  56. Problème surtout à mettre au point :
  57. Supposons une liste de fichiers ouverts dans lesquels on a fait une sélection.
  58. Si on ouvre le gestionnaire de fichier a ajoute un fichier à la liste, la sélection est remise à zéro.
  59. Ce n'est pas bon, il faudrait seulement ajouter les pages du nouveau fichier.
  60. C'est assez compliqué à gérer si des fichiers ont été supprimés. Une routine de comparaison
  61. avant et après avoir ouvert le gesionnaire devrait faire le travail.
  62. TODO :
  63. Autoscale : Distinguer les options : pour les pages et global ? Pas sûr que ce soit utile.
  64. Quand un répertoire est sélectionné, avertir
  65. quand on ouvre un fichier, et puis ensuite un projet qui a plusieurs fichiers, pdfshuffler n'est pas bien mis à jour
  66. popumenu rotate : les valeurs de la fenêtre transformations ne sont pas mises à jour.
  67. bugs
  68. fichier ini : ouvrir un fichier, ouvrir le fichier ini correspondant. Ne rien changer, fermer,
  69. le fichier ini est mis à jour à un moment quelconque et souvent toutes les transformations sont remises à zéro.
  70. Dans la même manipulation , quand on ouvre, il arrive que les modifications d'une page soient conservées
  71. et pas celle de l'autre page (en cahier)
  72. slow mode : si la première feuille est faite entièrement de pages blanches générées (par exemple 2 pour
  73. pages blanches au début dans une configuraiton à deux pages),
  74. line 4285, in createNewPdf
  75. pages[i - 1].append(dataz)
  76. IndexError: list index out of range
  77. petits défauts
  78. quand on clique sur les boutons de global rotation, double update du preview (problème des boutons radio)
  79. améliorations
  80. Le tooltip pour le nom de fichier pourrait afficher les valeurs réelles que donneront les différents paramètres
  81. """
  82. """
  83. EXPLANATIONS OF THE WORKFLOW
  84. The structure of the program is easy to understand.
  85. Everything runs around the "config" dictionary which defines how the source pdf files
  86. must be placed in the output file.
  87. The content of this dictionary may be viewd at any time by the command "Save project"
  88. which builds an ini file from this dictionary.
  89. The program has two parts :
  90. 1) The PdfRenderer class : It receives the config dictionary,
  91. and from its content builds the output file, applying the necessary
  92. transormations to the source pages. These transformations, in Pdf,
  93. are always handled byt transformation matrices. See Pdf specifications
  94. for details.
  95. 2) The gui, whose only purpose is to build the config dictionary in an easy way.
  96. Workflow :
  97. 1) Normal process is : - the gui creates and updates the config dict.
  98. - When the Go button is pressed, the config dictionary
  99. is sent to PdfRenderer which builds the output file
  100. 2) Preview process : To create the preview, a similar process is used.
  101. - the config dictionary is sent to PdfRenderer, with an
  102. additional parameter which indicates a page number
  103. - PdfRenderer creates a pdf file in memory which contains a single page.
  104. - This page is displayed in the gui by Poppler.
  105. Inside config, the pages are named in two different ways :
  106. - Absolute : 2:25 designates a single page, page 25 of the second file.
  107. - Positional : 2,1 (line, column) designates any page which is placed
  108. on line 2, column 1
  109. What renders things complicated is that pdf counts pages from the bottom
  110. left, starting by 0, which is not user friendly. So the program has to convert
  111. data in a readable format.
  112. Another complication is that Pdf defines the center of rotation at the lower left corner,
  113. which is not user friendly. The rotate function handles this and shifts the image to create
  114. a centered rotation.
  115. Transformations
  116. 1) When the user clicks on the preview, the selectPage function is launched.
  117. a) From the mouse coordinates, it determines the page clicked, and builds a page identifier
  118. which is a list of six values :
  119. - row and column (Pdf format)
  120. - file number and page number
  121. - row and column if the output page is rotated
  122. then it updates the selected pages list.
  123. b) it launchs area_expose to update the display
  124. c) it extracts from config the transformations already defined for this page
  125. and fills in the gtkEntry widgets which contains the transformations
  126. 2) When the user changes a value in these widgets, the transformationApply function is launched.
  127. It reads the values in the gui and updates the config dictionary
  128. Then it launchs the preview function which will update the preview
  129. HOWTO
  130. to add a parameter, three steps are necessary :
  131. 1) add the code which will use the parameter
  132. 2) add a control in Glade
  133. 3) add a line in makeinifile to write the parameter in the project file
  134. 4) add a line in setupGui to setup the gui from the ini file.
  135. """
  136. """
  137. Ubuntu 2016 - dépendences
  138. python3-gi
  139. python3-gi-cairo
  140. gir1.2-gtk-3.0
  141. gir1.2-poppler-0.18
  142. Installation de pyinstaller
  143. sudo pip3 install pyinstaller
  144. le paquet python-dev est aussi nécessaire (mais pip le trouve)
  145. """
  146. import time, math, string, os, sys, re, shutil, site
  147. #print(sys.version)
  148. sys.path.append(os.path.abspath(os.path.dirname(__file__)))
  149. try :
  150. import configparser # Python 3
  151. from configparser import ConfigParser, RawConfigParser
  152. except :
  153. from ConfigParser import ConfigParser, RawConfigParser
  154. import io
  155. from collections import defaultdict, OrderedDict
  156. import subprocess
  157. from subprocess import Popen, PIPE
  158. from ctypes import *
  159. import threading
  160. import tempfile, io
  161. import copy
  162. ##import urllib
  163. ##from urllib.parse import urlparse
  164. ##from urllib.request import urljoin
  165. from optparse import OptionParser
  166. import traceback
  167. import gi
  168. gi.require_version('Gtk', '3.0')
  169. gi.require_version('Poppler', '0.18')
  170. from gi.repository import Gtk
  171. from gi.repository import Gdk
  172. from gi.repository import Poppler
  173. from gi.repository import Pango
  174. from gi.repository import Gio, GLib
  175. from gi.repository import cairo
  176. Gtk.rc_parse("./gtkrc")
  177. from pdfbooklet.PyPDF2_G import PdfFileReader, PdfFileWriter
  178. import pdfbooklet.PyPDF2_G.generic as generic
  179. # from pdfbooklet import *
  180. from pdfbooklet.files_chooser import Chooser
  181. import locale #for multilanguage support
  182. import gettext
  183. import pdfbooklet.elib_intl3 as elib_intl3
  184. elib_intl3.install("pdfbooklet", "share/locale")
  185. debug_b = 0
  186. def join_list(my_list, separator) :
  187. mydata = ""
  188. if isinstance(my_list, list) :
  189. for s in my_list :
  190. mydata += s + separator
  191. elif isinstance(my_list, dict) :
  192. for s in my_list :
  193. try :
  194. item1 = unicode(my_list[s], "utf-8")
  195. except :
  196. item1 = my_list[s]
  197. mydata += item1 + separator
  198. crop = len(separator) * -1
  199. mydata = mydata[0:crop]
  200. return mydata
  201. def get_value(dictionary, key, default = 0) :
  202. if not key in dictionary :
  203. dictionary[key] = default
  204. return default
  205. else :
  206. return dictionary[key]
  207. def unicode2(string, dummy = "") :
  208. if sys.version_info[0] == 2 :
  209. if isinstance(string,unicode) :
  210. return string
  211. try :
  212. return unicode(string,"utf_8")
  213. except :
  214. try :
  215. # print string, " est ecrit en cp1252"
  216. return unicode(string,"cp1252")
  217. except :
  218. return string # Is this the good option ? Return False or an empty string ?
  219. #return "inconnu"
  220. def printExcept() :
  221. a,b,c = sys.exc_info()
  222. for d in traceback.format_exception(a,b,c) :
  223. print(d, end=' ')
  224. def bool_test(value) :
  225. if isinstance(value, str) :
  226. try :
  227. value = int(value)
  228. except :
  229. if value.strip().lower() == "true" :
  230. return True
  231. else :
  232. return False
  233. return bool(value)
  234. def alert(message, type = 0) :
  235. dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING,
  236. Gtk.ButtonsType.CLOSE , message)
  237. dialog.run()
  238. dialog.destroy()
  239. def showwarning(title, message) :
  240. """
  241. GTK_MESSAGE_INFO,
  242. GTK_MESSAGE_WARNING,
  243. GTK_MESSAGE_QUESTION,
  244. GTK_MESSAGE_ERROR,
  245. GTK_MESSAGE_OTHER
  246. """
  247. resetTransform_b = False
  248. dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL , Gtk.MessageType.WARNING,
  249. Gtk.ButtonsType.CLOSE , title)
  250. dialog.format_secondary_text(message)
  251. if "transformWindow" in app.arw :
  252. app.arw["transformWindow"].set_keep_above(False)
  253. resetTransform_b = True
  254. dialog.set_keep_above(True)
  255. dialog.run()
  256. dialog.destroy()
  257. if resetTransform_b == True :
  258. app.arw["transformWindow"].set_keep_above(True)
  259. def askyesno(title, string) :
  260. dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL , Gtk.MessageType.QUESTION,
  261. Gtk.ButtonsType.NONE, title)
  262. dialog.add_button(Gtk.STOCK_YES, True)
  263. dialog.add_button(Gtk.STOCK_NO, False)
  264. dialog.format_secondary_text(string)
  265. dialog.set_keep_above(True)
  266. rep = dialog.run()
  267. dialog.destroy()
  268. return rep
  269. def ask_text(parent, message, default=''):
  270. """
  271. Display a dialog with a text entry.
  272. Returns the text, or None if canceled.
  273. """
  274. d = Gtk.MessageDialog(parent,
  275. Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
  276. Gtk.MessageType.QUESTION,
  277. Gtk.ButtonsType.OK_CANCEL,
  278. message)
  279. entry = Gtk.Entry()
  280. entry.set_text(default)
  281. entry.show()
  282. d.vbox.pack_end(entry, True, True, 0)
  283. entry.connect('activate', lambda _: d.response(Gtk.ResponseType.OK))
  284. d.set_default_response(Gtk.ResponseType.OK)
  285. r = d.run()
  286. text = entry.get_text()
  287. if sys.version_info[0] == 2 :
  288. text = text.decode('utf8')
  289. d.destroy()
  290. if r == Gtk.ResponseType.OK:
  291. return text
  292. else:
  293. return None
  294. class myConfigParser() :
  295. def __init__(self) :
  296. pass
  297. def read(self,
  298. iniFile_s,
  299. encoding = "utf8",
  300. comments = False) :
  301. # ----
  302. # read ini file and creates a dictionary
  303. # @param iniFile_s : ini file path
  304. # @param comments : if True, comments are included in the config. Otherwise they are skipped
  305. # @return : True if successful, False otherwise
  306. myconfig = OrderedDict()
  307. if not os.path.isfile(iniFile_s) :
  308. return False
  309. try : # if pdfbooklet.cfg is invalid, don't block the program
  310. if sys.version_info[0] == 3 :
  311. fileIni = open(iniFile_s, "r", encoding = "utf8")
  312. else :
  313. fileIni = open(iniFile_s, "r")
  314. # If BOM present, skip the first three bytes
  315. isBOM_s = fileIni.read(3)
  316. if isBOM_s == chr(239) + chr(187) + chr(191) : # There is a BOM, skips it
  317. pass
  318. else :
  319. fileIni.seek(0) # No BOM, come back to beginning of file
  320. except :
  321. myconfig = OrderedDict()
  322. myconfig["mru"] = OrderedDict()
  323. myconfig["mru2"] = OrderedDict()
  324. myconfig["options"] = OrderedDict()
  325. return myconfig
  326. section_s = ""
  327. while True :
  328. record_s = fileIni.readline()
  329. if record_s == "" : # end of file
  330. break
  331. # format line : strip and replace possible \ by /
  332. record_s = record_s.strip()
  333. if sys.version_info[0] == 2 :
  334. record_s = record_s.decode("utf8")
  335. record_s = record_s.replace("\\", "/") # TODO : or better : formatPath()
  336. # If the line is a section
  337. if record_s[0:1] == "[" and record_s[-1:] == "]" : # section
  338. section_s = record_s[1:-1]
  339. myconfig[section_s] = OrderedDict()
  340. else :
  341. # Skip useless lines
  342. if section_s == "" : # comment in the beginning of the file
  343. continue
  344. if len(record_s) == 0 : # empty line
  345. continue
  346. if record_s[0:1] == "#" : # comment
  347. comment_b = True
  348. else :
  349. comment_b = False
  350. if comments == False : # Skip comments
  351. if comment_b == True :
  352. continue
  353. # otherwise, store data in section
  354. # TODO : comments
  355. record_data = record_s.split("=")
  356. if len(record_data) > 1 :
  357. key = record_data[0].strip()
  358. linedata = record_data[1].strip()
  359. if linedata == "False" :
  360. linedata = False
  361. if linedata == "True" :
  362. linedata = True
  363. myconfig[section_s][key] = linedata
  364. return myconfig
  365. def write(self, myconfig, filename) :
  366. if sys.version_info[0] == 3 :
  367. iniFile = open(filename, "w", encoding = "utf8")
  368. else :
  369. iniFile = open(filename, "w")
  370. for a in myconfig :
  371. iniFile.write("[" + a + "]\n")
  372. for b in myconfig[a] :
  373. value = myconfig[a][b]
  374. if value == True :
  375. value = '1'
  376. elif value == False :
  377. value = '0'
  378. data1 = (b + " = " + value + "\n").encode("utf8") # En python 3 cette ligne convertit en bytes !!!
  379. data1 = (b + " = " + value + "\n")
  380. iniFile.write(data1)
  381. iniFile.write("\n")
  382. iniFile.close()
  383. return True
  384. class TxtOnly :
  385. def __init__(self,
  386. render,
  387. pdfList = None,
  388. pageSelection = None):
  389. global config, rows_i, columns_i, step_i, sections, output, input1, adobe_l, inputFiles_a, inputFile_a
  390. global numfolio, prependPages, appendPages, ref_page, selection, PSSelection
  391. global numPages, pagesSel, llx_i, lly_i, urx_i, ury_i, mediabox_l
  392. global ouputFile, optionsDict, selectedIndex_a, selected_page, deletedIndex_a, app
  393. global arw
  394. ## elib_intl.install("pdfbooklet", "share/locale")
  395. if None != pdfList :
  396. inputFiles_a = pdfList
  397. self.loadPdfFiles()
  398. else :
  399. inputFiles_a = {}
  400. inputFile_a = {}
  401. self.permissions_i = -1 # all permissions
  402. self.password_s = ""
  403. rows_i = 1
  404. columns_i = 2
  405. urx_i = 200
  406. ury_i = 200
  407. optionsDict = {}
  408. adobe_l = 0.3527
  409. self.radioSize = 1
  410. self.radioDisp = 1
  411. self.repeat = 0
  412. self.booklet = 1
  413. self.righttoleft = 0
  414. self.delete_rectangle = []
  415. def openProject2(self, filename_u) :
  416. # Called by OpenProject and OpenMru (in case the selected item was a project)
  417. global config, openedProject_u, preview_b, project_b
  418. if os.path.isfile(filename_u):
  419. openedProject_u = filename_u
  420. ## self.arw["window1"].set_title(u"Pdf-Booklet [ " + PB_version + " ] - " + filename_u)
  421. preview_b = False
  422. project_b = True
  423. self.parseIniFile(filename_u)
  424. preview_b = True
  425. project_b = False
  426. return True
  427. def readNumEntry(self, entry, widget_s = "") :
  428. if isinstance(entry, int) :
  429. return float(entry)
  430. elif isinstance(entry, str) :
  431. value = entry
  432. else :
  433. value = entry.get_text()
  434. value = value.replace(",", ".")
  435. if value == "" : value = 0
  436. try :
  437. value = float(value)
  438. except :
  439. showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
  440. return None
  441. return value
  442. def readmmEntry(self, entry, widget_s = "", default = 0) :
  443. global adobe_l
  444. value = ""
  445. if isinstance(entry, str) :
  446. value = entry
  447. else :
  448. value = entry.get_text()
  449. value = value.replace(",", ".")
  450. if (value == "") :
  451. value = default
  452. else :
  453. try :
  454. value = float(value) / adobe_l
  455. except :
  456. showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
  457. return None
  458. return value
  459. def readPercentEntry(self, entry, widget_s = "") :
  460. value = ""
  461. if sys.version_info[0] == 2 and isinstance(entry, unicode) :
  462. value = entry
  463. elif isinstance(entry, str) :
  464. value = entry
  465. else :
  466. value = entry.get_text()
  467. value = value.replace(",", ".")
  468. if (value == "") :
  469. value = 100
  470. else :
  471. value.replace("%", "")
  472. try :
  473. value = float(value) / 100
  474. if value < 0 :
  475. showwarning(_("Invalid data"), _("Invalid data for %s - must be > 0. Aborting \n") % widget_s)
  476. return None
  477. except :
  478. showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
  479. return None
  480. return value
  481. def readIntEntry(self, entry, widget_s = "", type_i = 0, default = 0) :
  482. # type = 0 : accepts all values >= 0
  483. # type = 1 : accepts all values > 0
  484. # type = -1 : accepts any integer, positive or negative
  485. # type = 2 : optional. Don't warn if missing, but warn if invalid (not integer)
  486. value = ""
  487. if isinstance(entry, str) :
  488. value = entry
  489. else :
  490. value = entry.get_text()
  491. value = value.replace(",", ".")
  492. try :
  493. value = int(value)
  494. if type_i == 0 :
  495. if value < 0 :
  496. showwarning(_("Invalid data"), _("Invalid data for %s - must be >= 0. Aborting \n") % widget_s)
  497. return None
  498. elif type_i == 1 :
  499. if value < 1 :
  500. showwarning(_("Invalid data"), _("Invalid data for %s - must be > 0. Aborting \n") % widget_s)
  501. return None
  502. except :
  503. if value == "" :
  504. if type_i == 2 :
  505. pass
  506. else :
  507. showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
  508. return None
  509. else :
  510. showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
  511. return None
  512. if value == "" :
  513. return default
  514. elif value == 0 and default > 0 :
  515. return default
  516. return value
  517. def readBoolean(self, entry) :
  518. value = ""
  519. if isinstance(entry, str) :
  520. if entry.strip().lower() == "true" :
  521. value = True
  522. else :
  523. value = False
  524. elif isinstance(entry, bool) :
  525. return entry
  526. else :
  527. value = entry.get_text()
  528. try :
  529. if int(value) < 1 :
  530. return False
  531. else :
  532. return True
  533. except :
  534. showwarning(_("Invalid data"), _("Invalid data for %s - must be 0 or 1. Aborting \n") % widget_s)
  535. def parseIniFile(self, inifile = "") :
  536. global config, rows_i, columns_i, step_i, cells_i, input1, adobe_l
  537. global numfolio, prependPages, appendPages, ref_page, selection
  538. global numPages, pagesSel, llx_i, lly_i, urx_i, ury_i, inputFile, inputFiles_a
  539. global startup_b
  540. config = parser.read(inifile)
  541. # migration to 2.4 format : copy xxxx1 values to xxxx and delete xxxx1
  542. for section in config :
  543. for option in config[section] :
  544. if option in["htranslate1", "vtranslate1", "scale1", "rotate1", "xscale1", "yscale1"] : # Clean no longer used data
  545. if option[:-1] in config[section] :
  546. config[section][option[:-1]] = config[section][option]
  547. del config[section][option]
  548. # store in dictionary
  549. self.pagesTr = config
  550. if not "options" in config :
  551. config["options"] = OrderedDict()
  552. for section in config :
  553. if not section in self.pagesTr :
  554. self.pagesTr[section] = OrderedDict()
  555. # inputs
  556. if startup_b == 0 :
  557. if "options" in config:
  558. if "inputs" in config["options"] :
  559. temp1 = config["options"]["inputs"]
  560. inputFiles_a = {}
  561. for filename in temp1.split("|") :
  562. if os.path.isfile(filename) :
  563. pdfFile = PdfFileReader(open(filename, "rb"))
  564. numpages = pdfFile.getNumPages()
  565. path, shortFileName = os.path.split(filename)
  566. i = len(inputFiles_a)
  567. inputFiles_a[i + 1] = filename
  568. self.loadPdfFiles()
  569. if "pageselection" in config["options"] :
  570. self.selection_s = config["options"]["pageselection"]
  571. # variables
  572. if "options" in config:
  573. if "booklet" in config["options"] :
  574. self.booklet = int(config["options"]["booklet"])
  575. # multi-line entries
  576. if "options" in config:
  577. if "userLayout" in config["options"] :
  578. layout_s = config["options"]["userLayout"]
  579. config["options"]["userLayout"] = layout_s.replace("/", "\n")
  580. (a,b,c,d) = self.parse_user_layout(layout_s)
  581. self.imposition = a
  582. def setOption(self, option, default = "") :
  583. if option in config["options"] :
  584. result = config["options"][option]
  585. else :
  586. result = default
  587. if isinstance(default, int) :
  588. try :
  589. return int(result)
  590. except :
  591. return default
  592. def parse_user_layout(self, layout_s) :
  593. if layout_s.strip() == "" :
  594. return ([], 0 , 0 , [])
  595. layout_s = layout_s.replace("/", "\n")
  596. lines = layout_s.split("\n")
  597. imposition = []
  598. lines2 = []
  599. for line in lines :
  600. if line.strip() == "" : # correct errors : ignore blank lines
  601. continue
  602. if line[0:1] == "#" : # ignore comments
  603. continue
  604. if line[0:4] == "====" : # New sheet
  605. imposition.append(lines2)
  606. lines2 = []
  607. else :
  608. lines2.append(line)
  609. if len(lines2) > 0 :
  610. imposition.append(lines2)
  611. numrows = len(lines2)
  612. cols = lines2[0].split(",")
  613. numcols = 0
  614. for a in cols :
  615. if a.strip() != "" :
  616. numcols += 1
  617. imposition2 = []
  618. for lines2 in imposition :
  619. pages = []
  620. for line in lines2 :
  621. line = line.split(",")
  622. for a in line :
  623. if a.strip() != "" : # correct errors : ignore trailing comma
  624. pages.append(a.strip())
  625. imposition2.append(pages)
  626. self.imposition = imposition2
  627. return (imposition2, numrows, numcols, pages)
  628. def loadPdfFiles(self) :
  629. global inputFile_a, inputFiles_a, pagesIndex_a, refPageSize_a
  630. i = 1
  631. inputFile_a = {}
  632. inputFile_details = {}
  633. for key in inputFiles_a :
  634. val = inputFiles_a[key]
  635. if os.path.isfile(val) :
  636. inputFile_a[val] = PdfFileReader(open(val, "rb"))
  637. inputFile_details[val] = {}
  638. if inputFile_a[val].getIsEncrypted() :
  639. inputFile_details[val]["encrypt"] = True
  640. if not hasattr(inputFile_a[val], "_decryption_key") : # if not already decrypted
  641. password = get_text(None, _("Please, enter the password for this file"))
  642. if password != None :
  643. password = password.encode("utf8")
  644. inputFile_a[val].decrypt(password) # Encrypted file
  645. if key == 1 : # we get permissions and password from the first file
  646. (a,b,self.permissions_i) = inputFile_a[val].getPermissions()
  647. self.password_s = password
  648. inputFile_details[val]["password"] = password
  649. selectedIndex_a = {}
  650. deletedIndex_a = {}
  651. i += 1
  652. def output_page_size(self, radiosize, ref_file = 1, ref_page = 0, logdata = 1) :
  653. global config, rows_i, columns_i, sections, output, adobe_l, inputFiles_a, inputFile_a
  654. global numPages, pagesSel, llx_i, lly_i, urx_i, ury_i, mediabox_l, outputScale, refPageSize_a
  655. # Ouput page size
  656. if ref_file in inputFiles_a :
  657. fileName = inputFiles_a[ref_file]
  658. fileName = unicode2(fileName)
  659. try :
  660. page0 = inputFile_a[fileName].getPage(ref_page)
  661. except :
  662. print(_("The reference page is invalid. We use the first page"))
  663. page0 = inputFile_a[fileName].getPage(0)
  664. llx_i=page0.mediaBox.getLowerLeft_x()
  665. lly_i=page0.mediaBox.getLowerLeft_y()
  666. urx_i=page0.mediaBox.getUpperRight_x()
  667. ury_i=page0.mediaBox.getUpperRight_y()
  668. urx_i=float(urx_i) - float(llx_i)
  669. ury_i=float(ury_i) - float(lly_i)
  670. refPageSize_a = [urx_i, ury_i]
  671. else :
  672. alert(_("Reference page invalid, there is no file n°" + str(ref_file)))
  673. return False
  674. #££self.print2 (_("Size of source file = %s mm x %s mm ") % (int(urx_i * adobe_l), int(ury_i * adobe_l)), 1)
  675. oWidth_i = urx_i * columns_i
  676. oHeight_i = ury_i * rows_i
  677. if radiosize == 1 :
  678. mediabox_l = [oWidth_i, oHeight_i]
  679. elif radiosize == 2 : # size = no change
  680. if oWidth_i < oHeight_i : # set orientation
  681. mediabox_l = [urx_i, ury_i]
  682. else :
  683. mediabox_l = [ury_i, urx_i]
  684. # calculate the scale factor
  685. deltaW = mediabox_l[0] / oWidth_i
  686. deltaH = mediabox_l[1] / oHeight_i
  687. if deltaW < deltaH :
  688. outputScale = deltaW
  689. else :
  690. outputScale = deltaH
  691. elif radiosize == 3 : # user defined
  692. customX = self.readNumEntry(app.arw["outputWidth"], _("Width"))
  693. if customX == None : return False
  694. customY = self.readNumEntry(app.arw["outputHeight"], _("Height"))
  695. if customY == None : return False
  696. mediabox_l = [ customX * (1 / adobe_l), customY * (1 / adobe_l)]
  697. # calculate the scale factor
  698. deltaW = mediabox_l[0] / oWidth_i
  699. deltaH = mediabox_l[1] / oHeight_i
  700. if deltaW < deltaH :
  701. outputScale = deltaW
  702. else :
  703. outputScale = deltaH
  704. outputUrx_i = mediabox_l[0]
  705. outputUry_i = mediabox_l[1]
  706. app.arw["info_fichier_sortie"].set_text(_("%s mm x %s mm ") % (int(outputUrx_i * adobe_l), int(outputUry_i * adobe_l)))
  707. class dummy:
  708. def __init__(self) :
  709. self.pagesTr = {}
  710. self.arw = {}
  711. class gtkGui:
  712. # parameters :
  713. # render is an instance of pdfRenderer
  714. # pdfList is a dictionary of path of pdf files : { 1:"...", 2:"...", ... }
  715. # pageSelection is a list of pages in the form : ["w:x", ... , "y:z"]
  716. def __init__(self,
  717. render,
  718. pdfList = None,
  719. pageSelection = None):
  720. global config, rows_i, columns_i, step_i, sections, output, input1, adobe_l, inputFiles_a, inputFile_a
  721. global numfolio, prependPages, appendPages, ref_page, selection, PSSelection
  722. global numPages, pagesSel, llx_i, lly_i, urx_i, ury_i, mediabox_l
  723. global ouputFile, optionsDict, selectedIndex_a, selected_page, selected_pages_a, selectedeletedIndex_a, app
  724. elib_intl3.install("pdfbooklet", "share/locale")
  725. if None != pdfList :
  726. inputFiles_a = pdfList
  727. ini.loadPdfFiles()
  728. else :
  729. inputFiles_a = {}
  730. inputFile_a = {}
  731. self.permissions_i = -1 # all permissions
  732. self.password_s = ""
  733. rows_i = 1
  734. columns_i = 2
  735. step_i = 1
  736. urx_i = 200
  737. ury_i = 200
  738. optionsDict = {}
  739. adobe_l = 0.3527
  740. areaAllocationH_i = 400
  741. areaAllocationW_i = 400
  742. self.freeze_b = False
  743. self.preview_scale = 1
  744. self.dev1 = "" # for development needs
  745. selectedIndex_a = {}
  746. selected_page = None
  747. selected_pages_a = []
  748. deletedIndex_a = {}
  749. app = self
  750. self.render = render
  751. self.ar_pages = []
  752. self.ar_layout = []
  753. self.previewPage = 0
  754. self.clipboard= {}
  755. self.shuffler = None
  756. self.imposition = []
  757. self.initdrag = []
  758. self.enddrag = []
  759. self.backup = []
  760. self.backup_index = 0
  761. self.backup_command = True
  762. self.widgets = Gtk.Builder()
  763. #self.widgets.set_translation_domain('pdfbooklet')
  764. self.widgets.add_from_file(sfp2('data/pdfbooklet3.glade'))
  765. arWidgets = self.widgets.get_objects()
  766. self.arw = {}
  767. for z in arWidgets :
  768. try :
  769. name = Gtk.Buildable.get_name(z)
  770. self.arw[name]= z
  771. z.set_name(name)
  772. except :
  773. pass
  774. #autoconnect signals for self functions
  775. self.widgets.connect_signals(self)
  776. self.arw["drawingarea1"].connect('draw', self.OnDraw)
  777. self.autoscale = self.arw["autoscale"]
  778. self.area = self.arw["drawingarea1"]
  779. self.settings = self.arw["settings"]
  780. self.overwrite = self.arw["overwrite"]
  781. self.noCompress = self.arw["noCompress"]
  782. self.slowmode = self.arw["slowMode"]
  783. self.righttoleft = self.arw["righttoleft"]
  784. self.status = self.arw["status"]
  785. self.window1 = self.arw["window1"]
  786. self.window1.show_all()
  787. self.window1.set_title("Pdf-Booklet [ " + PB_version + " ]")
  788. ## self.window1.connect("destroy", lambda w: Gtk.main_quit())
  789. self.window1.connect("destroy", self.close_application)
  790. """
  791. To change the cursor :
  792. watch_cursor = Gdk.Cursor(Gdk.CursorType.WATCH)
  793. self.window1.get_window().set_cursor(watch_cursor)
  794. ## display = self.window1.get_display()
  795. ## watch_cursor = Gdk.Cursor.new_from_name(display, "default")
  796. watch_cursor = Gdk.Cursor(Gdk.CursorType.WATCH)
  797. watch_cursor = Gdk.Cursor(Gdk.CursorType.CROSS)
  798. self.window1.get_window().set_cursor(watch_cursor)
  799. """
  800. self.mru_items = {}
  801. self.menuAdd()
  802. self.selection_s = ""
  803. # Global transformations
  804. self.Vtranslate1 = self.arw["vtranslate1"]
  805. self.scale1 = self.arw["scale1"]
  806. self.rotation1 = self.arw["rotation1"]
  807. self.thispage = self.arw["thispage"]
  808. self.evenpages = self.arw["evenpages"]
  809. self.oddpages = self.arw["oddpages"]
  810. self.area.show()
  811. self.pagesTr = {}
  812. # ############ Setup drag motion for drawingarea1 ##############
  813. # setup drag
  814. ## targets = Gtk.TargetList.new([])
  815. ## targets.add_text_targets(0)
  816. ##
  817. ## self.area.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, [],
  818. ## Gdk.DragAction.COPY)
  819. ## self.area.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY)
  820. ## self.area.drag_source_set_target_list(targets)
  821. ## self.area.drag_dest_set_target_list(targets)
  822. # self.area is connected to drag_motion in glade
  823. # ##################### Themes #################################
  824. menu_themes = Gtk.Menu()
  825. mem_menu_name = ""
  826. # Get the list of gtkrc-xxx files in "data", extract the name, and add items to menu
  827. themes_dict = {}
  828. themes_dir = os.path.join(prog_path_u, "share/themes")
  829. if os.path.isdir(themes_dir) :
  830. for a in os.listdir(themes_dir) : # TODO ££
  831. rcpath = os.path.join(prog_path_u,a)
  832. themes_dict[a] = rcpath
  833. themes_list = themes_dict.keys()
  834. ## themes_list.sort()
  835. # Extract similar short names to build submenus
  836. mem_short_name = ""
  837. submenus = []
  838. for menu_name in themes_list :
  839. short_name = menu_name.split("-")[0]
  840. if short_name == mem_short_name :
  841. if short_name not in submenus :
  842. submenus.append(short_name)
  843. mem_short_name = short_name
  844. # Build first level menu
  845. sub_dict = {}
  846. to_del = []
  847. keys = themes_dict.keys()
  848. ## keys.sort()
  849. for menu_name in submenus :
  850. if len(menu_name.strip()) == 0 :
  851. continue
  852. sub_dict[menu_name] = Gtk.MenuItem(menu_name)
  853. menu_themes.append(sub_dict[menu_name])
  854. sub_dict[menu_name].show()
  855. submenu = Gtk.Menu()
  856. submenu.show()
  857. for key in keys :
  858. rcpath = themes_dict[key]
  859. short_name = key.split("-")[0]
  860. if short_name == menu_name :
  861. commandes = Gtk.MenuItem(key)
  862. submenu.append(commandes)
  863. commandes.connect("activate", self.change_theme, rcpath, key)
  864. commandes.show()
  865. to_del.append(key)
  866. sub_dict[menu_name].set_submenu(submenu)
  867. # delete used keys and add the remaining to main menu
  868. for key in to_del :
  869. del themes_dict[key]
  870. keys = themes_dict.keys()
  871. ## keys.sort()
  872. for menu_name in keys :
  873. if len(menu_name.strip()) == 0 :
  874. continue
  875. rcpath = themes_dict[menu_name]
  876. commandes = Gtk.MenuItem(menu_name)
  877. menu_themes.append(commandes)
  878. commandes.connect("activate", self.change_theme, rcpath, menu_name)
  879. commandes.show()
  880. self.arw["themes"].set_submenu(menu_themes)
  881. aaa = 1
  882. def change_theme(self, widget, path, theme) :
  883. try:
  884. settings_location = os.path.join(site.getsitepackages()[1], "gnome/etc/gtk-3.0/settings.ini")
  885. except :
  886. settings_location = os.path.join(prog_path_u, "etc/gtk-3.0/settings.ini")
  887. f1 = open(settings_location, "w")
  888. f1.write("[Settings]\n")
  889. f1.write("gtk-theme-name = " + theme)
  890. f1.close()
  891. alert(_("You must restart the program to apply the new theme."))
  892. # this small function returns the type of a widget
  893. def widget_type(self, widget) :
  894. try :
  895. z = widget.class_path()
  896. z2 = z.split(".")[-1]
  897. return z2
  898. except:
  899. return False
  900. def gtk_delete(self, source=None, event=None):
  901. Gtk.main_quit()
  902. def close_application(self, widget, event=None, mydata=None):
  903. """Termination"""
  904. if self.shuffler != None :
  905. self.shuffler.close_application("")
  906. self.shuffler = None
  907. if Gtk.main_level():
  908. self.arw["window1"].destroy()
  909. Gtk.main_quit()
  910. Gdk.threads_leave()
  911. #os._exit(0)
  912. return False
  913. def file_manager(self,widget):
  914. global inputFiles_a
  915. mrudir = self.read_mru2()
  916. if mrudir == "" :
  917. mrudir = prog_path_u
  918. self.chooser = Chooser(inputFiles_a, share_path_u, mrudir)
  919. inputFiles_a = self.chooser.inputFiles_a
  920. if len(inputFiles_a) == 0 :
  921. return
  922. # add file(s) to most recently used
  923. self.mru(inputFiles_a)
  924. self.chooser.chooser.destroy()
  925. self.chooser = None
  926. if self.shuffler:
  927. self.shuffler.model.clear()
  928. self.shuffler.pdfqueue = []
  929. self.shuffler.nfile = 0
  930. for key in inputFiles_a :
  931. self.shuffler.add_pdf_pages(inputFiles_a[key])
  932. # TODO : N'est à faire que si la liste des fichiers a changé
  933. self.shuffler.rendering_thread.pdfqueue = self.shuffler.pdfqueue
  934. ini.loadPdfFiles()
  935. app.selection_s = ""
  936. self.previewUpdate()
  937. def FormatPath (
  938. self,
  939. path,
  940. typePath = 0) :
  941. # Replaces // and \\ by /, but preserves the initial // necessary in urls on a network
  942. if path[0:2] == "//" or path[0:2] == ["\\"] :
  943. prefix_s = "//"
  944. path = path[2:]
  945. else :
  946. prefix_s = ""
  947. if typePath == 1 :
  948. path = path.replace(":", "")
  949. prefix_s = ""
  950. path = path.replace("\\", "/")
  951. path = path.replace("//", "/")
  952. return(prefix_s + path)
  953. def openProject(self, widget, name = "") :
  954. global config, openedProject_u, preview_b, project_b
  955. old_dir = self.read_mru2()
  956. gtk_chooser = Gtk.FileChooserDialog(title=_('Import...'),
  957. action=Gtk.FileChooserAction.OPEN,
  958. buttons=(Gtk.STOCK_CANCEL,
  959. Gtk.ResponseType.CANCEL,
  960. Gtk.STOCK_OPEN,
  961. Gtk.ResponseType.OK))
  962. gtk_chooser.set_current_folder(old_dir)
  963. gtk_chooser.set_select_multiple(False)
  964. filter_all = Gtk.FileFilter()
  965. filter_all.set_name(_('All files'))
  966. filter_all.add_pattern('*')
  967. gtk_chooser.add_filter(filter_all)
  968. filter_ini = Gtk.FileFilter()
  969. filter_ini.set_name(_('INI files'))
  970. filter_ini.add_pattern('*.ini')
  971. gtk_chooser.add_filter(filter_ini)
  972. gtk_chooser.set_filter(filter_ini)
  973. response = gtk_chooser.run()
  974. if response == Gtk.ResponseType.OK:
  975. filename = gtk_chooser.get_filename()
  976. filename_u = unicode2(filename, "utf-8")
  977. self.mru(filename)
  978. ini.openProject2(filename_u)
  979. ini.loadPdfFiles()
  980. self.setupGui()
  981. self.arw["previewEntry"].set_text("1")
  982. self.previewUpdate()
  983. self.write_mru2(filename_u) # write the location of the opened directory in the cfg file
  984. ## elif response == Gtk.RESPONSE_CANCEL:
  985. ## print(_('Closed, no files selected'))
  986. gtk_chooser.destroy()
  987. def openMru(self, widget) :
  988. global config, openedProject_u, preview_b, project_b
  989. global inputFiles_a
  990. widget_name = widget.get_name()
  991. filenames_list_s = self.mru_items[widget_name][1]
  992. # are we opening a project file ?
  993. filename_u = unicode2(filenames_list_s[0], "utf-8")
  994. extension_s = os.path.splitext(filename_u)[1]
  995. if extension_s == ".ini" :
  996. ini.openProject2(filename_u)
  997. self.selection_s = config["options"]["pageSelection"]
  998. ini.loadPdfFiles()
  999. self.setupGui()
  1000. self.arw["previewEntry"].set_text("1")
  1001. self.previewUpdate()
  1002. return
  1003. else :
  1004. ini.parseIniFile(sfp3("pdfbooklet.cfg")) # reset transformations
  1005. self.setupGui(sfp3("pdfbooklet.cfg"))
  1006. inputFiles_a = {}
  1007. for filename_s in filenames_list_s :
  1008. filename_u = unicode2(filename_s, "utf-8")
  1009. extension_s = os.path.splitext(filename_u)[1]
  1010. i = len(inputFiles_a)
  1011. inputFiles_a[i + 1] = filename_u
  1012. ini.loadPdfFiles()
  1013. app.selection_s = ""
  1014. self.previewUpdate()
  1015. if self.shuffler:
  1016. self.shuffler.model.clear()
  1017. self.shuffler.pdfqueue = []
  1018. self.shuffler.nfile = 0
  1019. self.shuffler.npage = 0
  1020. for key in inputFiles_a :
  1021. self.shuffler.add_pdf_pages(inputFiles_a[key])
  1022. # TODO : N'est à faire que si la liste des fichiers a changé
  1023. self.shuffler.rendering_thread.pdfqueue = self.shuffler.pdfqueue
  1024. #for row in self.shuffler.model:
  1025. # row[6] = False
  1026. def saveProject(self, widget) :
  1027. global openedProject_u
  1028. if openedProject_u :
  1029. self.saveProjectAs("", openedProject_u)
  1030. else :
  1031. self.saveProjectAs("")
  1032. def saveProjectAs(self, widget, filename_u = "") :
  1033. global config, openedProject_u
  1034. if filename_u == "" :
  1035. old_dir = self.read_mru2()
  1036. gtk_chooser = Gtk.FileChooserDialog(title=_('Save project...'),
  1037. action=Gtk.FileChooserAction.SAVE,
  1038. buttons=(Gtk.STOCK_CANCEL,
  1039. Gtk.ResponseType.CANCEL,
  1040. Gtk.STOCK_SAVE,
  1041. Gtk.ResponseType.ACCEPT))
  1042. gtk_chooser.set_do_overwrite_confirmation(True)
  1043. gtk_chooser.set_current_folder(old_dir)
  1044. gtk_chooser.set_current_name("untitled document")
  1045. # or chooser.set_filename("untitled document")
  1046. response = gtk_chooser.run()
  1047. if response == Gtk.ResponseType.CANCEL:
  1048. ## print(_('Closed, no files selected'))
  1049. gtk_chooser.destroy()
  1050. return
  1051. elif response == Gtk.ResponseType.ACCEPT:
  1052. filename = gtk_chooser.get_filename()
  1053. filename_u = unicode2(filename, "utf-8")
  1054. if filename_u[-4:] != ".ini" :
  1055. filename_u += ".ini"
  1056. gtk_chooser.destroy()
  1057. self.mru(filename_u)
  1058. openedProject_u = filename_u
  1059. for section in self.pagesTr :
  1060. if not section in config :
  1061. config[section] = self.pagesTr[section]
  1062. # update with last selections in the gui
  1063. # perhaps we could also update first pagesTr
  1064. self.makeIniFile()
  1065. ## for section in out_a :
  1066. ## config[section] = out_a[section]
  1067. #config.write(iniFile)
  1068. self.write_ordered_config(filename_u)
  1069. self.write_mru2(filename_u) # write the location of the opened directory in the cfg file
  1070. def write_ordered_config(self, filename_u) :
  1071. global config
  1072. if sys.version_info[0] == 3 :
  1073. iniFile = open(filename_u, "w", encoding = "utf8")
  1074. else :
  1075. iniFile = open(filename_u, "w")
  1076. # store data in an ordered dictionary
  1077. out_a = OrderedDict()
  1078. sections_list = list(config.keys())
  1079. for section1 in["options", "mru", "mru2", "output"] :
  1080. if section1 in config :
  1081. out_a[section1] = OrderedDict()
  1082. for option in config[section1] :
  1083. value = config[section1][option]
  1084. if value == 'False' :
  1085. value = False
  1086. elif value == 'True' :
  1087. value = True
  1088. out_a[section1][option] = value
  1089. sections_list.remove(section1)
  1090. sections_list.sort()
  1091. for section1 in sections_list :
  1092. out_a[section1] = OrderedDict()
  1093. for option in config[section1] :
  1094. value = config[section1][option]
  1095. if value == 'False' :
  1096. value = False
  1097. elif value == 'True' :
  1098. value = True
  1099. out_a[section1][option] = value
  1100. # write data
  1101. for section2 in out_a :
  1102. iniFile.write("[" + section2 + "]\n")
  1103. for option2 in out_a[section2] :
  1104. iniFile.write(option2 + " = " + str(out_a[section2][option2]) + "\n")
  1105. iniFile.close()
  1106. def mru(self, filenames_a) :
  1107. mrudir = ""
  1108. if isinstance(filenames_a, dict) :
  1109. filenames = join_list(filenames_a, "|")
  1110. if 1 in filenames_a :
  1111. mrudir = os.path.split(filenames_a[1])[0]
  1112. else :
  1113. filenames = filenames_a
  1114. mrudir = os.path.split(filenames_a)[0]
  1115. configtemp = parser.read(sfp3("pdfbooklet.cfg"))
  1116. #### if configtemp.has_section(section) == False :
  1117. #### configtemp.add_section(section)
  1118. #### configtemp.set(section,option,value)
  1119. ##
  1120. ##
  1121. if not "mru" in configtemp :
  1122. configtemp["mru"] = {}
  1123. if not "mru2" in configtemp :
  1124. configtemp["mru2"] = {}
  1125. # cancel if already present
  1126. temp_a = []
  1127. for index in ["mru1", "mru2", "mru3", "mru4"] :
  1128. if index in configtemp["mru"] :
  1129. if filenames == configtemp["mru"][index] :
  1130. return
  1131. # shift mru
  1132. if "mru3" in configtemp["mru"] :
  1133. configtemp["mru"]["mru4"] = configtemp["mru"]["mru3"]
  1134. if "mru2" in configtemp["mru"] :
  1135. configtemp["mru"]["mru3"] = configtemp["mru"]["mru2"]
  1136. if "mru1" in configtemp["mru"] :
  1137. configtemp["mru"]["mru2"] = configtemp["mru"]["mru1"]
  1138. # set the new value
  1139. configtemp["mru"]["mru1"] = filenames
  1140. configtemp["mru2"]["mru1"] = mrudir
  1141. ## f = open(sfp3("pdfbooklet.cfg"),"w")
  1142. ## configtemp.write(f)
  1143. ## f.close()
  1144. parser.write(configtemp, sfp3("pdfbooklet.cfg"))
  1145. configtemp = None
  1146. self.menuAdd()
  1147. def mru_python2(self, filenames_a) :
  1148. mrudir = ""
  1149. if isinstance(filenames_a, dict) : # several files selected
  1150. filenames = join_list(filenames_a, "|")
  1151. if 1 in filenames_a : # At least one file
  1152. mrudir = os.path.split(filenames_a[1])[0]
  1153. else :
  1154. filenames = filenames_a
  1155. mrudir = os.path.split(filenames_a)[0]
  1156. #filenames = filenames.encode('utf-8')
  1157. configtemp = parser.read(sfp3("pdfbooklet.cfg"))
  1158. if configtemp.has_section("mru") == False :
  1159. configtemp.add_section("mru")
  1160. if configtemp.has_section("mru2") == False :
  1161. configtemp.add_section("mru2")
  1162. # cancel if already present
  1163. temp_a = []
  1164. for index in ["mru1", "mru2", "mru3", "mru4"] :
  1165. if configtemp.has_option("mru",index) :
  1166. if filenames == configtemp.get("mru",index) :
  1167. return
  1168. try :
  1169. # shift mru
  1170. if configtemp.has_option("mru","mru3") :
  1171. configtemp.set("mru","mru4",configtemp.get("mru","mru3"))
  1172. if configtemp.has_option("mru","mru2") :
  1173. configtemp.set("mru","mru3",configtemp.get("mru","mru2"))
  1174. if configtemp.has_option("mru","mru1") :
  1175. configtemp.set("mru","mru2",configtemp.get("mru","mru1"))
  1176. # set the new value
  1177. ## filenames_s = filenames.encode("utf-8")
  1178. ## mrudir_s = mrudir.encode("utf-8")
  1179. configtemp.set("mru","mru1",filenames)
  1180. configtemp.set("mru2","mru2",mrudir)
  1181. f = open(sfp3("pdfbooklet.cfg"),"w", encoding = "utf8")
  1182. configtemp.write(f)
  1183. f.close()
  1184. configtemp = None
  1185. self.menuAdd()
  1186. except :
  1187. printExcept()
  1188. alert("problem in mru (line 700)")
  1189. def read_mru2(self) :
  1190. if os.path.isfile(sfp3("pdfbooklet.cfg")) :
  1191. configtemp = parser.read(sfp3("pdfbooklet.cfg"))
  1192. mru_dir = ""
  1193. if "mru2" in configtemp :
  1194. if "mru2" in configtemp["mru2"] :
  1195. mru_dir = configtemp["mru2"]["mru2"]
  1196. configtemp = None
  1197. return mru_dir
  1198. def read_mru2_python2(self) :
  1199. if os.path.isfile(sfp3("pdfbooklet.cfg")) :
  1200. configtemp = parser.read(sfp3("pdfbooklet.cfg"))
  1201. try :
  1202. mru_dir = configtemp.get("mru2","mru2")
  1203. except :
  1204. mru_dir = ""
  1205. configtemp = None
  1206. return mru_dir
  1207. def write_mru2(self, filename_u) :
  1208. if os.path.isfile(sfp3("pdfbooklet.cfg")) :
  1209. configtemp = parser.read(sfp3("pdfbooklet.cfg"))
  1210. if not "mru2" in configtemp :
  1211. configtemp["mru2"] = OrderedDict()
  1212. (path_u, file_u) = os.path.split(filename_u)
  1213. configtemp["mru2"]["mru2"] = path_u
  1214. parser.write(configtemp, sfp3("pdfbooklet.cfg"))
  1215. def menuAdd(self) :
  1216. # Called by function mru, adds an entry to the menu
  1217. configtemp = parser.read(sfp3("pdfbooklet.cfg"))
  1218. if configtemp == False :
  1219. return
  1220. if "mru" in configtemp :
  1221. for item in ["mru1", "mru2", "mru3", "mru4"] :
  1222. if item in configtemp["mru"] :
  1223. filepath_list_s = configtemp["mru"][item]
  1224. filepath_list = filepath_list_s.split("|")
  1225. temp1 = []
  1226. for filepath_s in filepath_list :
  1227. filepath_s = self.FormatPath(filepath_s)
  1228. path_s,filename_s = os.path.split(filepath_s)
  1229. temp1 += [filename_s]
  1230. menu_entry_s = join_list(temp1, ", ")
  1231. if len(menu_entry_s) > 40 :
  1232. menu_entry_s = menu_entry_s[0:40] + "..."
  1233. self.mru_items[item] = [menu_entry_s, filepath_list] # contains real path, used to open files
  1234. self.arw[item].set_label(menu_entry_s) # displayed menu text
  1235. def pdfBooklet_doc(self, widget) :
  1236. userGuide_s = "documentation/" + _("Pdf-Booklet_User's_Guide.pdf")
  1237. if 'linux' in sys.platform :
  1238. subprocess.call(["xdg-open", userGuide_s])
  1239. else:
  1240. os.startfile(sfp(userGuide_s))
  1241. def popup_rotate(self, widget):
  1242. # Called by popup menu (right clic on preview)
  1243. global selected_page, rows_i
  1244. # We get the value from the widget name (the 3 menu options use the same function)
  1245. wname = widget.get_name()
  1246. match = re.match("rotate(\d*)", wname)
  1247. value = match.group(1)
  1248. # Code below is just an adaptation of function "transormationsApply"
  1249. if selected_page == None :
  1250. showwarning(_("No selection"), _("There is no selected page. \nPlease select a page first. "))
  1251. return
  1252. # selected_page 4 and 5 contain the correct page reference, including the global rotation.
  1253. humanReadableRow_i = rows_i - selected_page[4]
  1254. Id = str(str(humanReadableRow_i) + "," + str(selected_page[5] + 1))
  1255. pageId = str(selected_page[2]) + ":" + str(selected_page[3])
  1256. ## # if transformation is for this page only, use page ref instead of position ref
  1257. ## if self.thispage.get_active() == 1 :
  1258. ## Id = pageId
  1259. if (Id in config) == False :
  1260. config[Id] = {}
  1261. config[Id]["htranslate"] = '0'
  1262. config[Id]["vtranslate"] = '0'
  1263. config[Id]["scale"] = '100'
  1264. config[Id]["xscale"] = '100'
  1265. config[Id]["yscale"] = '100'
  1266. config[Id]["vflip"] = False
  1267. config[Id]["hflip"] = False
  1268. config[Id]["rotate"] = str(value)
  1269. self.preview(self.previewPage, 0)
  1270. def __________________INI_FILE() :
  1271. pass
  1272. def saveDefaults(self, dummy) :
  1273. out_a = self.makeIniFile()
  1274. iniFile = open(sfp3("pdfbooklet.cfg"), "w")
  1275. for a in out_a :
  1276. if not a in ["mru", "mru2", "options"] :
  1277. continue
  1278. iniFile.write("[" + a + "]\n")
  1279. for b in out_a[a] :
  1280. value = out_a[a][b]
  1281. if value == True :
  1282. value = '1'
  1283. elif value == False :
  1284. value = '0'
  1285. iniFile.write(b + " = " + value + "\n")
  1286. iniFile.write("\n")
  1287. iniFile.close()
  1288. def readNumEntry(self, entry, widget_s = "") :
  1289. if sys.version_info[0] == 2 and isinstance(entry, unicode) :
  1290. value = entry
  1291. elif isinstance(entry, str) :
  1292. value = entry
  1293. else :
  1294. value = entry.get_text()
  1295. value = value.replace(",", ".")
  1296. if value == "" : value = 0
  1297. try :
  1298. value = float(value)
  1299. except :
  1300. showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
  1301. return None
  1302. return value
  1303. def readmmEntry(self, entry, widget_s = "", default = 0) :
  1304. global adobe_l
  1305. value = ""
  1306. if sys.version_info[0] == 2 and isinstance(entry, unicode) :
  1307. value = entry
  1308. elif isinstance(entry, str) :
  1309. value = entry
  1310. else :
  1311. value = entry.get_text()
  1312. value = value.replace(",", ".")
  1313. if (value == "") :
  1314. value = default
  1315. else :
  1316. try :
  1317. value = float(value) / adobe_l
  1318. except :
  1319. showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
  1320. return None
  1321. return value
  1322. def readPercentEntry(self, entry, widget_s = "") :
  1323. value = ""
  1324. if sys.version_info[0] == 2 and isinstance(entry, unicode) :
  1325. value = entry
  1326. elif isinstance(entry, str) :
  1327. value = entry
  1328. else :
  1329. value = entry.get_text()
  1330. value = value.replace(",", ".")
  1331. if (value == "") :
  1332. value = 100
  1333. else :
  1334. value.replace("%", "")
  1335. try :
  1336. value = float(value) / 100
  1337. if value < 0 :
  1338. showwarning(_("Invalid data"), _("Invalid data for %s - must be > 0. Aborting \n") % widget_s)
  1339. return None
  1340. except :
  1341. showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
  1342. return None
  1343. return value
  1344. def readIntEntry(self, entry, widget_s = "", type_i = 0, default = "") :
  1345. # type = 0 : accepts all values >= 0
  1346. # type = 1 : accepts all values > 0
  1347. # type = -1 : accepts any integer, positive or negative
  1348. # type = 2 : optional. Don't warn if missing, but warn if invalid (not integer)
  1349. value = ""
  1350. if isinstance(entry, str) :
  1351. value = entry
  1352. else :
  1353. value = entry.get_text()
  1354. value = value.replace(",", ".")
  1355. try :
  1356. value = int(value)
  1357. if type_i == 0 :
  1358. if value < 0 :
  1359. showwarning(_("Invalid data"), _("Invalid data for %s - must be >= 0. Aborting \n") % widget_s)
  1360. return None
  1361. elif type_i == 1 :
  1362. if value < 1 :
  1363. showwarning(_("Invalid data"), _("Invalid data for %s - must be > 0. Aborting \n") % widget_s)
  1364. return None
  1365. except :
  1366. if value == "" :
  1367. if type_i == 2 :
  1368. pass
  1369. else :
  1370. showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
  1371. return None
  1372. else :
  1373. showwarning(_("Invalid data"), _("Invalid data for %s - must be numeric. Aborting \n") % widget_s)
  1374. return None
  1375. if value == "" :
  1376. return default
  1377. return value
  1378. def readBoolean(self, entry) :
  1379. value = ""
  1380. if sys.version_info[0] == 2 :
  1381. if isinstance(entry, unicode) :
  1382. value = entry
  1383. elif isinstance(entry, str) :
  1384. value = entry
  1385. else :
  1386. value = entry.get_text()
  1387. try :
  1388. if int(value) < 1 :
  1389. return False
  1390. else :
  1391. return True
  1392. except :
  1393. showwarning(_("Invalid data"), _("Invalid data for %s - must be 0 or 1. Aborting \n") % widget_s)
  1394. def readGui(self, logdata = 1) :
  1395. global config, rows_i, columns_i, step_i, sections, output, input1, input2, adobe_l, inputFiles_a, inputFile_a
  1396. global numfolio, prependPages, appendPages, ref_page, selection
  1397. global numPages, pagesSel, llx_i, lly_i, urx_i, ury_i, mediabox_l, outputScale, refPageSize_a
  1398. if self.freeze_b == True :
  1399. return
  1400. # Read the gui and update the config dictionary
  1401. self.makeIniFile()
  1402. outputFile = config["options"]["output"]
  1403. outputScale = 1
  1404. rows_i = self.readIntEntry(self.arw["entry11"], _("rows"), 1)
  1405. #if rows_i == None : return False
  1406. columns_i = self.readIntEntry(self.arw["entry12"], _("columns"), 1)
  1407. #if columns_i == None : return False
  1408. if (rows_i < 1) :
  1409. rows_i = 1
  1410. if (columns_i < 1) :
  1411. columns_i = 1
  1412. if ini.repeat == 1 :
  1413. step_i = self.readIntEntry(self.arw["entry15"], _("step"), 1)
  1414. if step_i == None : return False
  1415. if (step_i < 1) :
  1416. step_i = 1
  1417. else :
  1418. step_i = rows_i * columns_i
  1419. numfolio = self.readIntEntry(self.arw["entry13"], _("folios"))
  1420. prependPages = self.readIntEntry(self.arw["entry32"], _("Leading blank pages"))
  1421. if prependPages == None : return False
  1422. appendPages = self.readIntEntry(self.arw["entry33"], _("Trailing blank pages"))
  1423. if appendPages == None : return False
  1424. selection = self.selection_s
  1425. if self.arw["radiosize1"].get_active() == 1 :
  1426. radiosize = 1
  1427. elif self.arw["radiosize2"].get_active() == 1 :
  1428. radiosize = 2
  1429. if self.arw["radiosize3"].get_active() == 1 :
  1430. radiosize = 3
  1431. referencePage = config["options"]["referencePage"]
  1432. if referencePage.strip() == "" or referencePage == "0" :
  1433. referencePage = "1"
  1434. temp1 = referencePage.split(":")
  1435. try :
  1436. if len(temp1) == 2 :
  1437. ref_file = int(temp1[0])
  1438. ref_page = int(temp1[1])
  1439. else :
  1440. ref_file = 1
  1441. ref_page = int(temp1[0])
  1442. except :
  1443. alert(_("Invalid value for reference page, please correct and try again"))
  1444. return False
  1445. if ref_page > 0 :
  1446. system_ref_page = ref_page - 1 # system numbering start from 0 and not 1
  1447. ini.output_page_size(radiosize, ref_file, system_ref_page, logdata)
  1448. return True
  1449. def setOption(self, option, widget, section = "options") :
  1450. global config
  1451. if section in config :
  1452. if option in config[section] :
  1453. value = config[section][option]
  1454. z = widget.class_path()[1]
  1455. z2 = z.split(".")
  1456. z3 = z2[-1]
  1457. if z3 == "GtkSpinButton" :
  1458. mydata = value
  1459. mydata = mydata.replace(",",".")
  1460. if mydata.strip() != "" :
  1461. widget.set_value(float(mydata))
  1462. elif z3 == "GtkTextView" :
  1463. widget.get_buffer().set_text(value)
  1464. elif z3 == "GtkCheckButton" :
  1465. try :
  1466. value = int(value)
  1467. except :
  1468. pass
  1469. if bool_test(value) == True :
  1470. widget.set_active(True)
  1471. else :
  1472. widget.set_text(value)
  1473. def setupGui(self, inifile = "") :
  1474. global config, rows_i, columns_i, step_i, cells_i, input1, adobe_l
  1475. global numfolio, prependPages, appendPages, ref_page, selection
  1476. global numPages, pagesSel, llx_i, lly_i, urx_i, ury_i, inputFile, inputFiles_a
  1477. global startup_b
  1478. self.freeze_b = True # prevent update of the display, which would trigger readGui and corrupt the data
  1479. # set radio buttons
  1480. if "presets" in config["options"] :
  1481. temp1 = config["options"]["presets"]
  1482. self.arw[temp1].set_active(True)
  1483. if "size" in config["options"] :
  1484. temp1 = config["options"]["size"]
  1485. self.arw[temp1].set_active(True)
  1486. if "presetOrientation" in config["options"] :
  1487. temp1 = config["options"]["presetOrientation"]
  1488. self.arw[temp1].set_active(True)
  1489. if "globalRotation" in config["options"] :
  1490. temp1 = config["options"]["globalRotation"]
  1491. self.arw[temp1].set_active(True)
  1492. self.setOption("rows", self.arw["entry11"])
  1493. self.setOption("columns", self.arw["entry12"])
  1494. self.setOption("step", self.arw["entry15"])
  1495. self.setOption("numfolio", self.arw["entry13"])
  1496. self.setOption("prependPages", self.arw["entry32"])
  1497. self.setOption("appendPages", self.arw["entry33"])
  1498. self.setOption("referencePage", self.arw["entry31"])
  1499. self.setOption("creep", self.arw["creep"])
  1500. self.setOption("output", self.arw["entry2"])
  1501. self.setOption("width", self.arw["outputWidth"])
  1502. self.setOption("height", self.arw["outputHeight"])
  1503. self.setOption("userLayout", self.arw["user_layout"])
  1504. self.setOption("htranslate", self.arw["htranslate2"], "output")
  1505. self.setOption("vtranslate", self.arw["vtranslate2"], "output")
  1506. self.setOption("scale", self.arw["scale2"], "output")
  1507. self.setOption("rotate", self.arw["rotation2"], "output")
  1508. self.setOption("xscale", self.arw["xscale2"], "output")
  1509. self.setOption("yscale", self.arw["yscale2"], "output")
  1510. self.setOption("vflip", self.arw["vflip2"], "output")
  1511. self.setOption("hflip", self.arw["hflip2"], "output")
  1512. self.setOption("font_size", self.arw["numbers_font_size"], "page_numbers")
  1513. self.setOption("start_from", self.arw["numbers_start_from"], "page_numbers")
  1514. self.setOption("bottom_margin", self.arw["numbers_bottom_margin"], "page_numbers")
  1515. # set check boxes
  1516. if "advanced" in config["options"] :
  1517. if config.getint("options", "advanced") == 1 : self.advanced.set_active(1)
  1518. else : self.advanced.set_active(0)
  1519. self.guiAdvanced()
  1520. if "autoscale" in config["options"] :
  1521. if bool_test(config["options"]["autoscale"]) == True :
  1522. self.autoscale.set_active(1)
  1523. else :
  1524. self.autoscale.set_active(0)
  1525. if "autoRotate" in config["options"] :
  1526. if int(config["options"]["autoRotate"]) == 1 : self.autorotate.set_active(1)
  1527. else : self.autorotate.set_active(0)
  1528. if "showPdf" in config["options"] :
  1529. if bool_test(config["options"]["showPdf"]) == True : self.arw["show"].set_active(1)
  1530. else : self.arw["show"].set_active(0)
  1531. if "saveSettings" in config["options"] :
  1532. if bool_test(config["options"]["saveSettings"]) == True : self.settings.set_active(1)
  1533. else : self.settings.set_active(0)
  1534. if "noCompress" in config["options"] :
  1535. if bool_test(config["options"]["noCompress"]) == True : self.noCompress.set_active(1)
  1536. else : self.noCompress.set_active(0)
  1537. if "overwrite" in config["options"] :
  1538. if bool_test(config["options"]["overwrite"]) == True : self.overwrite.set_active(1)
  1539. else : self.overwrite.set_active(0)
  1540. if "righttoleft" in config["options"] :
  1541. if bool_test(config["options"]["righttoleft"]) == True : self.righttoleft.set_active(1)
  1542. else : self.righttoleft.set_active(0)
  1543. if "slowmode" in config["options"] :
  1544. if bool_test(config["options"]["slowmode"]) == True : self.slowmode.set_active(1)
  1545. else : self.slowmode.set_active(0)
  1546. if "page_numbers" in config :
  1547. if "page_numbers" in config["page_numbers"] :
  1548. if bool_test(config["page_numbers"]["page_numbers"]) == True :
  1549. self.arw["page_numbers"].set_active(1)
  1550. else :
  1551. self.arw["page_numbers"].set_active(0)
  1552. # set TextView
  1553. buf = self.arw["textview1"].get_buffer()
  1554. if "userLayout" in config["options"] :
  1555. buf.set_text(config["options"]["userLayout"])
  1556. self.freeze_b = False
  1557. def makeIniFile(self, inifile = "") :
  1558. # Reads the gui and updates config dictionary
  1559. global config, rows_i, columns_i, step_i, cells_i, input1, adobe_l
  1560. global numfolio, prependPages, appendPages, ref_page, selection
  1561. global numPages, pagesSel, inputFile
  1562. global out_a
  1563. out_a = config
  1564. config["options"]["rows"] = self.arw["entry11"].get_text()
  1565. config["options"]["columns"] = self.arw["entry12"].get_text()
  1566. config["options"]["booklet"] = str(ini.booklet)
  1567. config["options"]["step"] = self.arw["entry15"].get_text()
  1568. config["options"]["numfolio"] = self.arw["entry13"].get_text()
  1569. config["options"]["prependPages"] = self.arw["entry32"].get_text()
  1570. config["options"]["appendPages"] = self.arw["entry33"].get_text()
  1571. config["options"]["referencePage"] = self.arw["entry31"].get_text()
  1572. config["options"]["creep"] = self.arw["creep"].get_text()
  1573. config["options"]["pageSelection"] = self.selection_s # TODO : is this a good idea ?
  1574. buf = self.arw["user_layout"].get_buffer()
  1575. start, end = buf.get_bounds()
  1576. layout_s = buf.get_text(start, end, True)
  1577. config["options"]["userLayout"] = layout_s.replace("\n", "/")
  1578. temp1 = ""
  1579. for key in inputFiles_a :
  1580. val = inputFiles_a[key]
  1581. temp1 += val + '|'
  1582. config["options"]["inputs"] = temp1
  1583. config["options"]["output"] = self.arw["entry2"].get_text()
  1584. config["options"]["repeat"] = str(self.arw["entry15"].get_text())
  1585. config["options"]["showPdf"] = str(self.arw["show"].get_active())
  1586. config["options"]["saveSettings"] = str(self.settings.get_active())
  1587. config["options"]["autoscale"] = str(self.autoscale.get_active())
  1588. config["options"]["width"] = str(self.arw["outputWidth"].get_text())
  1589. config["options"]["height"] = str(self.arw["outputHeight"].get_text())
  1590. config["options"]["noCompress"] = str(self.noCompress.get_active())
  1591. config["options"]["righttoleft"] = str(self.righttoleft.get_active())
  1592. config["options"]["overwrite"] = str(self.overwrite.get_active())
  1593. config["options"]["slowmode"] = str(self.slowmode.get_active())
  1594. if not "output" in config :
  1595. config["output"] = OrderedDict()
  1596. config["output"]["htranslate"] = self.arw["htranslate2"].get_text()
  1597. config["output"]["vtranslate"] = self.arw["vtranslate2"].get_text()
  1598. config["output"]["scale"] = self.arw["scale2"].get_text()
  1599. config["output"]["rotate"] = self.arw["rotation2"].get_text()
  1600. config["output"]["xscale"] = self.arw["xscale2"].get_text()
  1601. config["output"]["yscale"] = self.arw["yscale2"].get_text()
  1602. config["output"]["vflip"] = self.arw["vflip2"].get_active()
  1603. config["output"]["hflip"] = self.arw["hflip2"].get_active()
  1604. if not "page_numbers" in config :
  1605. config["page_numbers"] = OrderedDict()
  1606. config["page_numbers"]["page_numbers"] = self.arw["page_numbers"].get_active()
  1607. config["page_numbers"]["font_size"] = self.arw["numbers_font_size"].get_text()
  1608. config["page_numbers"]["start_from"] = self.arw["numbers_start_from"].get_text()
  1609. config["page_numbers"]["bottom_margin"] = self.arw["numbers_bottom_margin"].get_text()
  1610. ## config["output"]["yscale"] = self.arw["yscale2"].get_text()
  1611. # radio buttons
  1612. group = self.arw["radiopreset1"].get_group()
  1613. for a in group :
  1614. if a.get_active() == True :
  1615. config["options"]["presets"] = a.get_name()
  1616. group = self.arw["radiosize1"].get_group()
  1617. for a in group :
  1618. if a.get_active() == True :
  1619. config["options"]["size"] = a.get_name()
  1620. group = self.arw["presetOrientation1"].get_group()
  1621. for a in group :
  1622. if a.get_active() == True :
  1623. config["options"]["presetOrientation"] = a.get_name()
  1624. group = self.arw["globalRotation0"].get_group()
  1625. for a in group :
  1626. if a.get_active() == True :
  1627. config["options"]["globalRotation"] = a.get_name()
  1628. # most recently used
  1629. # TODO : if file exists (ici et ailleurs)
  1630. configtemp = parser.read(sfp3("pdfbooklet.cfg"))
  1631. if "mru" in configtemp :
  1632. for option in configtemp["mru"] :
  1633. if "mru" in config :
  1634. config["mru"][option] = configtemp["mru"][option]
  1635. return config
  1636. def _______________________PRESETS() :
  1637. pass
  1638. def guiPresets(self, radiobutton = 0, event = None) :
  1639. global startup_b, project_b, preview_b
  1640. if radiobutton != 0 :
  1641. if radiobutton.get_active() == 0 : # signal is sent twice, ignore one of them
  1642. return
  1643. if project_b == True : # Don't change values if we are loading a project
  1644. return
  1645. preview_b = False # prevent multiple preview commands due to signals emitted by controls
  1646. presetOrientation_i = self.arw["presetOrientation1"].get_active()
  1647. if self.arw["radiopreset1"].get_active() == 1 : # single booklet
  1648. if presetOrientation_i == 1 :
  1649. self.presetBooklet(0,0)
  1650. else :
  1651. self.presetBooklet(0,1)
  1652. self.guiPresetsShow("booklet")
  1653. elif self.arw["radiopreset2"].get_active() == 1 : # Multiple booklets
  1654. if presetOrientation_i == 1 :
  1655. self.presetBooklet(5,0)
  1656. else :
  1657. self.presetBooklet(5,1)
  1658. self.guiPresetsShow("booklet")
  1659. elif self.arw["radiopreset3"].get_active() == 1 : # 2-up
  1660. if presetOrientation_i == 1 :
  1661. self.presetUp(1,2)
  1662. else :
  1663. self.presetUp(2,1)
  1664. self.guiPresetsShow("copies")
  1665. elif self.arw["radiopreset4"].get_active() == 1 : # 4-up in lines
  1666. if presetOrientation_i == 1 :
  1667. self.presetUp(2,2,1)
  1668. else :
  1669. self.presetUp(2,2,1)
  1670. self.guiPresetsShow("")
  1671. elif self.arw["radiopreset5"].get_active() == 1 : # 4-up in columns
  1672. if presetOrientation_i == 1 :
  1673. self.presetUp(2,2,2)
  1674. else :
  1675. self.presetUp(2,2,2)
  1676. self.guiPresetsShow("")
  1677. elif self.arw["radiopreset6"].get_active() == 1 : # x copies
  1678. if presetOrientation_i == 1 :
  1679. self.presetCopies(1,2)
  1680. else :
  1681. self.presetCopies(2,1)
  1682. self.guiPresetsShow("copies")
  1683. elif self.arw["radiopreset7"].get_active() == 1 : # 1 page
  1684. if presetOrientation_i == 1 :
  1685. self.presetMerge()
  1686. else :
  1687. self.presetMerge()
  1688. self.guiPresetsShow("copies")
  1689. elif self.arw["radiopreset8"].get_active() == 1 : # User defined
  1690. return # This button launchs the function "user_defined" which will handle the request
  1691. preview_b = True
  1692. if radiobutton != 0 and startup_b == False :
  1693. self.preview(self.previewPage)
  1694. def user_defined(self, widget) :
  1695. # Called by the "user defined" option button in the main window
  1696. # Gets data from the dialog where user enters the user defined layout
  1697. # Process the text from the TextView and sets controls and variables
  1698. # then update the preview.
  1699. # @widget : if this parameter is False, the dialog is not shown (used by OpenProject)
  1700. global startup_b, project_b, preview_b
  1701. preview_b = False # prevent multiple preview commands due to signals emitted by controls
  1702. if widget == False :
  1703. response = 1
  1704. else :
  1705. dialog = self.arw["dialog2"]
  1706. response = dialog.run()
  1707. dialog.hide()
  1708. if response == 0 :
  1709. return
  1710. if response == 1 :
  1711. buf = self.arw["user_layout"].get_buffer()
  1712. start, end = buf.get_bounds()
  1713. layout_s = buf.get_text(start, end, False)
  1714. imposition2 = ini.parse_user_layout(layout_s)
  1715. self.userpages = imposition2[0]
  1716. (self.imposition, numrows, numcols, pages) = imposition2
  1717. # set step
  1718. if self.arw["step_defined"].get_active() == True :
  1719. step_s = self.arw["step_value"].get_text()
  1720. self.presetCopies(numrows,numcols,step_s)
  1721. self.guiPresetsShow("copies")
  1722. else :
  1723. self.presetUp(numrows,numcols)
  1724. self.guiPresetsShow("")
  1725. total_pages = numrows * numcols
  1726. if len(pages) != total_pages :
  1727. message =_("Expected page number was : %d. Only %d found. \nThere is an error in your layout, please correct") % (total_pages, len(pages))
  1728. alert(message)
  1729. # Update gui before updating preview, since preview takes its data from the gui
  1730. while Gtk.events_pending():
  1731. Gtk.main_iteration()
  1732. preview_b = True
  1733. if startup_b == False :
  1734. self.preview(self.previewPage)
  1735. def select_step_value(self, widget, event) :
  1736. # launched when user types something in "step_value" entry
  1737. # Selects the appropriate radio button
  1738. self.arw["step_defined"].set_active(True)
  1739. def guiPresetsShow(self, action_s) :
  1740. StepWidgets = [self.arw["label15"], self.arw["entry15"]]
  1741. LeafsWidgets = [self.arw["label13"], self.arw["entry13"]]
  1742. OrientationWidgets = [self.arw["label11"], self.arw["presetOrientation1"],
  1743. self.arw["label12"], self.arw["presetOrientation2"]]
  1744. for a in StepWidgets + LeafsWidgets + OrientationWidgets :
  1745. a.hide()
  1746. if action_s == "booklet" :
  1747. for a in LeafsWidgets + OrientationWidgets :
  1748. a.show()
  1749. if action_s == "copies" :
  1750. for a in StepWidgets + OrientationWidgets :
  1751. a.show()
  1752. def presetBooklet(self, leafs_i, orientation) :
  1753. if orientation == 0 :
  1754. self.arw["entry11"].set_value(1) # rows
  1755. self.arw["entry12"].set_value(2) # columns
  1756. else :
  1757. self.arw["entry11"].set_value(2) # rows
  1758. self.arw["entry12"].set_value(1) # columns
  1759. self.arw["entry13"].set_text(str(leafs_i)) # leafs in booklet
  1760. ini.repeat = 0
  1761. ini.booklet = 1
  1762. def presetUp(self, r,c,l=1) :
  1763. self.arw["entry11"].set_value(r) # rows
  1764. self.arw["entry12"].set_value(c) # columns
  1765. ini.booklet = 0 # checkerboard
  1766. ini.radioDisp = l # lines / columns
  1767. ini.repeat = 0
  1768. def presetCopies(self, r,c, step="1") :
  1769. global step_i
  1770. self.arw["entry11"].set_value(r) # rows
  1771. self.arw["entry12"].set_value(c) # columns
  1772. ini.booklet = 0 # checkerboard
  1773. ini.repeat = 1
  1774. self.arw["entry15"].set_text(step) # step
  1775. def presetCopies2(self, r,c,l=1) :
  1776. self.arw["entry11"].set_value(r) # rows
  1777. self.arw["entry12"].set_value(c) # columns
  1778. ini.booklet = 0 # checkerboard
  1779. ini.radioDisp = l # lines / columns
  1780. ini.repeat = 0
  1781. def presetMerge(self) : # One page
  1782. global outputFile, inputFile_a
  1783. self.arw["entry11"].set_value(1) # rows
  1784. self.arw["entry12"].set_value(1) # columns
  1785. ini.booklet = 0 # checkerboard
  1786. ini.repeat = 0
  1787. def _________________________PREVIEW() :
  1788. pass
  1789. def OnDraw(self, area, cr):
  1790. global page
  1791. global areaAllocationW_i, areaAllocationH_i
  1792. global previewColPos_a, previewRowPos_a, pageContent_a
  1793. global refPageSize_a, numPages
  1794. global outputStream, outputStream_mem
  1795. # Was the size changed ?
  1796. if (areaAllocationW_i == self.area.get_allocated_width()
  1797. and areaAllocationH_i == self.area.get_allocated_height()) :
  1798. nochange_b = True
  1799. else :
  1800. nochange_b = False
  1801. areaAllocationW_i = self.area.get_allocated_width()
  1802. areaAllocationH_i = self.area.get_allocated_height()
  1803. # If nothing has not yet been loaded, show nofile.pdf
  1804. try :
  1805. data1 = outputStream.getvalue()
  1806. except :
  1807. f1 = open(sfp2("data/nofile.pdf"),"rb")
  1808. data1 = f1.read()
  1809. f1.close()
  1810. # TODO : When two calls are too close, it may create a strange stack resulting in an empty OutputStream
  1811. # This has to be redesigned.
  1812. if len(data1) == 0 :
  1813. #print("......... OutputStream is empty")
  1814. return
  1815. # If the preview has not changed, don't render
  1816. # TODO : This must be redesigned, because with the image in memory, it may be necessary to render again with the same data
  1817. ## if data1 == outputStream_mem :
  1818. ## return
  1819. ## else :
  1820. ## outputStream_mem = data1
  1821. try :
  1822. bytes_data = GLib.Bytes(data1)
  1823. input_stream = Gio.MemoryInputStream.new_from_bytes(bytes_data)
  1824. # Take care that you need to call .close() on the Gio.MemoryInputStream once you're done with your pdf document.
  1825. document = Poppler.Document.new_from_stream(input_stream, -1, None, None)
  1826. self.document= document
  1827. except :
  1828. # bug workaround
  1829. """
  1830. Hi there, I installed the deb on Ubuntu 19.04. It installed fine but gives this message:
  1831. Traceback (most recent call last):
  1832. File "/usr/local/lib/python3.7/dist-packages/pdfbooklet/pdfbooklet.py", line 2342, in OnDraw
  1833. document = Poppler.Document.new_from_stream(input_stream, -1, None, None)
  1834. gi.repository.GLib.GError: poppler-quark: Failed to load document (0)
  1835. 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.
  1836. """
  1837. try:
  1838. filepath = os.path.join(tempfile.gettempdir(), "preview.pdf")
  1839. with open(filepath, "wb") as f1:
  1840. f1.write(data1)
  1841. document = Poppler.Document.new_from_file("file:///" + filepath, None)
  1842. self.document= document
  1843. except:
  1844. print("Error rendering document in poppler")
  1845. printExcept()
  1846. return False
  1847. page = document.get_page(0)
  1848. self.page = page
  1849. x = document.get_n_pages()
  1850. if x > 1 :
  1851. self.page1 = document.get_page(1)
  1852. else :
  1853. self.page1 = None
  1854. # calculate the preview size
  1855. pix_w, pix_h = page.get_size()
  1856. A = int((areaAllocationH_i * pix_w) / pix_h) # width of preview if full height
  1857. B = int((areaAllocationW_i * pix_h) / pix_w) # height of preview if full width
  1858. if A < areaAllocationW_i : # if full height is OK
  1859. heightPoints = areaAllocationH_i
  1860. widthPoints = A
  1861. scale = areaAllocationH_i / pix_h
  1862. else :
  1863. widthPoints = areaAllocationW_i
  1864. heightPoints = B
  1865. scale = areaAllocationW_i / pix_w
  1866. self.preview_scale = scale # this will be needed for drag (see the end_drag function)
  1867. Hoffset_i = int((areaAllocationW_i - widthPoints) /2)
  1868. Voffset_i = int((areaAllocationH_i - heightPoints)/2)
  1869. # set background.
  1870. cr.set_source_rgb(0.7, 0.6, 0.5)
  1871. cr.paint()
  1872. # set page background
  1873. cr.set_source_rgb(1, 1, 1)
  1874. cr.rectangle(Hoffset_i, Voffset_i, widthPoints, heightPoints)
  1875. cr.fill()
  1876. # render page
  1877. cr.save()
  1878. cr.translate(Hoffset_i, Voffset_i)
  1879. cr.scale(scale,scale)
  1880. self.page.render(cr)
  1881. cr.restore()
  1882. # If there are two pages in the preview, the second will be printed over the other
  1883. # to help fine tuning the margins for duplex printing
  1884. if self.page1 :
  1885. cr.save()
  1886. cr.translate(Hoffset_i, Voffset_i)
  1887. cr.scale(scale,scale)
  1888. self.page1.render(cr)
  1889. cr.restore()
  1890. # clear memory
  1891. input_stream.close()
  1892. # draw middle line
  1893. if ((ini.booklet > 0 and len(inputFiles_a) > 0)
  1894. or self.arw["draw_middle_line"].get_active() == True) :
  1895. cr.save()
  1896. cr.set_line_width(1)
  1897. cr.set_dash((10,8))
  1898. cr.set_source_rgb(0.5, 0.5, 1)
  1899. line_position = (areaAllocationW_i/2) + 0.5 # The reason for + 0.5 is explained here : https://www.cairographics.org/FAQ/#sharp_lines
  1900. # under the title : How do I draw a sharp, single-pixel-wide line?
  1901. cr.move_to(line_position, Voffset_i)
  1902. cr.line_to(line_position, (areaAllocationH_i - Voffset_i ))
  1903. cr.stroke()
  1904. cr.restore()
  1905. # if the output is turned, swap the numbers of rows and columns
  1906. if app.arw["globalRotation90"].get_active() == 1 \
  1907. or app.arw["globalRotation270"].get_active() == 1:
  1908. preview_cols = rows_i
  1909. preview_rows = columns_i
  1910. else :
  1911. preview_cols = columns_i
  1912. preview_rows = rows_i
  1913. columnWidth = widthPoints / preview_cols
  1914. rowHeight = heightPoints / preview_rows
  1915. #store position of columns and rows
  1916. # previewColPos_a will contain the position of the left of all columns (from left to right)
  1917. # previewRowPos_a will contain the position of the top of all rows (from bottom to top)
  1918. previewColPos_a = []
  1919. previewRowPos_a = []
  1920. previewPagePos_a = {}
  1921. for a1 in range(preview_cols) :
  1922. #left of the column
  1923. previewColPos_a += [int((a1 * columnWidth) + Hoffset_i)]
  1924. for a2 in range(preview_rows) :
  1925. #top of the row
  1926. # rows count starts from bottom => areaAllocationH_i - ...
  1927. previewRowPos_a += [areaAllocationH_i - (int((a2 * rowHeight) + Voffset_i ))]
  1928. # add the right pos of the last col
  1929. previewColPos_a += [int(previewColPos_a[a1] + columnWidth)]
  1930. previewRowPos_a += [int(previewRowPos_a[a2] - rowHeight)]
  1931. # show page numbers
  1932. i = 0
  1933. pageContent_a = {}
  1934. for a in self.rotate_layout() : # returns the layout, rotated if necessary
  1935. # human readable page number
  1936. pageRef_s = self.ar_pages[0][i]
  1937. file_number, page_number = pageRef_s.split(":")
  1938. page_number = int(page_number) + 1
  1939. if file_number == "1" :
  1940. pageNumber = str(page_number)
  1941. else :
  1942. pageNumber = str(file_number) + ":" + str(page_number)
  1943. fontsize_i = int(columnWidth / 4)
  1944. if fontsize_i < 10 :
  1945. fontsize_i = 10
  1946. colpos_i = previewColPos_a[a[1]] + (columnWidth / 2)
  1947. rowpos_i = previewRowPos_a[a[0]] - (rowHeight / 2)
  1948. x1,x2 = colpos_i,rowpos_i
  1949. # draw page number
  1950. if self.arw["hide_numbers"].get_active() == 0 :
  1951. # TODO cr.select_font_face("Georgia",
  1952. ## cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
  1953. ## AttributeError: 'gi.repository.cairo' object has no attribute 'FONT_SLANT_NORMAL'
  1954. ## Remplacé par 0 et 1 ci-dessous
  1955. cr.select_font_face("Georgia", 0, 1)
  1956. cr.set_font_size(fontsize_i)
  1957. x_bearing, y_bearing, txtWidth, txtHeight = cr.text_extents(pageNumber)[:4]
  1958. colpos_i -= int(txtWidth / 4)
  1959. rowpos_i -= int(txtHeight / 4)
  1960. cr.move_to(colpos_i, rowpos_i - y_bearing)
  1961. cr.set_source_rgba(1, 0, 0, 0.6)
  1962. cr.show_text(pageNumber)
  1963. pageId = str(a[0]) + ":" + str(a[1])
  1964. pageContent_a[pageId] = [file_number, page_number]
  1965. # page position
  1966. bottom = previewRowPos_a[a[0]]
  1967. top = previewRowPos_a[a[0]] - rowHeight
  1968. left = previewColPos_a[a[1]]
  1969. right = previewColPos_a[a[1]] + columnWidth
  1970. # draw rectangle if selected
  1971. humanReadableRow_i = (preview_rows - a[0])
  1972. pagePosition_a = [humanReadableRow_i, a[1] + 1]
  1973. pagePosition_s = self.rotate_position(pagePosition_a)
  1974. pageNumber_s = str(file_number) + ":" + str(page_number)
  1975. draw_page_b = False
  1976. type_s = ""
  1977. # is this page odd or even ?
  1978. if self.arw["evenpages"].get_active() == 1 :
  1979. if int(pageNumber) % 2 == 0 :
  1980. cr.set_source_rgb(0, 0, 1)
  1981. draw_page_b = True
  1982. type_s = "even"
  1983. elif self.arw["oddpages"].get_active() == 1 :
  1984. if int(pageNumber) % 2 == 1 :
  1985. cr.set_source_rgb(0, 0, 1)
  1986. draw_page_b = True
  1987. type_s = "odd"
  1988. # if page is selected
  1989. elif pagePosition_s in selectedIndex_a :
  1990. # Select the rectangle color
  1991. cr.set_source_rgb(1, 0, 0)
  1992. draw_page_b = True
  1993. type_s = "select"
  1994. elif pageNumber_s in selectedIndex_a :
  1995. # Select the rectangle color
  1996. cr.set_source_rgb(0, 0.7, 0)
  1997. draw_page_b = True
  1998. type_s = "select"
  1999. # draw rectangle
  2000. if draw_page_b == True :
  2001. file_number, page_number = self.myselectedpage.split(":")
  2002. selected_ref = file_number + ":" + str(int(page_number) - 1)
  2003. coord = [left, top]
  2004. cr.set_line_width(3)
  2005. cr.rectangle(coord[0], coord[1], columnWidth, rowHeight)
  2006. cr.stroke()
  2007. i += 1
  2008. # Show page size and total pages number
  2009. ## cr.select_font_face("Arial", 0, 1)
  2010. # TODO Anciennement cr.select_font_face("Arial", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
  2011. # mais les attributs ne marche plus.
  2012. ## cr.set_font_size(12)
  2013. try :
  2014. message = str(round(refPageSize_a[0] * adobe_l))
  2015. message += " x " + str(round(refPageSize_a[1] * adobe_l))
  2016. message += " - " + str(numPages) + " pages"
  2017. ## x_bearing, y_bearing, txtWidth, txtHeight = cr.text_extents(message)[:4]
  2018. ## cr.move_to(20,20)
  2019. ## cr.set_source_rgb(0, 0.6, 0)
  2020. ## cr.show_text(message)
  2021. self.arw["info_fichier"].set_text(message)
  2022. except :
  2023. pass # if refPageSize is not defined, error
  2024. def rotate_layout(self) :
  2025. rotated_layout= []
  2026. for i in range (len(self.ar_layout)) :
  2027. r, c = self.ar_layout[i]
  2028. # Flip columns or rows if vertical or horizontal flip is selected
  2029. if app.arw["vflip2"].get_active() == 1 :
  2030. r = (rows_i - 1) - r
  2031. if app.arw["hflip2"].get_active() == 1 :
  2032. c = (columns_i - 1) - c
  2033. # if output page is rotated (global rotation)
  2034. if app.arw["globalRotation270"].get_active() == 1 :
  2035. # invert row; We use columns_i because when rotated 90°,
  2036. # the numbers of rows of the preview is the number of columns of the page
  2037. r = (rows_i - 1) - r
  2038. r,c = c,r # swap
  2039. elif app.arw["globalRotation90"].get_active() == 1 :
  2040. # invert column; ; We use rows_i because when rotated 90°,
  2041. # the numbers of columns of the preview is the number of rows of the page
  2042. c = (columns_i - 1) - c
  2043. r,c = c,r # swap
  2044. elif app.arw["globalRotation180"].get_active() == 1 :
  2045. # invert row and column
  2046. r = (rows_i - 1) - r
  2047. c = (columns_i - 1) - c
  2048. rotated_layout.append([r,c])
  2049. return rotated_layout
  2050. def rotate_position(self, position) :
  2051. ## print( "source : ", position)
  2052. r, c = position
  2053. # if output page is rotated (global rotation)
  2054. if app.arw["globalRotation270"].get_active() == 1 :
  2055. # invert row; We use columns_i because when rotated 90°,
  2056. # the numbers of rows of the preview is the number of columns of the page
  2057. r = (columns_i + 1) - r
  2058. r,c = c,r # swap
  2059. elif app.arw["globalRotation90"].get_active() == 1 :
  2060. # invert column; ; We use rows_i because when rotated 90°,
  2061. # the numbers of columns of the preview is the number of rows of the page
  2062. c = (rows_i + 1) - c
  2063. r,c = c,r # swap
  2064. elif app.arw["globalRotation180"].get_active() == 1 :
  2065. # invert row and column
  2066. r = (rows_i + 1) - r
  2067. c = (columns_i +1) - c
  2068. ## print ("dest : ", str(r) + "," + str(c))
  2069. return str(r) + "," + str(c)
  2070. def selectPage (self, widget, event=None):
  2071. # Called by a click on the preview or on the radio buttons, this function will :
  2072. # - Select the appropriate Id
  2073. # - launch area_expose to update the display
  2074. # - fill in the gtkEntry widgets which contains the transformations
  2075. #
  2076. # The selected_page list contains a list of six values :
  2077. # - row and column (Pdf format)
  2078. # - file number and page number
  2079. # - row and column if the output page is rotated
  2080. global previewColPos_a, previewRowPos_a, canvasId20, pageContent_a
  2081. global selectedIndex_a, selected_page, selected_pages_a, pageId
  2082. if event == None : # Click on a radio button
  2083. if self.thispage.get_active() == 1 :
  2084. pageId = str(selected_page[2]) + ":" + str(selected_page[3])
  2085. Id = pageId
  2086. elif self.evenpages.get_active() == 1 :
  2087. Id = "even"
  2088. elif self.oddpages.get_active() == 1 :
  2089. Id = "odd"
  2090. else : # first button, pages in this position
  2091. humanReadableRow_i = rows_i - selected_page[4]
  2092. Id = str(str(humanReadableRow_i) + "," + str(selected_page[5] + 1))
  2093. Id = str(str(selected_page[5] + 1) + "," + str(humanReadableRow_i))
  2094. elif event.type == Gdk.EventType.BUTTON_PRESS:
  2095. if event.button == 3 : # right click, runs the context menu
  2096. self.arw["contextmenu1"].popup(None, None, None, None, event.button, event.time)
  2097. return
  2098. else :
  2099. # get preview area
  2100. left_limit = previewColPos_a[0]
  2101. bottom_limit = previewRowPos_a[-1]
  2102. right_limit = previewColPos_a[-1]
  2103. top_limit = previewRowPos_a[0]
  2104. xpos = event.x
  2105. ypos = event.y
  2106. self.initdrag = [xpos, ypos] # Will be used for moving page with the mouse
  2107. self.preview_limits = [left_limit, right_limit, top_limit, bottom_limit]
  2108. # check if click is inside preview
  2109. if (xpos < left_limit
  2110. or xpos > right_limit
  2111. or ypos < bottom_limit
  2112. or ypos > top_limit) :
  2113. return
  2114. # find the row and column
  2115. for c in range(len(previewColPos_a)) :
  2116. if xpos > previewColPos_a[c] and xpos < previewColPos_a[c + 1]:
  2117. leftPos_i = previewColPos_a[c]
  2118. rightPos_i = previewColPos_a[c + 1]
  2119. break
  2120. for r in range(len(previewRowPos_a)) :
  2121. if ypos < previewRowPos_a[r] and ypos > previewRowPos_a[r + 1]:
  2122. bottomPos_i = previewRowPos_a[r]
  2123. topPos_i = previewRowPos_a[r + 1]
  2124. break
  2125. r1 = r
  2126. c1 = c
  2127. # it should be possible to use rotate_layout
  2128. # Flip columns or rows if vertical or horizontal flip is selected
  2129. ## if app.arw["vflip2"].get_active() == 1 :
  2130. ## r1 = (rows_i - 1) - r
  2131. ## if app.arw["hflip2"].get_active() == 1 :
  2132. ## c1 = (columns_i - 1) - c
  2133. # if output page is rotated (global rotation)
  2134. if app.arw["globalRotation90"].get_active() == 1 :
  2135. # invert row; We use columns_i because when rotated 90°,
  2136. # the numbers of rows of the preview is the number of columns of the page
  2137. r1 = (columns_i - 1) - r1
  2138. r1,c1 = c1,r1 # swap
  2139. elif app.arw["globalRotation270"].get_active() == 1 :
  2140. # invert column; ; We use rows_i because when rotated 90°,
  2141. # the numbers of columns of the preview is the number of rows of the page
  2142. c1 = (rows_i - 1) - c1
  2143. r1,c1 = c1,r1 # swap
  2144. elif app.arw["globalRotation180"].get_active() == 1 :
  2145. # invert row and column
  2146. r1 = (rows_i - 1) - r1
  2147. c1 = (columns_i - 1) - c1
  2148. selected_page = [r, c]
  2149. selected_page += pageContent_a[str(r) + ":" + str(c)]
  2150. selected_page += [r1, c1]
  2151. # Selection information
  2152. self.myselectedpage = str(selected_page[2]) + ":" + str(selected_page[3])
  2153. if self.thispage.get_active() == 1 : # This page
  2154. Id = self.myselectedpage
  2155. elif self.evenpages.get_active() == 1 : # even pages
  2156. Id = "even"
  2157. elif self.oddpages.get_active() == 1 : # odd pages
  2158. Id = "odd"
  2159. else : # first button, pages in this position
  2160. humanReadableRow_i = rows_i - selected_page[4]
  2161. Id = str(str(humanReadableRow_i) + "," + str(selected_page[5] + 1))
  2162. if event.state != Gdk.ModifierType.CONTROL_MASK : # If Control not pressed, delete previous selection
  2163. selectedIndex_a = {}
  2164. selected_pages_a = []
  2165. # position reference
  2166. selectedIndex_a[Id] = 1
  2167. selected_pages_a.append(selected_page)
  2168. # force the "draw" signal to update the display
  2169. self.arw["drawingarea1"].hide()
  2170. self.arw["drawingarea1"].show()
  2171. else : # unsupported event
  2172. return
  2173. # Load settings in transformation dialogs
  2174. if event.state != Gdk.ModifierType.CONTROL_MASK : # but only if CONTROL is not pressed
  2175. self.load_settings_in_dialog(Id)
  2176. def load_settings_in_dialog(self, Id) :
  2177. # defaults
  2178. self.arw["htranslate1"].set_text("0")
  2179. self.Vtranslate1.set_text("0")
  2180. self.scale1.set_text("100")
  2181. self.rotation1.set_text("0")
  2182. self.arw["xscale1"].set_text("100")
  2183. self.arw["yscale1"].set_text("100")
  2184. self.arw["vflip1"].set_active(False)
  2185. self.arw["hflip1"].set_active(False)
  2186. if Id in config :
  2187. if "htranslate" in config[Id] :
  2188. self.arw["htranslate1"].set_text(str(config[Id]["htranslate"]))
  2189. if "vtranslate" in config[Id] :
  2190. self.Vtranslate1.set_text(str(config[Id]["vtranslate"]))
  2191. if "scale" in config[Id] :
  2192. self.scale1.set_text(str(config[Id]["scale"]))
  2193. if "rotate" in config[Id] :
  2194. self.rotation1.set_text(str(config[Id]["rotate"]))
  2195. if "xscale" in config[Id] :
  2196. self.arw["xscale1"].set_text(str(config[Id]["xscale"]))
  2197. if "yscale" in config[Id] :
  2198. self.arw["yscale1"].set_text(str(config[Id]["yscale"]))
  2199. if "vflip" in config[Id] :
  2200. bool_b = config[Id]["vflip"]
  2201. if bool_b == "True" or bool_b == True or bool_b == "1" : # when parameter comes from the ini file, it is a string
  2202. bool_b = 1
  2203. elif bool_b == "False" or bool_b == False or bool_b == "0" :
  2204. bool_b = 0
  2205. self.arw["vflip1"].set_active(bool_b)
  2206. if "hflip" in config[Id] :
  2207. bool_b = config[Id]["hflip"]
  2208. if bool_b == "True" or bool_b == True or bool_b == "1" : # when parameter comes from the ini file, it is a string
  2209. bool_b = 1
  2210. elif bool_b == "False" or bool_b == False or bool_b == "0" :
  2211. bool_b = 0
  2212. self.arw["hflip1"].set_active(bool_b)
  2213. def drag_motion(self, widget, cr, x, y, time) :
  2214. # unused
  2215. # To activate, connect the signal "drag-motion" of then eventbox parent of drawingarea1
  2216. # to this function
  2217. print (x)
  2218. cr.set_line_width(1)
  2219. cr.rectangle(x, y, 2, 2)
  2220. cr.stroke()
  2221. return False
  2222. def set_even_odd_settings(self, widget) : # launched by a clic on the domain buttons
  2223. global selected_page, selectedIndex_a
  2224. #print ("set even odd")
  2225. if self.evenpages.get_active() == 1 :
  2226. Id = "even"
  2227. selected_page = Id
  2228. self.load_settings_in_dialog(Id)
  2229. elif self.oddpages.get_active() == 1 :
  2230. Id = "odd"
  2231. selected_page = Id
  2232. self.load_settings_in_dialog(Id)
  2233. else :
  2234. #print ("reset selection")
  2235. selected_page = None
  2236. selectedIndex_a = {}
  2237. self.previewUpdate()
  2238. def end_drag(self, widget, event) :
  2239. global outputScale
  2240. # Thisfunction will move the page when the mouse button is released
  2241. x = event.x
  2242. y = event.y
  2243. # scaling factor
  2244. if self.arw["autoscale"].get_active() == 1 :
  2245. scaling_factor = self.preview_scale * outputScale
  2246. else :
  2247. scaling_factor = self.preview_scale
  2248. scaling_factor2 = scaling_factor / adobe_l
  2249. hmove = ((x - self.initdrag[0]) / scaling_factor2)
  2250. vmove = ((y - self.initdrag[1]) / scaling_factor2)
  2251. if self.arw["move_with_mouse"].get_active() == 1 :
  2252. # Calculate the scaling factor
  2253. temp = self.arw["htranslate1"].get_text()
  2254. temp = temp.replace(",", ".")
  2255. temp = float(temp)
  2256. temp += hmove
  2257. temp = str(temp).split(".")
  2258. temp = temp[0] + "." + temp[1][0:1]
  2259. self.arw["htranslate1"].set_text(temp)
  2260. self.transformationsApply("")
  2261. temp = self.arw["vtranslate1"].get_text()
  2262. temp = temp.replace(",", ".")
  2263. temp = float(temp)
  2264. temp -= vmove
  2265. temp = str(temp).split(".")
  2266. temp = temp[0] + "." + temp[1][0:1]
  2267. self.arw["vtranslate1"].set_text(temp)
  2268. self.transformationsApply("")
  2269. elif self.arw["delete_rectangle"].get_active() == 1 :
  2270. # preview_limits gives : left x, right x, bottom y, top y, in pixels.
  2271. #
  2272. # init_drag gives : x, y
  2273. # x = horizontal position
  2274. left = self.preview_limits[0] # left margin
  2275. top = self.preview_limits[3] # top margin
  2276. x1 = (self.initdrag[0] - left) # start drag
  2277. y1 = self.initdrag[1]
  2278. y1 = self.preview_limits[2] - y1 # invert the vertical position, because Pdf counts from bottom
  2279. # This corrects also the top margin because bottom y = page height + margin
  2280. x2 = x - left # end drag
  2281. y2 = y
  2282. y2 = self.preview_limits[2] - y2
  2283. width = x2 - x1
  2284. height = y2 - y1
  2285. x1 = x1 / scaling_factor
  2286. y1 = y1 / scaling_factor
  2287. width = width / scaling_factor
  2288. height = height / scaling_factor
  2289. ini.delete_rectangle = [x1,y1,width,height]
  2290. elif self.arw["divide"].get_active() == 1 :
  2291. # preview_limits gives : left x, right x, bottom y, top y, in pixels.
  2292. #
  2293. # init_drag gives : x, y
  2294. # x = horizontal position
  2295. left = self.preview_limits[0] # left margin
  2296. top = self.preview_limits[3] # top margin
  2297. x1 = (self.initdrag[0] - left) # start drag
  2298. y1 = self.initdrag[1]
  2299. y1 = self.preview_limits[2] - y1 # invert the vertical position, because Pdf counts from bottom
  2300. # This corrects also the top margin because bottom y = page height + margin
  2301. x2 = x - left # end drag
  2302. y2 = y
  2303. y2 = self.preview_limits[2] - y2
  2304. width = x2 - x1
  2305. height = y2 - y1
  2306. x1 = x1 / scaling_factor
  2307. y1 = y1 / scaling_factor
  2308. width = width / scaling_factor
  2309. height = height / scaling_factor
  2310. ini.delete_rectangle = [x1,y1,width,height]
  2311. def createSelection(self) :
  2312. global inputFiles_a
  2313. i = 1
  2314. x = []
  2315. for f in inputFiles_a :
  2316. fileName = inputFiles_a[f]
  2317. numPages = inputFile_a[fileName].getNumPages()
  2318. for z in range(numPages) :
  2319. pageRef_s = str(i) + ":" + str(z)
  2320. if pageRef_s + "deleted" in deletedIndex_a :
  2321. if deletedIndex_a[pageRef_s + "deleted"] == 1 :
  2322. pass
  2323. else :
  2324. x += [pageRef_s]
  2325. else :
  2326. x += [pageRef_s]
  2327. i += 1
  2328. self.selection_s = self.compressSelection(x)
  2329. def compressSelection(self, x) :
  2330. i = 0
  2331. temp = {}
  2332. out = ""
  2333. for a in x :
  2334. b = a.split(":")
  2335. if len(b) == 1 :
  2336. npage = b[0]
  2337. nfile = 1
  2338. else :
  2339. npage = b[1]
  2340. nfile = b[0]
  2341. if i == 0 :
  2342. temp["file"] = nfile
  2343. temp["first"] = npage
  2344. temp["last"] = npage
  2345. else :
  2346. if nfile == temp["file"] and int(npage) == int(temp["last"]) + 1 :
  2347. temp["last"] = npage # on continue
  2348. else : # sinon on écrit
  2349. if temp["first"] == "-1":
  2350. out += "b"
  2351. else :
  2352. out += str(temp["file"]) + ":" + str(temp["first"])
  2353. if temp["last"] != temp["first"] :
  2354. out += "-" + str(temp["last"])
  2355. out += "; "
  2356. # et on mémorise
  2357. temp["file"] = nfile
  2358. temp["first"] = npage
  2359. temp["last"] = npage
  2360. i += 1
  2361. if temp["first"] == "-1":
  2362. out += "b"
  2363. else :
  2364. out += str(temp["file"]) + ":" + str(temp["first"])
  2365. if temp["last"] != temp["first"] :
  2366. out += "-" + str(temp["last"])
  2367. # compress blank pages
  2368. temp1 = out.split(";")
  2369. out = ""
  2370. blank_count = 0
  2371. for a in temp1 :
  2372. if a.strip() == "b" :
  2373. blank_count += 1
  2374. else :
  2375. if blank_count > 0 :
  2376. out += str(blank_count) + "b;"
  2377. blank_count = 0
  2378. out += a + ";"
  2379. if blank_count > 0 :
  2380. out += str(blank_count) + "b"
  2381. return out
  2382. def edit_selection(self, widget) :
  2383. dialog = self.arw["dialog1"]
  2384. TextBuffer = self.arw["textview1"].get_buffer()
  2385. selection1 = self.selection_s.replace(";","\n")
  2386. TextBuffer.set_text(selection1)
  2387. choice = dialog.run()
  2388. if choice == 1 :
  2389. start_iter = TextBuffer.get_start_iter()
  2390. end_iter = TextBuffer.get_end_iter()
  2391. selection2 = TextBuffer.get_text(start_iter, end_iter, False)
  2392. selection2 = selection2.replace("\n",";")
  2393. self.selection_s = selection2
  2394. if self.shuffler:
  2395. self.shuffler.model.clear()
  2396. self.shuffler.pdfqueue = []
  2397. self.shuffler.nfile = 0
  2398. self.loadShuffler()
  2399. # TODO : N'est à faire que si la liste des fichiers a changé
  2400. self.shuffler.rendering_thread.pdfqueue = self.shuffler.pdfqueue
  2401. dialog.hide()
  2402. self.previewUpdate()
  2403. def compare_files_selection(self,widget) :
  2404. # Create a selection to display two or more files side by side
  2405. # Number of files and pages
  2406. files_data = {}
  2407. index = 1
  2408. maxPages = 0
  2409. numFiles = len(inputFiles_a)
  2410. for f in inputFiles_a :
  2411. fileName = inputFiles_a[f]
  2412. numPages = inputFile_a[fileName].getNumPages()
  2413. if numPages > maxPages :
  2414. maxPages = numPages
  2415. files_data[index] = numPages
  2416. index += 1
  2417. # create the selection
  2418. index = 1
  2419. text = ""
  2420. for i in range(maxPages) :
  2421. temp = []
  2422. for j in range(1, numFiles + 1) :
  2423. if files_data[j] > i : # if the file is shorter, insert blank pages
  2424. temp.append(str(j) + ":" + str(i))
  2425. else :
  2426. temp.append("b")
  2427. text += ",".join(temp) + "\n"
  2428. TextBuffer = self.arw["textview1"].get_buffer()
  2429. TextBuffer.set_text(text)
  2430. def preview(self, previewPage, delete = 1) :
  2431. global mediabox_l0, columns_i, rows_i, step_i, urx_i, ury_i, mediabox_l
  2432. global outputScale, pageContent_a
  2433. global selectedIndex_a, deletedIndex_a
  2434. global areaAllocationW_i, areaAllocationH_i
  2435. global preview_b
  2436. if preview_b == False :
  2437. return
  2438. if self.readGui(0) :
  2439. self.manage_backup()
  2440. if self.render.parsePageSelection() :
  2441. #self.readConditions()
  2442. ar_pages, ar_layout, ar_cahiers = self.render.createPageLayout(0)
  2443. if ar_pages != None :
  2444. if previewPage > len(ar_pages) - 1 :
  2445. previewPage = len(ar_pages) - 1
  2446. self.previewPage = previewPage
  2447. self.arw["previewEntry"].set_text(str(previewPage + 1))
  2448. mem = ar_pages[previewPage]
  2449. try :
  2450. mem1 = ar_pages[previewPage + 1]
  2451. except : # If we are arrived at the last page
  2452. pass
  2453. ar_pages = {}
  2454. ar_pages[0] = mem
  2455. # If previewing of two pages one over the other is requested
  2456. if 1 == 2 :
  2457. ar_pages[1] = mem1
  2458. self.ar_pages = ar_pages
  2459. self.ar_layout = ar_layout
  2460. if self.render.createNewPdf(ar_pages, ar_layout, ar_cahiers, "", previewPage) :
  2461. # force the "draw" signal to update the display
  2462. self.arw["drawingarea1"].hide()
  2463. self.arw["drawingarea1"].show()
  2464. def preview_keys(self, widget, event = None) :
  2465. print ( "==========>>", event)
  2466. def previewNext(self, dummy, event=None) :
  2467. global selected_page, selectedIndex_a
  2468. if event.state != Gdk.ModifierType.CONTROL_MASK :
  2469. selected_page = None
  2470. selectedIndex_a = {}
  2471. self.previewPage += 1
  2472. self.arw["previewEntry"].set_text(str(self.previewPage + 1))
  2473. if self.arw["automaticUpdate"].get_active() == 0 : # If automatic update is not disabled
  2474. self.preview(self.previewPage) # update the preview
  2475. def previewPrevious(self, dummy, event = None) :
  2476. global selected_page, selectedIndex_a
  2477. if event.state != Gdk.ModifierType.CONTROL_MASK :
  2478. selected_page = None
  2479. selectedIndex_a = {}
  2480. self.previewPage -= 1
  2481. if self.previewPage < 0 :
  2482. self.previewPage = 0
  2483. self.arw["previewEntry"].set_text(str(self.previewPage + 1))
  2484. if self.arw["automaticUpdate"].get_active() == 0 : # If automatic update is not disabled
  2485. self.preview(self.previewPage) # update the preview
  2486. def previewFirst(self, widget) :
  2487. global selected_page, selectedIndex_a
  2488. selected_page = None
  2489. selectedIndex_a = {}
  2490. self.previewPage = 0
  2491. self.arw["previewEntry"].set_text(str(self.previewPage + 1))
  2492. if self.arw["automaticUpdate"].get_active() == 0 : # If automatic update is not disabled
  2493. self.preview(self.previewPage) # update the preview
  2494. def previewLast(self, widget) :
  2495. global selected_page, selectedIndex_a
  2496. selected_page = None
  2497. selectedIndex_a = {}
  2498. self.previewPage = 1000000 # CreatePageLayout will substitute the right number
  2499. self.arw["previewEntry"].set_text(str(self.previewPage + 1))
  2500. if self.arw["automaticUpdate"].get_active() == 0 : # If automatic update is not disabled
  2501. self.preview(self.previewPage) # update the preview
  2502. def previewUpdate(self, Event = None, mydata = None) :
  2503. global inputFiles_a
  2504. if len(inputFiles_a) == 0 :
  2505. #showwarning(_("No file loaded"), _("Please select a file first"))
  2506. return False
  2507. if Event != None :
  2508. value_s = self.arw["entry11"].get_text()
  2509. value2_s = self.arw["entry12"].get_text()
  2510. if value_s != "" and value2_s != "" :
  2511. previewPage = self.arw["previewEntry"].get_text()
  2512. if previewPage != "" :
  2513. self.preview(int(previewPage) - 1)
  2514. return
  2515. else :
  2516. return
  2517. ## self.preview(self.previewPage)
  2518. if self.arw["automaticUpdate"].get_active() == 0 : # If automatic update is not disabled
  2519. self.preview(self.previewPage) # update the preview
  2520. def previewDelayedUpdate(self, event) : # Unused : does not work
  2521. return
  2522. print("previewUpdateDelayed")
  2523. try :
  2524. self.t1.cancel()
  2525. except :
  2526. pass
  2527. self.t1 = threading.Timer(1, self.previewUpdate, [event])
  2528. self.t1.start()
  2529. print("timer démarré")
  2530. def preview2(self, widget) :
  2531. global selected_page
  2532. selected_page = None
  2533. previewPage = int(widget.get_text())
  2534. self.previewPage = previewPage - 1
  2535. self.arw["previewEntry"].set_text(str(self.previewPage + 1))
  2536. if self.arw["automaticUpdate"].get_active() == 0 : # If automatic update is not disabled
  2537. self.preview(self.previewPage) # update the preview
  2538. def manage_backup(self) :
  2539. return
  2540. if self.backup_command == False :
  2541. self.backup_command = True
  2542. return
  2543. if len(self.backup) == 0 :
  2544. self.backup.append(copy.deepcopy(config))
  2545. self.backup_index = len(self.backup)
  2546. else :
  2547. last = self.backup[-1]
  2548. a = repr(config)
  2549. b = repr(last)
  2550. if a == b :
  2551. return
  2552. else :
  2553. self.backup.append(copy.deepcopy(config))
  2554. #print (len(self.backup))
  2555. self.backup_index = len(self.backup)
  2556. def go_back(self, widget) :
  2557. config = copy.deepcopy(self.backup[self.backup_index - 2])
  2558. self.setupGui()
  2559. self.backup_command = False
  2560. def _____________________SHUFFLER() :
  2561. pass
  2562. def runPS(self, widget=None) :
  2563. global inputFiles_a, pagesSel
  2564. if len(inputFiles_a) == 0 :
  2565. showwarning(_("No selection"), _("There is no selected file. \nPlease select a file first. "))
  2566. return
  2567. if self.shuffler == None :
  2568. self.shuffler = PdfShuffler()
  2569. self.shuffler_window = self.shuffler.uiXML.get_object('main_window')
  2570. self.shuffler.uiXML.get_object('menubar').hide()
  2571. self.shuffler.uiXML.get_object('toolbar1').hide()
  2572. #self.shuffler.uiXML.get_object('menu1_RR').hide()
  2573. #self.shuffler.uiXML.get_object('menu1_RL').hide()
  2574. self.shuffler.uiXML.get_object('menu1_crop').hide()
  2575. self.shuffler.window.set_deletable(False)
  2576. shufflerBB = self.arw['shufflerbuttonbox']
  2577. shufflerBB.unparent()
  2578. vbox = self.shuffler.uiXML.get_object('vbox1')
  2579. vbox.pack_start(shufflerBB, False, True, 0)
  2580. self.loadShuffler()
  2581. else :
  2582. self.shuffler_window.show()
  2583. def loadShuffler(self) :
  2584. render.parsePageSelection("", 0)
  2585. for key in inputFiles_a :
  2586. pdfdoc = PDF_Doc(inputFiles_a[key], self.shuffler.nfile, self.shuffler.tmp_dir)
  2587. if pdfdoc.nfile != 0 and pdfdoc != []:
  2588. self.shuffler.nfile = pdfdoc.nfile
  2589. self.shuffler.pdfqueue.append(pdfdoc)
  2590. angle=0
  2591. crop=[0.,0.,0.,0.]
  2592. for page in pagesSel :
  2593. file1, page1 = page.split(":")
  2594. npage = int(page1) + 1
  2595. filenumber = int(file1) - 1
  2596. pdfdoc = self.shuffler.pdfqueue[filenumber]
  2597. if npage > 0 :
  2598. docPage = pdfdoc.document.get_page(npage-1)
  2599. else :
  2600. docPage = pdfdoc.document.get_page(0)
  2601. w, h = docPage.get_size()
  2602. # blank page
  2603. if npage == 0 :
  2604. descriptor = 'Blank'
  2605. width = self.shuffler.iv_col_width
  2606. row =(descriptor, # 0
  2607. None, # 1
  2608. 1, # 2
  2609. -1, # 3
  2610. self.zoom_scale, # 4
  2611. "", # 5
  2612. 0, # 6
  2613. 0,0, # 7-8
  2614. 0,0, # 9-10
  2615. w,h, # 11-12
  2616. 2. ) # 13 FIXME
  2617. self.shuffler.model.append(row)
  2618. else :
  2619. descriptor = ''.join([pdfdoc.shortname, '\n', _('page'), ' ', str(npage)])
  2620. iter = self.shuffler.model.append((descriptor, # 0
  2621. None, # 1
  2622. pdfdoc.nfile, # 2
  2623. npage, # 3
  2624. self.shuffler.zoom_scale, # 4
  2625. pdfdoc.filename, # 5
  2626. angle, # 6
  2627. crop[0],crop[1], # 7-8
  2628. crop[2],crop[3], # 9-10
  2629. w,h, # 11-12
  2630. 2. )) # 13 FIXME
  2631. self.shuffler.update_geometry(iter)
  2632. res = True
  2633. self.shuffler.reset_iv_width()
  2634. if res:
  2635. self.shuffler.render()
  2636. return res
  2637. def closePS(self) :
  2638. if self.shuffler :
  2639. self.shuffler.rendering_thread.quit = True
  2640. Gdk.threads_enter()
  2641. # TODO ££
  2642. ## if self.shuffler.rendering_thread.paused == True:
  2643. ## self.shuffler.rendering_thread.evnt.set()
  2644. ## self.shuffler.rendering_thread.evnt.clear()
  2645. self.shuffler_window.destroy()
  2646. self.shuffler= None
  2647. self.shuffler
  2648. self.runPS()
  2649. def getShufflerSel(self, widget):
  2650. selection = []
  2651. for row in self.shuffler.model:
  2652. Id = str(row[2]) + ":" + str(row[3])
  2653. selection += [Id]
  2654. angle = row[7]
  2655. if angle != 0 :
  2656. if angle == 90 : # In Pdf format, global rotation rotates clockwise,
  2657. angle = 270 # fine rotation (used by PdfBooklet) rotates counterclockwise.
  2658. elif angle == -90 :
  2659. angle = 90
  2660. if not Id in config and angle != 0 :
  2661. config[Id] = {}
  2662. # defaults
  2663. config[Id]["htranslate"] = 0
  2664. config[Id]["vtranslate"] = 0
  2665. config[Id]["scale"] = 1
  2666. config[Id]["rotate"] = 0
  2667. if angle != 0 :
  2668. config[Id]["shuffler_rotate"] = angle
  2669. ## if angle == 270 :
  2670. ## config[Id]["vtranslate"] = pix_w
  2671. ## elif angle == 90 :
  2672. ## config[Id]["htranslate"] = pix_h
  2673. self.selection_s = self.compressSelection(selection)
  2674. self.shuffler_window.hide()
  2675. self.previewUpdate()
  2676. def closeShuffler(self, widget) :
  2677. self.shuffler_window.hide()
  2678. def ________________TRANSFORMATIONS() :
  2679. pass
  2680. def ta2(self, widget, event = "") :
  2681. print("ta2", event)
  2682. self.transformationsApply("")
  2683. def transformationsApply(self, widget, event="", force_update = False) :
  2684. # Reads the values in the gui and updates the config dictionary
  2685. global selected_page, selected_pages_a, rows_i
  2686. for this_selected_page in selected_pages_a :
  2687. if this_selected_page == None :
  2688. showwarning(_("No selection"), _("There is no selected page. \nPlease select a page first. "))
  2689. return
  2690. # this_selected_page 4 and 5 contain the correct page reference, including the global rotation.
  2691. humanReadableRow_i = rows_i - this_selected_page[4]
  2692. Id = str(str(humanReadableRow_i) + "," + str(this_selected_page[5] + 1))
  2693. pageId = str(this_selected_page[2]) + ":" + str(this_selected_page[3])
  2694. # if transformation is for this page only, use page ref instead of position ref
  2695. if self.thispage.get_active() == 1 :
  2696. Id = pageId
  2697. if self.evenpages.get_active() == 1 :
  2698. Id = "even"
  2699. if self.oddpages.get_active() == 1 :
  2700. Id = "odd"
  2701. config[Id] = {}
  2702. config[Id]["htranslate"] = self.arw["htranslate1"].get_text() # data from the gui unmodified
  2703. config[Id]["vtranslate"] = self.Vtranslate1.get_text()
  2704. config[Id]["scale"] = self.scale1.get_text()
  2705. config[Id]["rotate"] = self.rotation1.get_text()
  2706. config[Id]["xscale"] = self.arw["xscale1"].get_text()
  2707. config[Id]["yscale"] = self.arw["yscale1"].get_text()
  2708. config[Id]["vflip"] = self.arw["vflip1"].get_active()
  2709. config[Id]["hflip"] = self.arw["hflip1"].get_active()
  2710. #if not force_update :
  2711. # prevent useless update. If no change, return.
  2712. # TODO : à débuguer
  2713. ## a = repr(config[Id])
  2714. ## if not "memory1" in self :
  2715. ## memory1 = {}
  2716. ## b = self.memory1["memory1"]
  2717. ## if a == b :
  2718. ## return
  2719. ## else :
  2720. ## self.memory1["memory2"] = 0
  2721. ## self.memory1["memory1"] = a
  2722. ## self.memory1["memory2"] += 1
  2723. if self.arw["automaticUpdate"].get_active() == 0 : # If automatic update is not disabled
  2724. self.preview(self.previewPage) # update the preview
  2725. def resetTransformations(self, event = 0) :
  2726. self.arw["htranslate1"].set_value(0)
  2727. self.arw["vtranslate1"].set_value(0)
  2728. self.arw["scale1"].set_value(100)
  2729. self.arw["xscale1"].set_value(100)
  2730. self.arw["yscale1"].set_value(100)
  2731. self.arw["rotation1"].set_value(0)
  2732. self.arw["vflip1"].set_active(False)
  2733. self.arw["hflip1"].set_active(False)
  2734. if event != 0 : self.transformationsApply("dummy", force_update = True)
  2735. pass
  2736. def resetTransformations2(self, event = 0) :
  2737. # reset default values for global transformations
  2738. self.arw["htranslate2"].set_value(0)
  2739. self.arw["vtranslate2"].set_value(0)
  2740. self.arw["scale2"].set_value(100)
  2741. self.arw["xscale2"].set_value(100)
  2742. self.arw["yscale2"].set_value(100)
  2743. self.arw["rotation2"].set_value(0)
  2744. self.arw["vflip2"].set_active(False)
  2745. self.arw["hflip2"].set_active(False)
  2746. if event != 0 :
  2747. self.arw["globalRotation0"].set_active(1)
  2748. self.previewUpdate()
  2749. pass
  2750. def copy_transformations(self, event) : # called by context menu
  2751. self.clipboard["htranslate1"] = self.arw["htranslate1"].get_text() # data from the gui unmodified
  2752. self.clipboard["vtranslate1"] = self.Vtranslate1.get_text()
  2753. self.clipboard["scale1"] = self.scale1.get_text()
  2754. self.clipboard["rotate1"] = self.rotation1.get_text()
  2755. self.clipboard["this_page"] = self.thispage.get_active()
  2756. self.clipboard["xscale1"] = self.arw["xscale1"].get_text()
  2757. self.clipboard["yscale1"] = self.arw["yscale1"].get_text()
  2758. self.clipboard["vflip1"] = self.arw["vflip1"].get_active()
  2759. self.clipboard["hflip1"] = self.arw["hflip1"].get_active()
  2760. def paste_transformations(self, event) : # called by context menu
  2761. self.arw["htranslate1"].set_text(self.clipboard["htranslate1"])
  2762. self.Vtranslate1.set_text(self.clipboard["vtranslate1"])
  2763. self.scale1.set_text(self.clipboard["scale1"])
  2764. self.rotation1.set_text(self.clipboard["rotate1"])
  2765. self.thispage.set_active(self.clipboard["this_page"])
  2766. self.arw["xscale1"].set_text(self.clipboard["xscale1"])
  2767. self.arw["yscale1"].set_text(self.clipboard["yscale1"])
  2768. self.arw["vflip1"].set_active(self.clipboard["vflip1"])
  2769. self.arw["hflip1"].set_active(self.clipboard["hflip1"])
  2770. self.transformationsApply("", force_update = True)
  2771. def version21(self, widget) :
  2772. showwarning("Not yet implemented", "This feature will be implemented in version 2.2")
  2773. def aboutPdfbooklet(self, widget) :
  2774. self.arw["Pdf-Booklet"].show()
  2775. def aboutdialog1_close(self, widget,event) :
  2776. self.arw["Pdf-Booklet"].hide()
  2777. def print2(self, string, cr=0) : # no longer used
  2778. global editIniFile
  2779. return
  2780. editIniFile = 0
  2781. enditer = self.text1.get_end_iter()
  2782. self.text1.insert(enditer, string)
  2783. if cr == 1 :
  2784. self.text1.insert(enditer, chr(10))
  2785. iter0 = self.text1.get_end_iter()
  2786. self.arw["text1"].scroll_to_iter(iter0,0)
  2787. # TODO : bug
  2788. # This command hangs the program (the interface remains blank) when :
  2789. # a file has been loaded
  2790. # The page selector has been used
  2791. # a second file is loaded.
  2792. #while Gtk.events_pending():
  2793. # Gtk.main_iteration()
  2794. def test(self,event, dummy = None) :
  2795. print("test")
  2796. def destroyWindow() :
  2797. pass # commande encore présente dans Glade, à supprimer
  2798. def go(self, button, preview = -1) :
  2799. if self.readGui() :
  2800. if self.render.parsePageSelection() :
  2801. self.readConditions()
  2802. ar_pages, ar_layout, ar_cahiers = self.render.createPageLayout()
  2803. if ar_pages != None :
  2804. # Verify that the output file may be written to
  2805. if app.arw["entry2"].get_text() == "" :
  2806. inputFile = inputFiles_a[1]
  2807. inputFile_name = os.path.splitext(inputFile)[0]
  2808. outputFile = inputFile_name + "-bklt.pdf"
  2809. else :
  2810. outputFile = app.arw["entry2"].get_text()
  2811. inputFile = inputFiles_a[1]
  2812. inputFile_wo_ext = os.path.splitext(inputFile)[0]
  2813. (inputFile_path, inputFile_name) = os.path.split(inputFile)
  2814. (inputFile_basename, inputFile_ext) = os.path.splitext(inputFile_name)
  2815. inputFile_path += "/"
  2816. inputFile_ext = inputFile_ext[1:]
  2817. outputFile = outputFile.replace("%F", inputFile_wo_ext)
  2818. outputFile = outputFile.replace("%N", inputFile_name)
  2819. outputFile = outputFile.replace("%B", inputFile_basename)
  2820. outputFile = outputFile.replace("%P", inputFile_path)
  2821. outputFile = outputFile.replace("%E", inputFile_ext)
  2822. if preview == -1 :
  2823. if os.path.isfile(outputFile) :
  2824. if app.overwrite.get_active() == 0 :
  2825. answer_b = askyesno(_("File existing"), _("The outputfile already exists \n" \
  2826. "overWrite ? " ))
  2827. if False == answer_b :
  2828. return False
  2829. if self.render.createNewPdf(ar_pages, ar_layout, ar_cahiers, outputFile, preview) :
  2830. if self.arw["show"].get_active() == 1 :
  2831. if 'linux' in sys.platform :
  2832. subprocess.call(["xdg-open", outputFile])
  2833. else:
  2834. os.startfile(outputFile)
  2835. return [ar_pages, ar_layout, ar_cahiers]
  2836. def readConditions(self) :
  2837. global optionsDict, config
  2838. # Used by the go function above
  2839. return
  2840. if app.arw["entry3"].get() == "" :
  2841. return
  2842. else :
  2843. inifile = app.arw["entry3"].get()
  2844. config = parser.read(inifile)
  2845. optionsDict = {}
  2846. optionsDict["pages"] = {}
  2847. optionsDict["conditions"] = {}
  2848. if config.has_section("pages") :
  2849. for a in config.options("pages") :
  2850. optionsDict["pages"][a] = config["pages"][a]
  2851. if config.has_section("conditions") :
  2852. for a in config.options("conditions") :
  2853. optionsDict["conditions"][a] = config["conditions"][a]
  2854. def test1(self, widget) :
  2855. print("input")
  2856. def test2(self, widget) :
  2857. print("output")
  2858. def test3(self, widget) :
  2859. print("value_changed")
  2860. def test4(self, widget) :
  2861. print("change value")
  2862. def test5(self, widget) :
  2863. print("test5")
  2864. class pdfRender():
  2865. def transform(self, row, column, page_number, output_page_number, file_number) :
  2866. global optionsDict, config, rows_i
  2867. # init variables
  2868. V_offset = row * ury_i
  2869. H_offset = column * urx_i
  2870. cos_l = 1
  2871. sin_l = 0
  2872. transform_s = " %s %s %s %s %s %s cm \n" % (cos_l , sin_l, -sin_l, cos_l , H_offset , V_offset)
  2873. transformations = []
  2874. # Transformations defined in gui
  2875. # Transformations for page in position (row, col)
  2876. section_s = str(rows_i - row) + "," + str(column + 1)
  2877. if section_s in config :
  2878. transform_s += self.transform2(section_s)
  2879. # Transformations for page #:#
  2880. pageId = str(file_number) + ":" + str(page_number + 1)
  2881. if pageId in config :
  2882. transform_s += self.transform2(pageId)
  2883. # Debug !!!
  2884. """ Je ne comprends pas le sens de ce bloc, qui refait une deuxième fois la transformation,
  2885. si bien que quand, par exemple, on demande une transformation de 45, on obtient 90 !
  2886. ht = ini.readmmEntry(config[pageId]["htranslate"])
  2887. vt = ini.readmmEntry(config[pageId]["vtranslate"])
  2888. sc = ini.readPercentEntry(config[pageId]["scale"])
  2889. ro = ini.readNumEntry(config[pageId]["rotate"])
  2890. try :
  2891. pdfrotate = ini.readNumEntry(config[pageId]["pdfrotate"])
  2892. except :
  2893. pdfrotate = 0
  2894. xs = ini.readPercentEntry(config[pageId]["xscale"])
  2895. ys = ini.readPercentEntry(config[pageId]["yscale"])
  2896. matrix_s = self.calcMatrix2(ht, vt,
  2897. cScale = sc,
  2898. xscale = xs,
  2899. yscale = ys,
  2900. cRotate = ro,
  2901. Rotate = pdfrotate,
  2902. vflip = config[pageId]["vflip"],
  2903. hflip = config[pageId]["hflip"])
  2904. ## if "shuffler_rotate" in config[pageId] :
  2905. ## # we need the page size
  2906. ## pdfDoc = app.shuffler.pdfqueue[file_number-1]
  2907. ## page = pdfDoc.document.get_page(page_number)
  2908. ## pix_w, pix_h = page.get_size()
  2909. ##
  2910. ## angle = int(config[pageId]["shuffler_rotate"])
  2911. ## pdfrotate += angle
  2912. ## if angle == 270 :
  2913. ## vtranslate = float(vtranslate) + float(pix_w)
  2914. ## elif angle == 90 :
  2915. ## htranslate = float(htranslate) + float(pix_h)
  2916. ## elif angle == 180 :
  2917. ## htranslate = float(htranslate) + float(pix_w)
  2918. ## vtranslate = float(vtranslate) + float(pix_h)
  2919. transform_s += self.calcMatrix2(ht, vt,
  2920. cScale = sc,
  2921. cRotate = ro,
  2922. Rotate = pdfrotate)
  2923. """
  2924. # Transformations for even and odd pages
  2925. if page_number % 2 == 1 :
  2926. transform_s += self.transform2("even")
  2927. if page_number % 2 == 0 :
  2928. transform_s += self.transform2("odd")
  2929. # Transformations defined in ini file
  2930. if "pages" in config :
  2931. pages_a = config["pages"].keys()
  2932. if section_s in pages_a : # If the layout page presently treated is referenced in [pages]
  2933. temp1 = config["pages"][section_s]
  2934. transformations += temp1.split(", ")
  2935. if str(page_number + 1) in pages_a : # If the page presently treated is referenced in [pages]
  2936. temp1 = config["pages"][str(page_number + 1)]
  2937. transformations += temp1.split(", ")
  2938. if "conditions" in config :
  2939. conditions_a = config["conditions"].keys()
  2940. for line1 in conditions_a :
  2941. condition_s = config["conditions"][line1]
  2942. command_s, filters_s = condition_s.split("=>")
  2943. if (eval(command_s)) :
  2944. transformations += filters_s.split(", ")
  2945. for a in transformations :
  2946. transform_s += self.calcMatrix(a)
  2947. return (transform_s)
  2948. def transform2(self, Id) :
  2949. # Calculates matrix for a given section
  2950. matrix_s = ""
  2951. if Id in config :
  2952. if not "pdfRotate" in config[Id] :
  2953. config[Id]["pdfRotate"] = 0
  2954. if not "rotate" in config[Id] :
  2955. config[Id]["rotate"] = 0
  2956. if "shuffler_rotate" in config[Id] :
  2957. # we need the page size
  2958. pdfDoc = app.shuffler.pdfqueue[file_number-1]
  2959. page = pdfDoc.document.get_page(page_number)
  2960. pix_w, pix_h = page.get_size()
  2961. angle = int(config[Id]["shuffler_rotate"])
  2962. pdfrotate += angle
  2963. if angle == 270 :
  2964. vtranslate = float(vtranslate) + float(pix_w)
  2965. elif angle == 90 :
  2966. htranslate = float(htranslate) + float(pix_h)
  2967. elif angle == 180 :
  2968. htranslate = float(htranslate) + float(pix_w)
  2969. vtranslate = float(vtranslate) + float(pix_h)
  2970. ht = ini.readmmEntry(config[Id]["htranslate"])
  2971. vt = ini.readmmEntry(config[Id]["vtranslate"])
  2972. sc = ini.readPercentEntry(config[Id]["scale"])
  2973. ro = ini.readNumEntry(config[Id]["rotate"])
  2974. xs = ini.readPercentEntry(config[Id]["xscale"])
  2975. ys = ini.readPercentEntry(config[Id]["yscale"])
  2976. matrix_s = self.calcMatrix2(ht, vt,
  2977. cScale = sc,
  2978. xscale = xs,
  2979. yscale = ys,
  2980. cRotate = ro,
  2981. Rotate = ini.readNumEntry(config[Id]["pdfRotate"]),
  2982. vflip = ini.readBoolean(config[Id]["vflip"]),
  2983. hflip = ini.readBoolean(config[Id]["hflip"]))
  2984. return matrix_s
  2985. def calcMatrix(self, mydata, myrows_i = 1, mycolumns_i = 1) :
  2986. # Calculate matrix for transformations defined in the configuration
  2987. global config
  2988. trans = mydata.strip()
  2989. cos_l = 1
  2990. cos2_l = 1
  2991. sin_l = 0
  2992. Htranslate = 0
  2993. Vtranslate = 0
  2994. if config.has_option(trans, "PdfRotate") :
  2995. Rotate = config.getint(trans, "PdfRotate")
  2996. sin_l = math.sin(math.radians(Rotate))
  2997. cos_l = math.cos(math.radians(Rotate))
  2998. cos2_l = cos_l
  2999. if config.has_option(trans, "Rotate") :
  3000. try :
  3001. Rotate = config.getfloat(trans, "Rotate")
  3002. except :
  3003. pass
  3004. sin_l, cos_l, HCorr, VCorr = self.centeredRotation(Rotate, myrows_i, mycolumns_i)
  3005. cos2_l = cos_l
  3006. Htranslate += HCorr
  3007. Vtranslate += VCorr
  3008. if config.has_option(trans, "Scale") :
  3009. Scale_f = ini.readPercentEntry(config[trans]["Scale"])
  3010. cos_l = cos_l * Scale_f
  3011. cos2_l = cos_l
  3012. HCorr = (urx_i * mycolumns_i * (Scale_f - 1)) / 2
  3013. VCorr = (ury_i * myrows_i * (Scale_f - 1)) / 2
  3014. Htranslate -= HCorr
  3015. Vtranslate -= VCorr
  3016. if config.has_option(trans, "xscale") :
  3017. Scale_f = ini.readPercentEntry(config[trans]["xscale"])
  3018. cos_l = cos_l * Scale_f
  3019. HCorr = (urx_i * mycolumns_i * (Scale_f - 1)) / 2
  3020. Htranslate -= HCorr
  3021. if config.has_option(trans, "yScale") :
  3022. Scale_f = ini.readPercentEntry(config[trans]["yScale"])
  3023. cos2_l = cos2_l * Scale_f
  3024. VCorr = (ury_i * myrows_i * (Scale_f - 1)) / 2
  3025. Vtranslate -= VCorr
  3026. # Vertical flip : 1 0 0 -1 0 <height>
  3027. if config.has_option(trans, "vflip") :
  3028. value = config[trans]["vflip"].lower()
  3029. if value == "true" or value == "1" :
  3030. cos2_l = cos2_l * (-1)
  3031. Vtranslate += ury_i * myrows_i
  3032. # Horizontal flip : -1 0 0 1 0 <width>
  3033. if config.has_option(trans, "hflip") :
  3034. value = config[trans]["hflip"].lower()
  3035. if value == "true" or value == "1" :
  3036. cos_l = cos_l * (-1)
  3037. Htranslate += urx_i * mycolumns_i
  3038. if config.has_option(trans, "htranslate") :
  3039. ht = ini.readNumEntry(config[trans]["htranslate"])
  3040. Htranslate += ht / adobe_l
  3041. if config.has_option(trans, "vtranslate") :
  3042. vt = ini.readNumEntry(config[trans]["vtranslate"])
  3043. Vtranslate += vt / adobe_l
  3044. 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
  3045. if abs(cos_l) < 0.00001 : cos_l = 0
  3046. transform_s = " %s %s %s %s %s %s cm \n" % (cos_l , sin_l, -sin_l, cos2_l , Htranslate , Vtranslate)
  3047. if "Matrix" in config[trans] :
  3048. Matrix = config[trans]["Matrix"]
  3049. transform_s = " %s cm \n" % (Matrix)
  3050. return transform_s
  3051. def calcMatrix2(self, Htranslate, Vtranslate,
  3052. cScale = 1, Scale = 1,
  3053. Rotate = 0, cRotate = 0,
  3054. vflip = 0, hflip = 0,
  3055. xscale = 1, yscale = 1,
  3056. global_b = False) :
  3057. # calculate matrix for transformations defined in parameters
  3058. Htranslate = float(Htranslate)
  3059. Vtranslate = float(Vtranslate)
  3060. cos_l = 1
  3061. sin_l = 0
  3062. if global_b == True : # for global transformations, reference for centered scale, rotation and flip is the output page
  3063. myrows_i = rows_i
  3064. mycolumns_i = columns_i
  3065. else : # for page transformations, reference is the active source page
  3066. myrows_i = 1
  3067. mycolumns_i = 1
  3068. if Scale != 1:
  3069. Scale_f = float(Scale)
  3070. elif cScale != 1 :
  3071. Scale_f = float(cScale)
  3072. else :
  3073. Scale_f = 1
  3074. if Rotate != 0 :
  3075. sin_l = math.sin(math.radians(float(Rotate)))
  3076. cos_l = math.cos(math.radians(float(Rotate)))
  3077. # TODO Rotate and cRotate are not compatible.
  3078. elif cRotate != 0 :
  3079. sin_l, cos_l, HCorr, VCorr = self.centeredRotation(float(cRotate), myrows_i, mycolumns_i)
  3080. Htranslate += (HCorr * Scale_f)
  3081. Vtranslate += (VCorr * Scale_f)
  3082. if Scale != 1 :
  3083. sin_l = sin_l * Scale_f
  3084. cos_l = cos_l * Scale_f
  3085. HCorr = (urx_i * (Scale_f - 1)) / 2
  3086. VCorr = (ury_i * (Scale_f - 1)) / 2
  3087. if cScale != 1 :
  3088. sin_l = sin_l* Scale_f
  3089. cos_l = cos_l * Scale_f
  3090. HCorr = (urx_i * mycolumns_i * (Scale_f - 1)) / 2
  3091. VCorr = (ury_i * myrows_i * (Scale_f - 1)) / 2
  3092. Htranslate -= HCorr
  3093. Vtranslate -= VCorr
  3094. 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
  3095. if abs(cos_l) < 0.00001 : cos_l = 0
  3096. transform_s = " %s %s %s %s %s %s cm \n" % (cos_l , sin_l, -sin_l, cos_l , Htranslate , Vtranslate)
  3097. Htranslate = 0
  3098. Vtranslate = 0
  3099. cos_l = 1
  3100. cos2_l = 1
  3101. sin_l = 0
  3102. if xscale != '1' and xscale != 1 :
  3103. xscale = float(xscale)
  3104. cos_l = cos_l * xscale
  3105. HCorr = (urx_i * mycolumns_i * (xscale - 1)) / 2
  3106. Htranslate -= HCorr
  3107. if yscale != '1' and yscale != 1:
  3108. yscale = float(yscale)
  3109. cos2_l = cos2_l * yscale
  3110. VCorr = (ury_i * myrows_i * (yscale - 1)) / 2
  3111. Vtranslate -= VCorr
  3112. 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
  3113. if abs(cos_l) < 0.00001 : cos_l = 0
  3114. transform_s += " %s %s %s %s %s %s cm \n" % (cos_l , sin_l, -sin_l, cos2_l , Htranslate , Vtranslate)
  3115. Htranslate = 0
  3116. Vtranslate = 0
  3117. cos_l = 1
  3118. cos2_l = 1
  3119. sin_l = 0
  3120. # Vertical flip : 1 0 0 -1 0 <height>
  3121. if vflip != 0 and vflip != False :
  3122. cos2_l = cos2_l * (-1)
  3123. Vtranslate += ury_i * myrows_i
  3124. # Horizontal flip : -1 0 0 1 0 <width>
  3125. if hflip != 0 and hflip != False :
  3126. cos_l = cos_l * (-1)
  3127. Htranslate += urx_i * mycolumns_i
  3128. 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
  3129. if abs(cos_l) < 0.00001 : cos_l = 0
  3130. if abs(cos2_l) < 0.00001 : cos2_l = 0
  3131. transform_s += " %s %s %s %s %s %s cm \n" % (cos_l , sin_l, -sin_l, cos2_l , Htranslate , Vtranslate)
  3132. return transform_s
  3133. def centeredRotation_old(self, Rotate) :
  3134. Rotate = math.radians(Rotate)
  3135. sin_l = math.sin(Rotate)
  3136. cos_l = math.cos(Rotate)
  3137. # If a is the angle of the diagonale, and R the rotation angle, the center of the rectangle moves like this :
  3138. # Horizontal move = sin(a + R) - sin(a)
  3139. # Vertical move = cos(a + R) - cos(a)
  3140. #Hence, corrections are sin(a) - sin(a+R) and cos(a) - cos(a-R)
  3141. diag = math.pow((urx_i * urx_i) + (ury_i * ury_i), 0.5)
  3142. alpha = math.atan2(ury_i, urx_i)
  3143. S1 = math.sin(alpha)
  3144. S2 = math.sin(alpha + Rotate)
  3145. C1 = math.cos(alpha)
  3146. C2 = math.cos(alpha + Rotate)
  3147. Vcorr = (S1 - S2) * diag / 2
  3148. Hcorr = (C1 - C2) * diag / 2
  3149. return (sin_l, cos_l, Hcorr, Vcorr)
  3150. def centeredRotation(self, Rotate, myrows_i = 1, mycolumns_i = 1) :
  3151. Rotate = math.radians(Rotate)
  3152. sin_l = math.sin(Rotate)
  3153. cos_l = math.cos(Rotate)
  3154. # If a is the angle of the diagonale, and R the rotation angle, the center of the rectangle moves like this :
  3155. # Horizontal move = sin(a + R) - sin(a)
  3156. # Vertical move = cos(a + R) - cos(a)
  3157. #Hence, corrections are sin(a) - sin(a+R) and cos(a) - cos(a-R)
  3158. oWidth_i = urx_i * mycolumns_i
  3159. oHeight_i = ury_i * myrows_i
  3160. diag = math.pow((oWidth_i * oWidth_i) + (oHeight_i * oHeight_i), 0.5)
  3161. alpha = math.atan2(oHeight_i, oWidth_i)
  3162. S1 = math.sin(alpha)
  3163. S2 = math.sin(alpha + Rotate)
  3164. C1 = math.cos(alpha)
  3165. C2 = math.cos(alpha + Rotate)
  3166. Vcorr = (S1 - S2) * diag / 2
  3167. Hcorr = (C1 - C2) * diag / 2
  3168. return (sin_l, cos_l, Hcorr, Vcorr)
  3169. def CalcAutoScale(self, fileNum, page) :
  3170. global inputFiles_a, inputFile_a, refPageSize_a
  3171. fileName = inputFiles_a[fileNum]
  3172. page0 = inputFile_a[fileName].getPage(page)
  3173. llx_i=page0.mediaBox.getLowerLeft_x()
  3174. lly_i=page0.mediaBox.getLowerLeft_y()
  3175. urx_i=page0.mediaBox.getUpperRight_x()
  3176. ury_i=page0.mediaBox.getUpperRight_y()
  3177. page_width = float(urx_i) - float(llx_i)
  3178. page_height = float(ury_i) - float(lly_i)
  3179. (ref_width, ref_height) = refPageSize_a
  3180. delta1 = ref_height / page_height
  3181. delta2 = ref_width / page_width
  3182. Vdiff = ref_height - (page_height * delta2)
  3183. Hdiff = ref_width - (page_width * delta1)
  3184. # Choose the lower factor, and calculate the translation for centering the image
  3185. if delta1 < delta2 :
  3186. Scale = delta1
  3187. Vtranslate = 0
  3188. Htranslate = Hdiff/2
  3189. else:
  3190. Scale = delta2
  3191. Vtranslate = Vdiff/2
  3192. Htranslate = 0
  3193. return (Scale, Htranslate, Vtranslate)
  3194. def parsePageSelection(self, selection = "", append_prepend = 1) :
  3195. global pagesSel, totalPages, pgcount, input1, numPages, step_i, blankPages
  3196. global inputFiles_a, inputFile_a
  3197. if len(inputFiles_a) == 0 :
  3198. showwarning(_("No file loaded"), _("Please select a file first"))
  3199. return False
  3200. if selection == "" :
  3201. selection = app.selection_s
  3202. if selection.strip() == "" : # if there is no selection, then we create the default selection
  3203. # which contains all pages of all documents
  3204. i = 1
  3205. for f in inputFiles_a :
  3206. fileName = inputFiles_a[f]
  3207. numPages = inputFile_a[fileName].getNumPages()
  3208. selection += str(i) + ":1-%s;" % (numPages)
  3209. i += 1
  3210. app.selection_s = selection
  3211. if selection == "" : # This should not happen...
  3212. showwarning(_("No selection"), _("There is no selection"))
  3213. return False
  3214. selection = re.sub("[;,]{2,}", ";", selection) # we replace successive ; or , by a single ;
  3215. syntax_s = re.sub("[0-9,;:b\-\s]*", "", selection) # To verify all characters are valid, we remove all them and there should remain nothing
  3216. syntax_s = syntax_s.strip()
  3217. if syntax_s != "" : # If something remains, it is not valid
  3218. showwarning(_("Invalid data"), _("Invalid data for Selection : %s. Aborting \n") % syntax_s)
  3219. return False
  3220. if append_prepend == 1 :
  3221. pagesSel = prependPages * ["1:-1"]
  3222. else :
  3223. pagesSel = []
  3224. selection = selection.replace(";", ",")
  3225. selection = selection.strip()
  3226. if selection[-1:] == "," : # remove the trailing ,
  3227. selection = selection[0:-1]
  3228. list1 = selection.split(",")
  3229. for a in list1 :
  3230. a = a.strip()
  3231. b = a.split(":")
  3232. if (len(b) == 1) :
  3233. docId_s = "1:"
  3234. else :
  3235. docId_s = b[0] + ":"
  3236. a = b[1]
  3237. if a.count("-") > 0:
  3238. list2 = a.split("-")
  3239. serie = list(range(int(list2[0]) - 1, int(list2[1])))
  3240. for x in serie :
  3241. page_s = docId_s + str(x)
  3242. pagesSel = pagesSel + [page_s]
  3243. elif a[-1:] == "b" :
  3244. if a[0:-1].strip() == "" :
  3245. blank_pages_i = 1
  3246. else :
  3247. blank_pages_i = int(a[0:-1])
  3248. pagesSel = pagesSel + (["1:-1"] * blank_pages_i)
  3249. else :
  3250. try :
  3251. a = str(int(a) - 1)
  3252. pagesSel = pagesSel + [docId_s + a]
  3253. except :
  3254. alert("Invalid selection ", a)
  3255. if append_prepend == 1 :
  3256. pagesSel = pagesSel + appendPages * ["1:-1"]
  3257. if ini.booklet > 0 :
  3258. step_i = 2
  3259. blankPages = (len(pagesSel) % -4) * -1
  3260. #app.print2(_("Blank pages to be added : %s") % (blankPages) , 1)
  3261. else :
  3262. if step_i < 1 :
  3263. step_i = 1
  3264. blankPages = (len(pagesSel) % (step_i * -1)) * -1
  3265. pagesSel += ["1:-1"] * blankPages
  3266. totalPages = len(pagesSel)
  3267. pgcount = totalPages
  3268. return True
  3269. def createPageLayout(self, logdata = 1) :
  3270. global config, rows_i, columns_i, cells_i, step_i, sections, output, input1, adobe_l
  3271. global numfolio,prependPages, appendPages, ref_page, selection
  3272. global numPages, blankPages, pagesSel, llx_i, lly_i, urx_i, ury_i, mediabox_l, pgcount
  3273. ar_pages = {}
  3274. ar_cahiers = {}
  3275. index=0
  3276. last=0
  3277. cells_i = columns_i * rows_i
  3278. # Create booklets
  3279. if ini.booklet > 0 :
  3280. multiple_bkt = int(cells_i / 2)
  3281. ini.radioDisp= 1
  3282. folios = pgcount / 4
  3283. if numfolio == 0 : # single booklet
  3284. numcahiers = 1
  3285. else : # multiple booklets
  3286. numcahiers = int(folios / numfolio)
  3287. if (folios % numfolio > 0) :
  3288. numcahiers = numcahiers + 1
  3289. # equilibrate booklets size
  3290. minfolios = int(folios / numcahiers)
  3291. restefolios = folios - (minfolios * numcahiers)
  3292. for k in range(numcahiers) :
  3293. if (k < restefolios) : # number of folios in each booklet
  3294. ar_cahiers[k] = minfolios + 1
  3295. else :
  3296. ar_cahiers[k] = minfolios
  3297. first = last + 1 # num of first page of the booklet
  3298. last = first + (ar_cahiers[k] * 4) - 1 # num of the last page of the booklet
  3299. if logdata == 1 :
  3300. #app.print2(_( "Booklet %s : pages %s - %s") % (k + 1, first, last), 1)
  3301. pass
  3302. bkltPages = (last - first) + 1 # number of pages in the booklet
  3303. for i in range (bkltPages // 2) : # page nums for each sheet
  3304. if ((i % 2) == 0) : # Page paire à gauche
  3305. pg2 = (i + first)
  3306. pg1 = (last - i)
  3307. else :
  3308. pg1 = (i + first)
  3309. pg2 = (last - i)
  3310. ar_pages[index] = [pagesSel[pg1 - 1], pagesSel[pg2 - 1]] * multiple_bkt
  3311. index += 1
  3312. else :
  3313. while index < (pgcount / step_i) :
  3314. start = last
  3315. last = last + step_i
  3316. pages = []
  3317. for a in range(start, start + cells_i) :
  3318. PageX = start + (a % step_i)
  3319. if PageX > totalPages :
  3320. pages = pages + [-1]
  3321. else :
  3322. pages = pages + [pagesSel[PageX]]
  3323. ar_pages[index] = pages
  3324. index += 1
  3325. # create layout
  3326. ar_layout = []
  3327. if ini.radioDisp == 1 :
  3328. for r in range(rows_i) :
  3329. r2 = rows_i - (r + 1) # rows are counted from bottom because reference is lower left in pdf so we must invert
  3330. for c in range (columns_i) :
  3331. ar_layout += [[r2, c]]
  3332. elif ini.radioDisp == 2 :
  3333. for c in range(columns_i) :
  3334. for r in range(rows_i) :
  3335. r2 = rows_i - (r + 1) # rows are counted from bottom so we must invert
  3336. ar_layout += [[r2, c]]
  3337. # If option "Right to left" has been selected creates inverted layout (which will overwrite the previous one)
  3338. if bool_test(config["options"]["righttoleft"]) == True :
  3339. # create inverted layout
  3340. ar_layout = []
  3341. if ini.radioDisp == 1 :
  3342. for r in range(rows_i) :
  3343. r2 = rows_i - (r + 1) # rows are counted from bottom because reference is lower left in pdf so we must invert
  3344. for c in range (columns_i) :
  3345. c2 = columns_i - (c + 1) # Invert for right to left option
  3346. ar_layout += [[r2, c2]]
  3347. elif ini.radioDisp == 2 :
  3348. for c in range(columns_i) :
  3349. c2 = columns_i - (c + 1) # Invert for right to left optionInvert for right to left option Invert for right to left option
  3350. for r in range(rows_i) :
  3351. r2 = rows_i - (r + 1) # rows are counted from bottom so we must invert
  3352. ar_layout += [[r2, c2]]
  3353. # End of inverted layout
  3354. # User defined layout
  3355. if "radiopreset8" in app.arw and app.arw["radiopreset8"].get_active() == 1 :
  3356. # number of sheets of this layout
  3357. sheets = len(ini.imposition)
  3358. # create blank pages if necessary
  3359. rest = len(ar_pages) % sheets
  3360. if rest > 0 :
  3361. blank = []
  3362. for i in range(len(ar_pages[0])) :
  3363. blank.append(["1:-1"])
  3364. for i in range(rest) :
  3365. ar_pages[len(ar_pages) + i] = blank
  3366. # create a copy of ar_pages
  3367. ar_pages1 = {}
  3368. for key in ar_pages :
  3369. pages1 = ar_pages[key]
  3370. temp = []
  3371. for a in pages1 :
  3372. temp.append(a)
  3373. ar_pages1[key] = temp
  3374. if sheets == 1 :
  3375. userpages = ini.imposition[0]
  3376. for key in ar_pages :
  3377. pages = ar_pages[key]
  3378. pages1 = ar_pages1[key]
  3379. i = 0
  3380. # we reorder the pages following the order given in userpages
  3381. for value in userpages :
  3382. if value == "b" :
  3383. pages[i] = "1:-1"
  3384. else :
  3385. row = int(value) - 1
  3386. pages[i] = pages1[row]
  3387. i += 1
  3388. ar_pages[key] = pages
  3389. elif sheets == 2 : # more complicated...
  3390. userpagesA = app.imposition[0]
  3391. userpagesB = app.imposition[1]
  3392. step = len(userpagesA)
  3393. for key in range(0,len(ar_pages),2) :
  3394. pagesA = ar_pages[key]
  3395. pages1A = ar_pages1[key]
  3396. pagesB = ar_pages[key + 1]
  3397. pages1B = ar_pages1[key + 1]
  3398. i = 0
  3399. # we reorder the pages following the order given in userpages
  3400. for value in userpagesA :
  3401. if value == "b" :
  3402. pagesA[i] = "1:-1"
  3403. else :
  3404. row = int(value) - 1
  3405. if row < step :
  3406. index = pages1A[row]
  3407. else :
  3408. rowB = row % step
  3409. index = pages1B[rowB]
  3410. pagesA[i] = index
  3411. i += 1
  3412. i = 0
  3413. for value in userpagesB :
  3414. if value == "b" :
  3415. pagesB[i] = "1:-1"
  3416. else :
  3417. row = int(value) - 1
  3418. if row < step :
  3419. index = pages1A[row]
  3420. else :
  3421. rowB = row % step
  3422. index = pages1B[rowB]
  3423. pagesB[i] = index
  3424. i += 1
  3425. ar_pages[key] = pagesA
  3426. ar_pages[key + 1] = pagesB
  3427. return (ar_pages, ar_layout, ar_cahiers)
  3428. def createNewPdf(self, ar_pages, ar_layout, ar_cahiers, outputFile, preview = -1) :
  3429. global debug_b, inputFile_a, inputFiles_a, result
  3430. global mediabox_l
  3431. global outputStream
  3432. if debug_b == 1 :
  3433. logfile_f = open ("log.txt", "wb")
  3434. # status
  3435. statusTotal_i = len(ar_pages)
  3436. statusValue_i = 1
  3437. time_s=time.time()
  3438. output = PdfFileWriter()
  3439. if preview >= 0 : # if this is a preview
  3440. outputStream = io.BytesIO()
  3441. else :
  3442. try :
  3443. outputStream = open(outputFile, "wb")
  3444. except :
  3445. showwarning(_("File already open"), _("The output file is already opened \n" \
  3446. "probably in Adobe Reader. \n" \
  3447. "Close the file and start again"))
  3448. return
  3449. if preview >= 0 : # if this is a preview
  3450. output_page_number = preview + 1
  3451. else :
  3452. output_page_number = 1
  3453. ## # encryption
  3454. ## if app.permissions_i != -1 and app.password_s != "" : # if permissions or password were present in the file
  3455. ## output.encrypt("", app.password_s, P = app.permissions_i) # TODO : there may be two passwords (user and owner)
  3456. for a in ar_pages :
  3457. # create the output sheet
  3458. page2 = output.addBlankPage(100,100)
  3459. newSheet = page2.getObject()
  3460. newSheet[generic.NameObject("/Contents")] = generic.ArrayObject([])
  3461. newSheet.mediaBox.upperRight = mediabox_l # output page size
  3462. newSheet.cropBox.upperRight = mediabox_l
  3463. # global rotation
  3464. if ("options" in config) and ("globalRotation" in config["options"]) :
  3465. gr = config["options"]["globalRotation"]
  3466. gr_s = gr.strip()[14:]
  3467. gr_i = int(gr_s)
  3468. if gr_i > 0 :
  3469. newSheet[generic.NameObject("/Rotate")] = generic.NumberObject(gr_i)
  3470. i = 0
  3471. ar_data = []
  3472. if outputScale != 1 and app.autoscale.get_active() == 1 :
  3473. temp1 = "%s 0 0 %s 0 0 cm \n" % (str(outputScale), str(outputScale))
  3474. ar_data.append([temp1])
  3475. #Output page transformations
  3476. if "output" in config :
  3477. OHShift = ini.readmmEntry(config["output"]["htranslate"])
  3478. OVShift = ini.readmmEntry(config["output"]["vtranslate"])
  3479. OScale = ini.readPercentEntry(config["output"]["scale"])
  3480. ORotate = ini.readNumEntry(config["output"]["rotate"])
  3481. Ovflip = ini.readBoolean(config["output"]["vflip"])
  3482. Ohflip = ini.readBoolean(config["output"]["hflip"])
  3483. Oxscale = ini.readPercentEntry(config["output"]["xscale"])
  3484. if Oxscale == None : return False
  3485. Oyscale = ini.readPercentEntry(config["output"]["yscale"])
  3486. if Oyscale == None : return False
  3487. temp1 = self.calcMatrix2(OHShift, OVShift,
  3488. cScale = OScale,
  3489. cRotate = ORotate,
  3490. vflip = Ovflip,
  3491. hflip = Ohflip,
  3492. xscale = Oxscale,
  3493. yscale = Oyscale,
  3494. global_b = True)
  3495. ar_data.append([temp1])
  3496. # Transformations defined in ini file
  3497. if "pages" in config :
  3498. pages_a = config["pages"].keys()
  3499. if "@" + str(output_page_number) in pages_a : # If the output page presently treated is referenced in [pages]
  3500. temp1 = config["pages"]["@" + str(output_page_number)]
  3501. transformations = temp1.split(", ")
  3502. for name_s in transformations :
  3503. if config.has_option(name_s, "globalRotation") :
  3504. gr_s = config[name_s.strip()]["globalRotation"]
  3505. gr_i = int(gr_s)
  3506. if gr_i == 90 :
  3507. newSheet[generic.NameObject("/Rotate")] = generic.NumberObject(90)
  3508. elif gr_i == 180 :
  3509. newSheet[generic.NameObject("/Rotate")] = generic.NumberObject(180)
  3510. elif gr_i == 270 :
  3511. newSheet[generic.NameObject("/Rotate")] = generic.NumberObject(270)
  3512. else :
  3513. transform_s = self.calcMatrix(name_s, rows_i, columns_i)
  3514. ar_data.append([transform_s])
  3515. if "output_conditions" in config :
  3516. conditions_a = config["output_conditions"].keys()
  3517. for line1 in conditions_a :
  3518. condition_s = config["output_conditions"][line1]
  3519. command_s, filters_s = condition_s.split("=>")
  3520. if (eval(command_s)) :
  3521. transformations = filters_s.split(", ")
  3522. for name_s in transformations :
  3523. if "globalRotation" in config[name_s.strip()] :
  3524. gr_s = config[name_s.strip()]["globalRotation"]
  3525. gr_i = int(gr_s)
  3526. if gr_i == 90 :
  3527. newSheet[generic.NameObject("/Rotate")] = generic.NumberObject(90)
  3528. elif gr_i == 180 :
  3529. newSheet[generic.NameObject("/Rotate")] = generic.NumberObject(180)
  3530. elif gr_i == 270 :
  3531. newSheet[generic.NameObject("/Rotate")] = generic.NumberObject(270)
  3532. else :
  3533. transform_s = self.calcMatrix(name_s, rows_i, columns_i)
  3534. ar_data.append([transform_s])
  3535. oString = ""
  3536. # Ready to create the page
  3537. for r, c in ar_layout : # We are creating the page in row r and column c
  3538. data_x = []
  3539. if ar_pages[0] == [] : # system not yet initialised
  3540. return
  3541. file_number, page_number = ar_pages[a][i].split(":")
  3542. file_number = int(file_number)
  3543. page_number = int(page_number)
  3544. if (page_number < 0) :
  3545. i += 1
  3546. continue # blank page
  3547. data_x.append("q\n")
  3548. # Create the transformation matrix for the page
  3549. matrix_s = self.transform(r, c, page_number, output_page_number, file_number)
  3550. if matrix_s == False :
  3551. return False
  3552. data_x.append(matrix_s)
  3553. # Booklets : option "creep"
  3554. if ini.booklet > 0 : # This option makes sense only for booklets
  3555. creep_f1 = app.readmmEntry(app.arw["creep"])
  3556. if creep_f1 > 0 :
  3557. # calculate creep for each booklet (number of folios may vary)
  3558. for key, value in ar_cahiers.items() :
  3559. creep_f = creep_f1 / value
  3560. # reset to 0 for each booklet
  3561. # Starting page of each booklet
  3562. start_pages = [0]
  3563. mem = 0
  3564. for key, value in ar_cahiers.items() :
  3565. page_value = value * 2 # ar_cahiers gives the number of folios, not pages
  3566. start_pages.append(page_value + mem)
  3567. mem += page_value
  3568. # Calculate creep for a given page
  3569. for start_page in start_pages :
  3570. if output_page_number <= start_page :
  3571. page_in_booklet = output_page_number - start_page # page number inside a given booklet
  3572. Htrans = creep_f * (int(page_in_booklet/2) - (page_in_booklet % 2)) # increment every two pages.
  3573. # It is a bit difficult to explain...
  3574. # We must get this :
  3575. # page -5 => -3 External page
  3576. # page -4 => -2
  3577. # page -3 => -2
  3578. # page -2 => -1
  3579. # page -1 => -1
  3580. # page 0 => 0 Internal page
  3581. if c == 0 :
  3582. data_x.append(self.calcMatrix2(Htrans , 0)) # shift left
  3583. else :
  3584. data_x.append(self.calcMatrix2((Htrans * -1), 0)) # shift right
  3585. break
  3586. # scale the page to fit the output sheet, if required
  3587. if"autoscale" in config["options"]:
  3588. if ini.readBoolean(config["options"]["autoscale"]) == True :
  3589. (scaleFactor_f, Htranslate, Vtranslate) = self.CalcAutoScale(file_number, page_number)
  3590. matrix1_s = self.calcMatrix2(Htranslate, Vtranslate, Scale = scaleFactor_f)
  3591. data_x.append(matrix1_s)
  3592. file_name = inputFiles_a[file_number]
  3593. newPage = inputFile_a[file_name].getPage(page_number)
  3594. data_x.append(newPage)
  3595. # Add page number if required
  3596. # Choose font
  3597. try:
  3598. temp1 = newPage['/Resources'].getObject()
  3599. if isinstance(temp1, dict) :
  3600. temp2 = temp1['/Font'].getObject()
  3601. temp3 = temp2.keys()
  3602. if '/F1' in temp3 :
  3603. font = '/F1'
  3604. else : # we get the first font in the list
  3605. for k in temp3 :
  3606. font = k
  3607. break
  3608. except :
  3609. 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.
  3610. if app.arw["page_numbers"].get_active() == True :
  3611. font_size = ini.readIntEntry(app.arw["numbers_font_size"], default = 18)
  3612. bottom_margin = ini.readIntEntry(app.arw["numbers_bottom_margin"], default = 20)
  3613. start_from = ini.readIntEntry(app.arw["numbers_start_from"])
  3614. position = urx_i / 2
  3615. if start_from <= page_number + 1 :
  3616. 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))
  3617. data_x.append("Q\n")
  3618. if len(ini.delete_rectangle) > 0 :
  3619. (x1,y1,w1,h1) = ini.delete_rectangle
  3620. data_x.append("q n 1 1 1 rg \n") # Couleur RGB ; 1 1 1 = white; 0,0,0 = black
  3621. data_x.append(" n %d %d %d %d re f* Q\n" % (x1,y1,w1,h1)) # rectangle : x, y, width, height
  3622. ar_data.append(data_x)
  3623. i += 1
  3624. aa = urx_i
  3625. bb = ury_i
  3626. datay = []
  3627. for datax in ar_data :
  3628. datay += datax + ["\n"]
  3629. ## datay += ["q n 10 10 m 10 122 l S \n"]
  3630. ## datay += [" n 10 10 m 72 10 l S \n"]
  3631. ## datay += ["Q\n"]
  3632. if preview > 0 :
  3633. newSheet.mergePage3(datay) # never use slow mode for preview
  3634. elif ("slowMode" in app.arw
  3635. and app.arw["slowMode"].get_active() == 0) : # normal mode
  3636. newSheet.mergePage3(datay)
  3637. else : # slow mode (uses mergePage instead of mergePage3)
  3638. dataz = ""
  3639. pages = []
  3640. end_code = ""
  3641. for data2 in datay :
  3642. if not isinstance(data2, str) :
  3643. pages.append([data2, dataz])
  3644. dataz = ""
  3645. else :
  3646. dataz += data2
  3647. if not (dataz == "" or len(pages) == 0) : # skip blank pages
  3648. i = len(pages)
  3649. pages[i - 1].append(dataz)
  3650. for content in pages :
  3651. if len(content) == 3 :
  3652. end_code == content[2]
  3653. else :
  3654. end_code = ""
  3655. newSheet.mergeModifiedPage(content[0], content[1], end_code)
  3656. if ( "noCompress" in app.arw
  3657. and app.arw["noCompress"].get_active() == 0) :
  3658. newSheet.compressContentStreams()
  3659. if preview == -1 : # if we are creating a real file (not a preview)
  3660. message_s = _("Assembling pages: %s ") % (ar_pages[a])
  3661. app.print2( message_s , 1)
  3662. app.status.set_text("page " + str(statusValue_i) + " / " + str(statusTotal_i))
  3663. statusValue_i += 1
  3664. output_page_number += 1
  3665. while Gtk.events_pending():
  3666. Gtk.main_iteration()
  3667. time_e=time.time()
  3668. #app.print2(_("Total length : %s ") % (time_e - time_s), 1)
  3669. output.write(outputStream)
  3670. if preview == -1 : # if we are creating a real file (not a preview)
  3671. outputStream.close() # We must close the file, otherwise it is not possible to see it in the Reader
  3672. # TODO : after closing the reader, preview should be automatically updated.
  3673. del output
  3674. if debug_b == 1 :
  3675. logfile_f.close()
  3676. # TODO
  3677. """
  3678. if preview == -1 : # if we are creating a real file (not a preview)
  3679. if app.settings.get_active() == 1 :
  3680. app.saveProjectAs("",inputFile + ".ini")
  3681. """
  3682. return True
  3683. def printTree(curPage,out) :
  3684. #curPage = source.getPage(page)
  3685. keys_a = list(curPage.keys())
  3686. #temp1 = curPage["/Parent"].getObject()
  3687. for j in keys_a :
  3688. #if j in ["/Parent", "/Rotate", "/MediaBox", "/Type", "/Annots", "/Contents"] :
  3689. if j in ["/Parent"] :
  3690. continue
  3691. temp1 = curPage[j].getObject()
  3692. print("======> page " + str(page) + " " + j, file=out)
  3693. print(temp1, file=out)
  3694. if isinstance(temp1, dict) :
  3695. for k in temp1 :
  3696. temp2 = temp1[k].getObject()
  3697. print(str(k) + " : ", end=' ', file=out)
  3698. print(temp2, file=out)
  3699. if isinstance(temp2, dict) :
  3700. for l in temp2 :
  3701. temp3 = temp2[l].getObject()
  3702. print(str(l) + " : ", end=' ', file=out)
  3703. print(temp3, file=out)
  3704. if isinstance(temp3, dict) :
  3705. for m in temp3 :
  3706. temp4 = temp3[m].getObject()
  3707. print(str(m) + " : ", end=' ', file=out)
  3708. print(temp4, file=out)
  3709. if isinstance(temp4, dict) :
  3710. for n in temp4 :
  3711. temp5 = temp4[n].getObject()
  3712. print(str(n) + " : ", end=' ', file=out)
  3713. print(temp5, file=out)
  3714. #out.close()
  3715. def parseOptions() :
  3716. global arg_a
  3717. parser = OptionParser()
  3718. parser.add_option("-f", "--file", dest="filename",
  3719. help="write report to FILE", metavar="FILE")
  3720. parser.add_option("-r", "--rows", dest="rows_i",
  3721. help="write report to FILE", metavar="FILE")
  3722. parser.add_option("-c", "--columns", dest="columns_i",
  3723. help="write report to FILE", metavar="FILE")
  3724. parser.add_option("-n", "--numfolio", dest="numfolio",
  3725. help="write report to FILE", metavar="FILE")
  3726. parser.add_option("-b", "--booklet", dest="booklet",
  3727. help="write report to FILE", metavar="FILE")
  3728. parser.add_option("-a", "--appendPages", dest="appendPages",
  3729. help="write report to FILE", metavar="FILE")
  3730. parser.add_option("-p", "--prependPages", dest="prependPages",
  3731. help="write report to FILE", metavar="FILE")
  3732. parser.add_option("-s", "--selection", dest="selection",
  3733. help="write report to FILE", metavar="FILE")
  3734. parser.add_option("-o", "--output", dest="outputFile",
  3735. help="write report to FILE", metavar="FILE")
  3736. parser.add_option("-i", "--iniFile", dest="iniFile",
  3737. help="write report to FILE", metavar="FILE")
  3738. parser.add_option("-e", "--referencePage", dest="referencePage",
  3739. help="write report to FILE", metavar="FILE")
  3740. parser.add_option("-q", "--quiet",
  3741. action="store_false", dest="verbose", default=True,
  3742. help="don't print status messages to stdout")
  3743. (option_v, arg_a) = parser.parse_args()
  3744. ## if None != option_v.iniFile :
  3745. ## ini_s = option_v.iniFile
  3746. ## parseIniFile(ini_s)
  3747. def extractBase() :
  3748. """
  3749. extract absolute path to script
  3750. @return prog_s : absolute program path
  3751. @return pwd_s : current working dir
  3752. @return base_s : dirname of prog_s
  3753. """
  3754. # read current dir
  3755. prog_s = sys.argv[0]
  3756. pwd_s = os.path.abspath(".")
  3757. name_s = os.path.basename(prog_s)
  3758. _sep_s = '\\'
  3759. # extract program path
  3760. # if path starts with \ or x:\ absolute path
  3761. if _sep_s == prog_s[0] or \
  3762. (2 < len(prog_s) and \
  3763. ":" == prog_s[1] and
  3764. _sep_s == prog_s[2]) :
  3765. base_s = os.path.dirname(prog_s)
  3766. # if it starts with ./ , relative path
  3767. elif 1 < len(prog_s) and \
  3768. "." == prog_s[0] and \
  3769. _sep_s == prog_s[1] :
  3770. path_s = os.path.abspath(prog_s)
  3771. base_s = os.path.dirname(path_s)
  3772. # if it is in the active directory
  3773. elif os.path.exists(os.path.join(pwd_s, prog_s)) or \
  3774. os.path.exists(os.path.join(pwd_s, prog_s) + ".exe"): # Necessary if the user starts the program without the extension (maggy, without .exe)
  3775. path_s = os.path.join(pwd_s, prog_s)
  3776. base_s = os.path.dirname(path_s)
  3777. else :
  3778. tab_a = os.environ["PATH"].split(":")
  3779. limit = len(tab_a)
  3780. found = False
  3781. for scan in range(limit) :
  3782. path_s = os.path.join(tab_a[scan], prog_s)
  3783. if os.path.exists(path_s) :
  3784. base_s = os.path.dirname(path_s)
  3785. found = True
  3786. break
  3787. if not found :
  3788. raise ScriptRt("path to program is undefined")
  3789. # application base import
  3790. return(name_s, pwd_s, base_s)
  3791. def sfp(path) :
  3792. # sfp = set full path
  3793. return os.path.join(prog_path_u, path)
  3794. def sfp2(file1) :
  3795. # sfp2 = set full path, used for temporary directory
  3796. try:
  3797. return os.path.join(share_path_u, file1)
  3798. except :
  3799. time.sleep(1) # Sometimes there was a sort of conflict with another thread
  3800. return os.path.join(share_path_u, file1)
  3801. def sfp3(file1) :
  3802. # sfp3 = set full path, used for config directory
  3803. try:
  3804. return os.path.join(cfg_path_u, file1)
  3805. except :
  3806. time.sleep(1) # Sometimes there was a sort of conflict with another thread
  3807. def close_applicationx(self, widget, event=None, mydata=None):
  3808. if Gtk.main_level():
  3809. app.arw["window1"].destroy()
  3810. Gtk.main_quit()
  3811. else:
  3812. sys.exit(0)
  3813. return False
  3814. ###########################################################################
  3815. # MAIN ####################################################################
  3816. ###########################################################################
  3817. def main() :
  3818. global PdfShuffler, PDF_Doc
  3819. from pdfbooklet.pdfshuffler_g3 import PdfShuffler, PDF_Doc
  3820. global isExcept
  3821. global startup_b
  3822. global preview_b
  3823. global project_b
  3824. global openedProject_u
  3825. global areaAllocationW_i
  3826. global areaAllocationH_i
  3827. global base_a
  3828. global prog_path_u
  3829. global temp_path_u
  3830. global cfg_path_u
  3831. global share_path_u
  3832. global rows_i
  3833. global columns_i
  3834. global step_i
  3835. global outputScale
  3836. global outputStream_mem
  3837. global mem
  3838. isExcept = False
  3839. startup_b = True
  3840. preview_b = True
  3841. project_b = False
  3842. openedProject_u = ""
  3843. areaAllocationW_i = 1
  3844. areaAllocationH_i = 1
  3845. rows_i = 1
  3846. columns_i = 2
  3847. step_i = 1
  3848. outputScale = 1
  3849. outputStream_mem = ""
  3850. mem = {}
  3851. mem["update"] = time.time()
  3852. base_a = extractBase()
  3853. prog_path_u = unicode2(base_a[2])
  3854. errorLog = sys.argv[0] + ".log"
  3855. argv_a = sys.argv
  3856. sys.argv = [sys.argv[0]] # remove any parameter because they are not supported by PdfShuffler
  3857. if os.path.exists(sfp(errorLog)) :
  3858. try : # Sometimes the file may be locked
  3859. os.remove(sfp(errorLog))
  3860. except :
  3861. pass
  3862. # set directories for Linux and Windows
  3863. if 'linux' in sys.platform :
  3864. cfg_path_u = os.path.join(os.environ['HOME'], ".config", "pdfbooklet")
  3865. if os.path.isdir(cfg_path_u) == False :
  3866. os.mkdir(cfg_path_u)
  3867. share_path_u = "/usr/share/pdfbooklet"
  3868. if os.path.isdir(share_path_u) == False :
  3869. os.mkdir(share_path_u)
  3870. else:
  3871. #TODO : this is not the recommended situation.
  3872. cfg_path_u = prog_path_u
  3873. share_path_u = prog_path_u
  3874. #parseOptions()
  3875. try:
  3876. global render, app, parser, ini
  3877. global inputFiles_a
  3878. render = pdfRender()
  3879. parser = myConfigParser()
  3880. ini = TxtOnly(render)
  3881. # command line processing
  3882. if len(argv_a) > 2 and argv_a[2].strip() != "" :
  3883. arg1 = argv_a[1]
  3884. (name,ext) = os.path.splitext(arg1) # determine file type from the extension
  3885. # TODO : determine from mimetype for Linux
  3886. if ext == ".ini" :
  3887. startup_b = False
  3888. app = dummy()
  3889. ini.openProject2(arg1)
  3890. ini.output_page_size(1)
  3891. app.pagesTr = copy.deepcopy(config)
  3892. if render.parsePageSelection("1-20", 0) :
  3893. ## self.readConditions()
  3894. ar_pages, ar_layout, ar_cahiers = render.createPageLayout()
  3895. if ar_pages != None :
  3896. render.createNewPdf(ar_pages, ar_layout, ar_cahiers, "test.pdf", -1)
  3897. return True
  3898. settings = Gtk.Settings.get_default()
  3899. settings.props.gtk_button_images = True
  3900. app = gtkGui(render)
  3901. app.guiPresetsShow("booklet")
  3902. app.resetTransformations(0)
  3903. app.resetTransformations2(0)
  3904. app.guiPresets(0)
  3905. if os.path.isfile(sfp3("pdfbooklet.cfg")) == False :
  3906. f1 = open(sfp3("pdfbooklet.cfg"), "w")
  3907. f1.close()
  3908. ini.parseIniFile(sfp3("pdfbooklet.cfg"))
  3909. if len(argv_a) > 1 : # a filename has been added in the command line
  3910. if len(argv_a[1]) > 0 : # this is not an empty string
  3911. arg1 = argv_a[1]
  3912. (name,ext) = os.path.splitext(arg1) # determine file type from the extension
  3913. # TODO : determine from mimetype for Linux
  3914. if ext == ".ini" : # If it is a project
  3915. startup_b = False
  3916. if not ini.openProject2(arg1) : # if file not found
  3917. print ("Unknown file on the command line : " + arg1)
  3918. else :
  3919. app.arw["previewEntry"].set_text("1")
  3920. while Gtk.events_pending():
  3921. Gtk.main_iteration()
  3922. app.previewUpdate()
  3923. if len(argv_a) > 2 :
  3924. if argv_a[2].strip() == "-r" :
  3925. app.go("") # TODO : should be go("", 0) but this creates errors
  3926. app.close_application("")
  3927. elif ext == ".pdf" :
  3928. inputFiles_a = {}
  3929. inputFiles_a[1] = argv_a[1]
  3930. ini.loadPdfFiles()
  3931. app.selection_s = ""
  3932. app.arw["previewEntry"].set_text("1")
  3933. app.previewUpdate()
  3934. else :
  3935. print("Unknown file type in the command line")
  3936. startup_b = False
  3937. ## Gdk.threads_init()
  3938. ## Gdk.threads_enter()
  3939. Gtk.main()
  3940. ## Gdk.threads_leave()
  3941. ## os._exit(0) # required because pdf-shuffler does not close correctly
  3942. except :
  3943. isExcept = True
  3944. excMsg_s = "unexpected exception"
  3945. (excType, excValue, excTb) = sys.exc_info()
  3946. tb_a = traceback.format_exception(excType, excValue, excTb)
  3947. for a in tb_a :
  3948. print(a)
  3949. # handle eventual exception
  3950. if isExcept :
  3951. ## if app.shuffler != None :
  3952. ## app.shuffler.window.destroy()
  3953. if Gtk.main_level():
  3954. ## Gtk.gdk.threads_enter()
  3955. Gtk.main_quit()
  3956. ## Gtk.gdk.threads_leave()
  3957. #os._exit(0)
  3958. sys.exit(1)
  3959. else:
  3960. sys.exit(1)
  3961. if __name__ == '__main__' :
  3962. main()