
Ignore-this: 1be68eaca162ca6f2ae2e3b9c10843c6 Bon d'accord, pdfinfo est vraiment intransigent sur le format des pages ... darcs-hash:20111121141735-28565-b2975fab2e460e2465e0e7ec6b3b1f3ef58ff154.gz
520 lines
18 KiB
Python
Executable file
520 lines
18 KiB
Python
Executable file
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
# #############################################################
|
||
# ..
|
||
# .... ............ ........
|
||
# . ....... . .... ..
|
||
# . ... .. .. .. .. ..... . ..
|
||
# .. .. ....@@@. .. . ........ .
|
||
# .. . .. ..@.@@..@@. .@@@@@@@ @@@@@@. ....
|
||
# .@@@@. .@@@@. .@@@@..@@.@@..@@@..@@@..@@@@.... ....
|
||
# @@@@... .@@@.. @@ @@ .@..@@..@@...@@@. .@@@@@. ..
|
||
# .@@@.. . @@@. @@.@@..@@.@@..@@@ @@ .@@@@@@.. .....
|
||
# ...@@@.... @@@ .@@.......... ........ ..... ..
|
||
# . ..@@@@.. . .@@@@. .. ....... . .............
|
||
# . .. .... .. .. . ... ....
|
||
# . . .... ............. .. ...
|
||
# .. .. ... ........ ... ...
|
||
# ................................
|
||
#
|
||
# #############################################################
|
||
# impression_canon.py
|
||
#
|
||
# Classe impression pour l'imprimante canon iR C3580i
|
||
#
|
||
# Copyright (c) 2006, 2007, 2008, 2009 by Cr@ns (http://www.crans.org)
|
||
# #############################################################
|
||
"""
|
||
Classe pour gérer l'envoie de pdf à l'imprimante.
|
||
Calcule le coût des options d'impression.
|
||
"""
|
||
__version__ = '9.11'
|
||
|
||
import sys, os.path
|
||
sys.path.append('/usr/scripts/gestion')
|
||
from config import impression as config_impression
|
||
from commands import getstatusoutput
|
||
import crans.utils.logs
|
||
from subprocess import Popen, PIPE
|
||
|
||
# ######################################################## #
|
||
# CONSTANTES #
|
||
# ######################################################## #
|
||
|
||
FICHIER_LOG = "/var/log/log_couts/impressions"
|
||
|
||
SNMP_CAPA_B = "mib-2.43.11.1.1.8.1.1"
|
||
SNMP_CAPA_C = "mib-2.43.11.1.1.8.1.2"
|
||
SNMP_CAPA_M = "mib-2.43.11.1.1.8.1.3"
|
||
SNMP_CAPA_Y = "mib-2.43.11.1.1.8.1.4"
|
||
SNMP_TON_B = "mib-2.43.11.1.1.9.1.1"
|
||
SNMP_TON_C = "mib-2.43.11.1.1.9.1.2"
|
||
SNMP_TON_M = "mib-2.43.11.1.1.9.1.3"
|
||
SNMP_TON_Y = "mib-2.43.11.1.1.9.1.4"
|
||
SNMP_BAC1 = "mib-2.43.8.2.1.10.1.2"
|
||
SNMP_BAC2 = "mib-2.43.8.2.1.10.1.3"
|
||
SNMP_BAC3 = "mib-2.43.8.2.1.10.1.4"
|
||
SNMP_BAC4 = "mib-2.43.8.2.1.10.1.5"
|
||
SNMP_COUNT_A4 = "enterprises.1602.1.11.1.4.1.4.113"
|
||
SNMP_COUNT_A3 = "enterprises.1602.1.11.1.4.1.4.112"
|
||
SNMP_COUNT_A4c = "enterprises.1602.1.11.1.4.1.4.123"
|
||
SNMP_COUNT_A3c = "enterprises.1602.1.11.1.4.1.4.122"
|
||
SNMP_COUNT_TOT = "enterprises.1602.1.11.1.4.1.4.101"
|
||
SNMP_ETAT = "hrPrinterStatus.1"
|
||
SNMP_ERR = "hrPrinterDetectedErrorState.1"
|
||
|
||
DECOUVERT_AUTHORISE = config_impression.decouvert
|
||
|
||
DICT_AGRAFAGE = { "None" : "aucune agrafe",
|
||
"TopLeft" : u"agrafe en haut à gauche",
|
||
"TopRight" : u"agrafe en haut à droite",
|
||
"BottomLeft" : u"agrafe en bas à gauche",
|
||
"BottomRight" : u"agrafe en bas à droite",
|
||
"Left": u"deux agrafes sur le bord gauche",
|
||
"Right" : u"deux agrafes sur le bord droit",
|
||
"Top" : u"deux agrafes sur le bord supérieur",
|
||
"Bottom" : u"deux agrafes sur le bord inférieur" }
|
||
|
||
AVAIL_AGRAFES = ["None", "TopLeft", "TopRight", "Left", "BottomLeft", "BottomRight", "Right"]
|
||
|
||
DICT_PAPIER = { 'A4' : "Papier A4 ordinaire",
|
||
'A3' : "Papier A3 ordinaire",
|
||
'A4tr' : "Transparent A4" }
|
||
|
||
# ######################################################## #
|
||
# ERREURS #
|
||
# ######################################################## #
|
||
#
|
||
|
||
class FichierInvalide(Exception):
|
||
"""
|
||
Exception renvoyée lorsqu'un fichier ne passe pas.
|
||
utilisée avec deux arguments : une chaîne décrivant l'erreur et une chaîne avec le nom du fichier
|
||
"""
|
||
def __str__(self):
|
||
"""
|
||
Description de l'erreur.
|
||
"""
|
||
return self.args[0]
|
||
def file(self):
|
||
"""
|
||
Nom du fichier qui pose problème
|
||
"""
|
||
try:
|
||
return self.args[1]
|
||
except:
|
||
return "n/a"
|
||
|
||
class SoldeInsuffisant(Exception):
|
||
"""
|
||
Solde insuffisant pour l'impression demandée
|
||
"""
|
||
pass
|
||
class PrintError(Exception):
|
||
"""
|
||
Erreur lors de l'impression
|
||
"""
|
||
pass
|
||
class SettingsError(Exception):
|
||
"""
|
||
Erreur de paramètres.
|
||
"""
|
||
pass
|
||
|
||
# ######################################################## #
|
||
# CLASSE IMPRESSION #
|
||
# ######################################################## #
|
||
#
|
||
#
|
||
class impression:
|
||
"""impression
|
||
|
||
Un objet impression correspond à un fichier pdf et un adhérent.
|
||
"""
|
||
# fichier (chemin)
|
||
_fichier = ""
|
||
# adherent (instance)
|
||
_adh = None
|
||
# paramettres
|
||
_settings = {
|
||
'agrafage': 'None',
|
||
'papier': 'A4',
|
||
'couleur': False,
|
||
'recto_verso': False,
|
||
'livret': False,
|
||
'copies': 1,
|
||
'portrait': True,
|
||
}
|
||
# le prix de l'impression
|
||
_prix = 0.0
|
||
_pages = 0
|
||
# le cout de base encre pour une impression en couleurs/n&b
|
||
# (prix pour papier A4)
|
||
_base_prix_nb = 0.0
|
||
_base_prix_couleurs = 0.0
|
||
|
||
# Format du pdf, tout droit sorti de pdfinfo
|
||
# (les dimensions sont donc en pts)
|
||
_format = '(A4)'
|
||
_width = 595.28
|
||
_height = 841.89
|
||
|
||
def __init__(self, path_to_pdf, adh = None):
|
||
"""impression(path_to_pdf [, adh])
|
||
|
||
Crée un nouvel objet impression à partir du fichier pdf pointé
|
||
par path_to_pdf. Si adh ext donné, il peut être soit une
|
||
instance d'un objet adhérent de crans_ldap soit le login de
|
||
l'adhérent. Lève l'exception FichierInvalide si le fichier
|
||
n'existe pas ou si ce n'est pas un pdf.
|
||
"""
|
||
|
||
# On ouvre les logs
|
||
self.log = crans.utils.logs.getFileLogger('impression')
|
||
|
||
self._fichier = path_to_pdf
|
||
self._adh = adh
|
||
|
||
# on verifie que le fichier existe
|
||
if not os.path.isfile(path_to_pdf):
|
||
raise FichierInvalide, ("Fichier introuvable", path_to_pdf)
|
||
if not open(path_to_pdf).read().startswith("%PDF"):
|
||
raise FichierInvalide, ("Le fichier ne semble pas etre un PDF", path_to_pdf)
|
||
|
||
# on compte les pages et on regarde le format
|
||
pdfinfo = Popen(["pdfinfo",self._fichier],stdout=PIPE,stderr=PIPE).communicate()
|
||
if pdfinfo[1] <> '':
|
||
raise FichierInvalide(u"pdfinfo n'arrive pas a lire le fichier (il est peut-etre corrompu ou protege par un mot de passe)",path_to_pdf)
|
||
self._pages = -1
|
||
for line in pdfinfo[0].split('\n'):
|
||
if line.startswith('Pages'):
|
||
self._pages = int(line.split()[1])
|
||
elif line.startswith('Page size'):
|
||
size = line.split()
|
||
if len(size) <= 6:
|
||
self._format = "Unknown"
|
||
else:
|
||
self._format = size[6]
|
||
|
||
self._width = float(size[2])
|
||
self._height = float(size[4])
|
||
# Hack pour mieux reconnaître les formats
|
||
w = min(self._width,self._height)
|
||
h = max(self._width,self._height)
|
||
err = 20
|
||
if abs(w - 595) < err and abs(h - 842) < err:
|
||
self._format = "(A4)"
|
||
elif abs(w - 842) < err and abs(h - 1180) < err:
|
||
self._format = "(A3)"
|
||
if self._pages <= 0:
|
||
raise FichierInvalide(u"Impossible de lire le nombre de pages",path_to_pdf)
|
||
|
||
if not self._format in ['(A4)','(A3)']:
|
||
raise FichierInvalide, u"Seuls les formats A3 et A4 sont supportes"
|
||
# calcule le prix de l'encre tout de suite
|
||
self._calcule_prix()
|
||
|
||
|
||
def _pdfbook(self):
|
||
if self._settings['papier'] == 'A3':
|
||
newfile = self._fichier[:-4] + '-a3book.pdf'
|
||
pdfbook = "pdfbook --paper a3paper %%s --outfile %s" % newfile
|
||
else:
|
||
newfile = self._fichier[:-4] + '-book.pdf'
|
||
pdfbook = "pdfbook %%s --outfile %s" % newfile
|
||
(status, rep) = getstatusoutput(pdfbook % self._fichier)
|
||
self.log.info("%s | rep: %s" % ((pdfbook % self._fichier), rep))
|
||
self._fichier = newfile
|
||
if status != 0:
|
||
self.log.error("pdfbook status:%d | rep: %s" % (status, rep))
|
||
raise FichierInvalide, ("pdfbook: Impossible de convertir le fichier",
|
||
self._fichier)
|
||
|
||
|
||
def changeSettings(self, **kw):
|
||
"""changeSettings([keyword=value])
|
||
|
||
Change les parametres de l'impression, recalcule et renvoie le nouveau prix.
|
||
Lève une exceotion SettingError si les paramètres son invalides.
|
||
"""
|
||
#recalcule et renvoie le prix
|
||
|
||
couleur = kw.get('couleur', None)
|
||
if couleur in [True, False]:
|
||
self._settings['couleur'] = couleur
|
||
elif couleur == "True":
|
||
self._settings['couleur'] = True
|
||
elif couleur == "False":
|
||
self._settings['couleur'] = False
|
||
|
||
try:
|
||
if int(kw['copies']) >= 1:
|
||
self._settings['copies'] = int(kw['copies'])
|
||
except:
|
||
pass
|
||
|
||
recto_verso = kw.get('recto_verso', None)
|
||
if recto_verso == "True": recto_verso = True
|
||
if recto_verso == "False": recto_verso = False
|
||
if recto_verso in [True, False]:
|
||
self._settings['recto_verso'] = recto_verso
|
||
|
||
papier = kw.get('papier', None)
|
||
if papier in ['A4', 'A3', 'A4tr']:
|
||
self._settings['papier'] = papier
|
||
if papier == 'A4tr':
|
||
self._settings['recto_verso'] = False
|
||
self._settings['agrafage'] = 'None'
|
||
|
||
agrafage = kw.get('agrafage', None)
|
||
if agrafage in ["None", "TopLeft", "Top", "TopRight",
|
||
"Left", "Right", "BottomLeft", "BottomRight"]:
|
||
self._settings['agrafage'] = agrafage
|
||
|
||
livret = kw.get('livret', None)
|
||
if livret == "True": livret = True
|
||
if livret == "False": livret = False
|
||
if livret in [True, False]:
|
||
self._settings['livret'] = livret
|
||
if livret:
|
||
self._settings['portrait'] = True #Le mode paysage est géré par _pdfbook
|
||
self._settings['recto_verso'] = True
|
||
self._settings['agrafage'] = 'None'
|
||
if self._settings['papier'] == 'A4tr':
|
||
self._settings['papier'] = 'A4'
|
||
else:
|
||
self._settings['portrait'] = self._width < self._height
|
||
|
||
return self._calcule_prix()
|
||
|
||
def printSettings(self):
|
||
"""printSettings()
|
||
|
||
Affiche les paramètres courrants sur la sortie standard
|
||
"""
|
||
if self._settings['couleur']:
|
||
print "Type impression: Couleur"
|
||
else:
|
||
print "Type impression: Noir et blanc"
|
||
|
||
print "Papier: " + DICT_PAPIER[self._settings['papier']]
|
||
|
||
if self._settings['livret']:
|
||
print u"Agrafage: Livret (piqûre à cheval)"
|
||
else:
|
||
print "Agrafage: " + DICT_AGRAFAGE[self._settings['agrafage']]
|
||
if self._settings['recto_verso']:
|
||
print "Disposition: recto/verso"
|
||
else:
|
||
print "Disposition: recto"
|
||
|
||
print "Copies: " + str(self._settings['copies'])
|
||
|
||
|
||
def prix(self):
|
||
"""prix()
|
||
|
||
Renvoie le prix courrant de l'impression
|
||
"""
|
||
return self._prix
|
||
|
||
def fileName(self):
|
||
"""fileName()
|
||
|
||
renvoie le nom du fichier pdf (exemple : monPdf.pdf)
|
||
"""
|
||
return os.path.basename(self._fichier)
|
||
|
||
def filePath(self):
|
||
"""filePath()
|
||
|
||
renvoie le chemin d'accès au fichier pdf.
|
||
"""
|
||
return self._fichier
|
||
|
||
def pages(self):
|
||
"""pages()
|
||
|
||
renvoie le nombre de pages du document (page au sens nombre de
|
||
faces à imprimer et non le nombre de feuilles)
|
||
"""
|
||
return self._pages
|
||
|
||
|
||
def imprime(self):
|
||
"""imprime()
|
||
|
||
imprime le document pdf. débite l'adhérent si adhérent il y a.
|
||
(si il a été indiqué à l'initialisation de l'objet)
|
||
"""
|
||
# debite l'adhérent si adherent il y a
|
||
if (self._adh != None):
|
||
adh = self._get_adh(self._adh)
|
||
if (self._prix > (adh.solde() - DECOUVERT_AUTHORISE)):
|
||
raise SoldeInsuffisant
|
||
adh.solde(-self._prix, "impression: " + self._fichier)
|
||
adh.save()
|
||
del adh
|
||
# imprime le document
|
||
self._exec_imprime()
|
||
|
||
|
||
def _calcule_prix(self):
|
||
|
||
faces = self._pages
|
||
|
||
if self._settings['livret']:
|
||
feuilles = int((faces+3)/4)
|
||
faces = 2 * feuilles
|
||
elif self._settings['recto_verso']:
|
||
feuilles = int(faces/2.+0.5)
|
||
else:
|
||
feuilles = faces
|
||
|
||
if (self._settings['papier'] == "A3"):
|
||
c_papier = config_impression.c_a3
|
||
pages = 2*faces
|
||
else:
|
||
pages = faces
|
||
if self._settings['papier'] == "A4tr":
|
||
c_papier = config_impression.c_trans
|
||
else:
|
||
c_papier = config_impression.c_a4
|
||
|
||
if self._settings['couleur']:
|
||
c_impression = c_papier * feuilles + config_impression.c_face_couleur * pages
|
||
else:
|
||
c_impression = c_papier * feuilles + config_impression.c_face_nb * pages
|
||
|
||
# Cout des agrafes
|
||
if self._settings['agrafage'] in ["Top", "Bottom", "Left", "Right"] or self._settings['livret']:
|
||
nb_agrafes = 2
|
||
elif self._settings['agrafage'] in ["None", None]:
|
||
nb_agrafes = 0
|
||
else:
|
||
nb_agrafes = 1
|
||
|
||
if feuilles <= 50:
|
||
c_agrafes = nb_agrafes * config_impression.c_agrafe
|
||
else:
|
||
c_agrafes = 0
|
||
|
||
c_total = int(self._settings['copies'] * ( c_impression +
|
||
c_agrafes ) + 0.5) # arrondi et facture
|
||
|
||
self._prix = float(c_total)/100
|
||
return self._prix
|
||
|
||
def _get_adh(self, adh):
|
||
if type(adh) == str:
|
||
sys.path.append("/usr/scripts/gestion/")
|
||
#from ldap_crans_test import crans_ldap
|
||
from ldap_crans import CransLdap
|
||
adh = CransLdap().getProprio(adh, 'w')
|
||
return adh
|
||
|
||
## ################################# ##
|
||
## fonction qui imprime pour de vrai ##
|
||
## ################################# ##
|
||
##
|
||
def _exec_imprime(self):
|
||
""" Envoie l'impression a l'imprimante avec les parametres actuels """
|
||
|
||
if self._settings['livret']:
|
||
self._pdfbook()
|
||
|
||
if (self._adh != None):
|
||
self.log.info('Impression [%s] : %s' % (self._adh, self._fichier))
|
||
else:
|
||
self.log.info("Impression : %s" % self._fichier)
|
||
|
||
# Envoi du fichier à CUPS
|
||
options = ''
|
||
# Création de la liste d'options
|
||
# pour le nombre de copies et specifie non assemblee
|
||
#options += '-# %d -o Collate=True' % self.nb_copies
|
||
|
||
# Pour spécifier l'imprimante
|
||
options += ' -P canon_irc3580'
|
||
|
||
# Pour spécifier un jobname de la forme adh:nom_du_fichier
|
||
jobname = '%s:%s' % (self._adh, self._fichier.split('/')[-1].replace("\"","\\\""))
|
||
# Ce nom apparaît sur l'interface d'impression de l'imprimante:
|
||
options += " -o CNDocName=\"%s\"" %jobname
|
||
|
||
# Et dans lpq:
|
||
options += " -T \"%s\"" % jobname
|
||
|
||
# Pour donner le login de l'adherent
|
||
options += ' -U \"%s\"' % self._adh
|
||
|
||
# Pour spécifier la version du language postscript utilisé par pdftops
|
||
# options += ' -o pdf-level3'
|
||
|
||
# Pour demander une page de garde
|
||
#options += ' -o job-sheets=crans' #page de garde de type standard
|
||
#options += " -o job-billing=%.2f" % self.cout
|
||
#options += ' -o job-sheets=none'
|
||
|
||
#Indique la présence d'un bac de sortie avec agrafeuse
|
||
# options += " -o Option20=MBMStaplerStacker -o OutputBin=StackerDown"
|
||
|
||
if self._settings['papier'] == 'A4tr':
|
||
options += ' -o InputSlot=SideDeck -o MediaType=OHP'
|
||
options += ' -o PageSize=A4'
|
||
elif self._settings['papier'] == 'A4':
|
||
options += ' -o PageSize=A4'
|
||
else:
|
||
options += ' -o pdf-expand -o pdf-paper=841x1190 -o PageSize=A3'
|
||
|
||
if self._settings['portrait']:
|
||
if self._settings['recto_verso']:
|
||
options += ' -o sides=two-sided-long-edge'
|
||
else:
|
||
options += ' -o sides=one-sided'
|
||
else:
|
||
if self._settings['recto_verso']:
|
||
options += ' -o sides=two-sided-short-edge -o landscape'
|
||
else:
|
||
options += ' -o sides=one-sided -o landscape'
|
||
if self._settings['couleur']:
|
||
options += ' -o CNColorMode=color'
|
||
else:
|
||
options += ' -o CNColorMode=mono'
|
||
|
||
if self._settings['livret']:
|
||
options += ' -o CNSaddleStitch=True'
|
||
options += ' -o OutputBin=TrayC'
|
||
else:
|
||
options += ' -o OutputBin=TrayA'
|
||
options += ' -o Collate=StapleCollate -o StapleLocation=%s' % self._settings['agrafage']
|
||
|
||
|
||
if not self._settings['livret'] and self._settings['agrafage'] in ['None', None]:
|
||
left = self._settings['copies']
|
||
while left >= 100:
|
||
cmd = "lpr %s -# %d %s" % (options, 99, self._fichier)
|
||
left -= 99
|
||
(status, rep) = getstatusoutput(cmd)
|
||
self.log.info("printing: %s" % cmd)
|
||
if status != 0:
|
||
self.log.error("erreur impression")
|
||
self.log.error("lpr status:%d | rep: %s" % (status, rep))
|
||
raise PrintError, "%s \n status:%d rep: %s" % (cmd, status, rep)
|
||
cmd = "lpr %s -# %d %s" % (options, left, self._fichier)
|
||
(status, rep) = getstatusoutput(cmd)
|
||
self.log.info("printing: %s" % cmd)
|
||
if status != 0:
|
||
self.log.error("erreur impression")
|
||
self.log.error("lpr status:%d | rep: %s" % (status, rep))
|
||
raise PrintError, "%s \n status:%d rep: %s" % (cmd, status, rep)
|
||
|
||
else:
|
||
cmd = "lpr %s %s" % (options, self._fichier)
|
||
self.log.info("printing [x%s]: %s" % (cmd, self._settings['copies']))
|
||
for i in range(self._settings['copies']):
|
||
(status, rep) = getstatusoutput(cmd)
|
||
if status != 0:
|
||
self.log.error("erreur impression")
|
||
self.log.error("lpr status:%d | rep: %s" % (status, rep))
|
||
raise PrintError, "%s \n status:%d rep: %s" % (cmd, status, rep)
|