"Después del juego es antes del juego"
Sepp Herberger

martes, 21 de abril de 2015

Forzar la impresión en escala de grises en una impresora en color usando tea4cups.

Recientemente hemos adquirido una impresora Brother DCP-9020CDW. Mi intención era definir sobre ella dos colas, que se comportarían como dos impresoras virtuales: una para imprimir en blanco y negro y la otra para imprimir en color. La causa es que si dejo una sola cola, seguramente la mayoría de las veces la impresión será en color por simple descuido o dejadez a la hora de configurar las propiedades del trabajo de impresión, con el consiguiente desperdicio de dinero que necesitamos para pagar el billón de euros de deuda pública.

La primera idea fue manipular el fichero .ppd, haciendo una versión del mismo que dijese que la impresora era en blanco y negro, y usar dicho fichero .ppd para dar de alta la impresora correspondiente. Pues no funcionó: si se manda imprimir en color desde un puesto el trabajo salía en color independientemente de lo que dijese el .ppd que estaba permitdo.

No me quedaba otra que usar tea4cups, esa estupenda navaja suiza para las colas de impresión. Descargamos el fichero de http://www.pykota.com/software/tea4cups/download, lo descomprimimos e instalamos cada cosa en su sitio:

  • El fichero tea4cups en el directorio de backends de impresión: /usr/lib/cups/backend/
  • El fichero tea4cups.conf en el directorio de configuración de cups: /etc/cups/..

Una vez hecho esto, modificamos tea4cups.conf según nuestras necesidades. El fichero está profusamente autodocumentado con varios ejemplos de todas las virguerías que se pueden hacer con las colas de impresión, yo lo he modificado mínimamente añadiendo al original las líneas en rojo:

server:~# cat /etc/cups/tea4cups.conf 
# $Id: tea4cups.conf 120 2006-08-10 21:42:16Z jerome $
#
# Tea4CUPS : Tee for CUPS
.......
[global]
......
debug : yes
........
directory : /var/spool/cups/
.........
prehook_accounting : /root/scripts/seguimiento_impresion
..................................
...................
# posthook_dialog2 : cat >/tmp/result2

#Se incluye un filtro para la cola "BN_SALAPROFESORES" que convierte a BN los trabajos de impresión
#mandados a color, modificando el .prn bruto antes de ser procesado.
[BN_SALAPROFESORES]
filter : sed "0,/@PJL SET RENDERMODE=COLOR/{s/@PJL SET RENDERMODE=COLOR/@PJL SET RENDERMODE=GRAYSCALE/}"

El fichero /root/scripts/seguimiento_impresión del prehook_accounting será el script que se ejecutará cada vez que se mande un trabajo a cualquier impresora de CUPS. En mi caso lo uso para llevar una contabilidad de los trabajos de impresión y para otras tareas de depuración. Su contenido es:

server:~# cat /root/scripts/seguimiento_impresion
#!/bin/bash

function valid_ip()
{
local ip=$1
local stat=1

if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
OIFS=$IFS
IFS='.'
ip=($ip)
IFS=$OIFS
[[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
&& ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
stat=$?
fi
return $stat
}


#URLEncode para el titulo del documento
TEATITLE=$(python -c "import sys, urllib as ul; print ul.quote_plus(sys.argv[1])" "$TEATITLE")

#Echo copia en /tmp/ el trabajo de impresión en bruto para depuración.
#cp "$TEADATAFILE" "/tmp/$TEAJOBID.prn"

dominio=$(host -d -t CNAME servidor | grep domain | cut -f1 -d".")
paginas=$(pkpgcounter "$TEADATAFILE")

#Hay que convertir el valor de TEACLIENTHOST en un nombre de equipo.
if [ "$TEACLIENTHOST" == "localhost" -o "$TEACLIENTHOST" == "" ]
then
equipo=$(hostname -s)
else
if valid_ip $TEACLIENTHOST
then
equipo=$(host $TEACLIENTHOST | cut -d" " -f5 | cut -d"." -f1)
else
equipo=$TEACLIENTHOST
fi
fi

#Con todos estos parámetros que vienen del backend de /usr/lib/backend/teac4cups podemos llevar la contabilidad....
#En el ejemplo es un simple fichero de log, pero podría llevarse a un fichero .csv, guardarse en una BBDD o enviarse a una
#aplicacion web.
echo $TEAPRINTERNAME $TEAJOBID $TEAUSERNAME $TEATITLE - $equipo.$dominio - $TEACOPIES `pkpgcounter $TEADATAFILE` $TEAJOBSIZE>>/var/log/printaccounting.log

exit 0

Para que nuestros trabajos de impresión pasen por el backend de tea4cups y sean enviados a este script anterior, hay que modificar su definición en el el fichero /etc/cups/printers.conf, retocando el DeviceURI tal que así (ojo al parche, esto hay que hacerlo con el servicio CUPS parado):

server:~# cat /etc/cups/printers.conf
# Printer configuration file for CUPS v1.5.3
# Written by cupsd
# DO NOT EDIT THIS FILE WHEN CUPSD IS RUNNING
<Printer BN_SALAPROFESORES>
UUID urn:uuid:b21a4e7f-1d82-35aa-63cc-440dad3b7ada
Info BN_SALAPROFESORES
Location Sala de Profesores-Blanco y Negro
MakeModel Brother DCP-9020CDW CUPS
DeviceURI tea4cups://socket://172.19.196.23
State Idle
..........
</Printer>
<Printer COLOR_SALAPROF>
UUID urn:uuid:2336104b-3317-3748-702f-3063db09d1fe
Info COLOR_SALAPROF
Location Sala de Profesores-Color
MakeModel Brother DCP-9020CDW CUPS
DeviceURI tea4cups://socket://172.19.196.23
State Idle
..........
</Printer>

Siendo 172.19.196.23 la IP de nuestra impresora en la red.

En cuanto a:

filter : sed "0,/@PJL SET RENDERMODE=COLOR/{s/@PJL SET RENDERMODE=COLOR/@PJL SET RENDERMODE=GRAYSCALE/}"

esa línea es la madre del cordero y la que se encarga de dar el cambiazo de color a blanco y negro a cualquier trabajo enviado a la cola de impresión BN_SALAPROFESORES. ¿Cómo funciona esto?. Bueno, pues hay repasar como procesa los datos CUPS:

  • Las aplicaciones mandan los trabajos de impresión a CUPS. Originalmente esos trabajos están en el formato de la aplicación: odt, pdf, jpeg, xls, lo que sea.
  • CUPS procesa con el driver el trabajo de impresión y lo convierte a un lenguaje inteligible por la impresora destino. Este lenguaje puede ser PCL3/4/5, PCLXL, PostScript, ZJS, HBP, etc, etc, etc.
  • Este fichero en bruto en uno de los formatos indicados anteriormente se envía directamente a la impresora, que con su firmware lo interpreta e imprime.

En mi caso se trataba de averiguar en que formato estaba el fichero en bruto enviado para la impresora en cuestión. Eso se hace fácilmente capturando dicho fichero mediante tea4cups, en el script seguimiento_impresion:

......
......
#Echo copia en /tmp/ el trabajo de impresión en bruto para depuración.
cp "$TEADATAFILE" "/tmp/$TEAJOBID.prn"
......
......

Así conseguiremos una copia del fichero en bruto una vez  procesado por el driver en /tmp/$jobid.prn. Simplemente mandamos imprimir algo por la impresora y una vez impreso nos vamos a /tmp/$jobid.prn" y husmeamos que hay dentro. En nuestro caso es lo siguiente (marco en rojo las partes relevantes):

ESC%-12345X@PJL 
@PJL SET REPRINT=OFF
@PJL SET HOLD=OFF
@PJL SET USERNAME="root"
@PJL SET JOBNAME="Manual Centralita 7962G.pdf"
@PJL SET LOGINUSER="root"
@PJL JOB NAME="Manual Centralita 7962G.pdf"
@PJL PRINTLOG ITEM = 1,PRINTER
@PJL PRINTLOG ITEM = 2,Tue,14 Apr 2015 13:47:5
@PJL PRINTLOG ITEM = 3,Administrador
@PJL PRINTLOG ITEM = 4,CONSERJERIA
@PJL SET JOBTIME = "20150414134705"
@PJL SET STRINGCODESET=HPROMAN8
@PJL COMMENT ECONOMODE=OFF
@PJL SET ECONOMODE=OFF
@PJL SET RENDERMODE=COLOR
@PJL SET COLORADAPT=OFF
@PJL SET APTMODE=OFF
@PJL SET LESSPAPERCURL=OFF
@PJL SET FIXINTENSITYUP=OFF
@PJL SET RESOLUTION=600
@PJL ENTER LANGUAGE=XL2HB
) BROTHER XL2HB;1;0
<C0>^@<F8><86><D1>X^BX^B<F8><89>A<C0>^@<F8><88><C0>^A<F8><82>H<C0>^@<F8>(<C0>^A<F8>&<C0>^B<F8>%<C8><C0>^HdRegular<F8>'<C0>^@<F8>4C<D3>d^@d^@<F8>*u<C0>^@<F8>d<C0>^@<F8>b<C1><A0>^R<F8>l<C1><9E>^Z<F8>kѠ^R<9E>^Z<F8>g<C9><C1>^W^@^@^@^C^@^@^@^A^@^E^@^@^@^D^@^H^D^A^@^E^@^A^@^D^@^Q^D^A^@^E^@^B^@^D^@^Q^D^A^@^E^@^C^@^D^@
..........
..........

Este fichero se compone de una cabecera, legible, seguida por los datos de impresión ya en el formato interno de la impresora. El lenguaje de impresión es XL2HB, que buscando un poco en internet resulta ser una versión de PCLXL hecha por  Brother.

La cabecera está también en un formato bastante popular llamado PJL, que es un lenguaje de control de trabajos de impresión. La parte interesante es esta:

@PJL SET RENDERMODE=COLOR

que es donde se define si el trabajo está en blanco y negro o en color. Esta claro que si justo antes de enviarlo a la impresora, hacemos una búsqueda en el fichero en bruto y cambiamos la línea anterior por:

@PJL SET RENDERMODE=GRAYSCALE

el trabajo llegará en blanco y negro, habiendo dado el cambiazo en el último momento. Esto se hace con una orden sed de sustitución ya archiconocida de Linux y Unix:

# sed "0,/@PJL SET RENDERMODE=COLOR/{s/@PJL SET RENDERMODE=COLOR/@PJL SET RENDERMODE=GRAYSCALE/}" < infile >outfile

Esta linea dice "búscame la primera aparición de @PJL SET RENDERMODE=COLOR y, si la hay, cámbiala por @PJL SET RENDERMODE=GRAYSCALE"

Al estar la orden dentro del fichero teacups.conf, en la clausula filter y en la sección de BN_SALAPROFESORES:

[BN_SALAPROFESORES]
filter : sed "0,/@PJL SET RENDERMODE=COLOR/{s/@PJL SET RENDERMODE=COLOR/@PJL SET RENDERMODE=GRAYSCALE/}"

se aplicará a todo trabajo de impresión que pase por la cola BN_SALAPROFESORES antes de enviarlo a la impresora, como era nuestro objetivo inicial.

Nota adicional: el formato XL2HB no está inicialmente soportado por pkpgcounter, por lo que da un número de páginas estrambótico al hacer la contabilidad. No problem, existe un parche sencillo sobre /usr/share/pyshared/pkpgpdls/pclxl.py (recordemos que es una variante de PCLXL) para que funcione:

Y con esto acabamos por esta vez.. Le estoy dando una caña tremenda al OpenWrt sobre los Astoria ARV7518PW, a ver si pronto tengo algo que contar, pero los Linux embebidos son duros...

 

6 comentarios:

  1. una consulta esta bien que yo coloque yo uso cups en un servidor x.x.x.33:631 ya instale el tea4cups y queiro visualizarlo por web como lo haría tea4cups:/cups://x.x.x.x:631

    ResponderEliminar
  2. Y otra consulta como veo cuantas impresiones hice.

    gracias

    ResponderEliminar
  3. Muy, muy interesante, y muy bien documentado. Perdona, ¿que diferencia hay entre usar pykota y tea4cups? Te lo pregunto ya que estoy buscando información, y hay usuarios que tiran de uno y otros del otro. Muchísimas gracias de antemano. Saludos, Arturo

    ResponderEliminar
    Respuestas
    1. No estoy 100% seguro, pero creo que pykuota es mucho mas que tea4cups: permite controlar cuanto imprime cada usuario, ponerles cuotas (límites) de impresión, lleva la contabilidad en una base de datos y permite hacer informes.

      En cambio tea4cups es mucho mas modesto: es un filtro que se pone en la cola de impresión y que permite ejecutar comandos cuando se envia un trabajo de impresión a la impresora. Es la herramienta básica sobre la que puedes construir pykota o cualquier otra cosa mucho mas compleja o variada, como se sugiere en los comentarios del fichero tea4cups.conf.

      Con tea4cups tienes la posibilidad de decir: cuando se mande un trabajo de impresión, llama a este script. El resto depende de tu imaginación.

      Eliminar
  4. Muy, muy interesante, y muy bien documentado. Perdona, ¿que diferencia hay entre usar pykota y tea4cups? Te lo pregunto ya que estoy buscando información, y hay usuarios que tiran de uno y otros del otro. Muchísimas gracias de antemano. Saludos, Arturo

    ResponderEliminar