
Ignore-this: 17421cadc44a40cbb153f7ae8020023a ou alors on met une très forte tolérance, car il y a vraiment des pdf qui se prennent pour du A4 sans être reconnus comme tels par pdfinfo bref ça fait chier plus de gens qu'autre chose. Je pense qu'il vaudrait mieux mettre seulement un warning, mais c'est plus compliqué à coder (y penser pour la nouvelle interface.) darcs-hash:20120621091124-28565-a7ee11be5008d2ceede65d19994a501808170478.gz
552 lines
19 KiB
Python
Executable file
552 lines
19 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
|
||
def _uniq_jid():
|
||
""" Alloue un jid unique """
|
||
fname = '/var/impression/fichiers/jid'
|
||
## Maybe need a lock ?
|
||
f = file(fname,'r+')
|
||
cur = int(f.read())+1
|
||
f.seek(0)
|
||
f.write(str(cur))
|
||
f.close()
|
||
return cur
|
||
|
||
# ######################################################## #
|
||
# 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
|
||
|
||
# Jid unique, à définir avant l'impression
|
||
_jid = 0
|
||
|
||
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 = 100
|
||
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):
|
||
page = "pdfinfo \"%s\" | grep Pages | awk '{print $2}'" % self._fichier
|
||
(status, npage) = getstatusoutput(page)
|
||
if status != 0:
|
||
self.log.error("pdfinfo status:%d | rep: %s" % (status, npage))
|
||
raise FichierInvalide, ("pdfinfo: Impossible de trouver le nombre de page du fichier",
|
||
self._fichier)
|
||
if int(int(npage)/4*4) < int(npage):
|
||
sig=int((int(npage)/4 +1)*4)
|
||
else:
|
||
sig=int(npage)
|
||
if self._settings['papier'] == 'A3':
|
||
newfile = self._fichier[:-4] + '-a3book.pdf'
|
||
pdfbook = "pdfbook --signature %s --paper a3paper %%s --outfile %s" % (sig,newfile)
|
||
else:
|
||
newfile = self._fichier[:-4] + '-book.pdf'
|
||
pdfbook = "pdfbook --signature %s %%s --outfile %s" % (sig,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)
|
||
"""
|
||
self._jid = _uniq_jid()
|
||
|
||
# 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(%d): %s" % (self._jid,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(%d) [%s] : %s' % (self._jid, self._adh, self._fichier))
|
||
else:
|
||
self.log.info("Impression(%d) : %s" % (self._jid, 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
|
||
if self._adh <> 'passoir':
|
||
options += ' -P canon_irc3580'
|
||
else:
|
||
options += ' -P canon_test'
|
||
|
||
|
||
# Pour spécifier un jobname de la forme jid:adh:nom_du_fichier
|
||
jobname = '%d:%s:%s' % (self._jid, 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)
|
||
|