PrintTest

Esta herramienta surgió de la necesidad de comprobar el funcionamiento de impresoras de tickets, es decir, que corta bien, que imprime el logo correcto, etc. Su funcionamiento es realmente simple, se selecciona la impresora, se escribe la secuencia de escape y se pulsa el botón "Enviar". No importa si la impresora es serie, paralelo, usb o es una impresora compartida desde otro equipo, el único requisito es que este instalada, aunque sea como una impresora "Genérica / Solo texto".

Aquí dejo un ejemplo de algunas secuencias de escape (pueden variar de una impresora a otra):

Para que la impresora emita un pitido
\x1B@\x0A\x0D\x1B\x07\x0A\x0D
 
Para que corte el papel
\x1B@\x0A\x0D\x1Bi\x0A\x0D
 
Para abrir el cajon de monedas
\x1B@\x0A\x0D\027\112\000\100\250\x0A\x0D
 
Para que escriba "Hola mundo"  
\x1B@\x0A\x0DHola mundo\x0A\x0D

Los caracteres especiales se escriben precedidos por el carácter "\" seguido de 3 cifras decimales o de "x" y dos cifras hexadecimales. Así el carácter ESC se escribe "\x1B" o "\027", y el retorno de carro "\x0D" o "\013".

El código de este programa, en delphi, es el siguiente:

unit ufrmMain;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Menus;
 
type
  TfrmMain = class(TForm)
    txtSecuencia: TEdit;
    cmbPrinters: TComboBox;
    Label1: TLabel;
    Enviar: TButton;
    Label2: TLabel;
    mnuMain: TPopupMenu;
    mnuCortar: TMenuItem;
    mnuCopiar: TMenuItem;
    mnuPegar: TMenuItem;
    mnuEliminar: TMenuItem;
    N1: TMenuItem;
    mnuSeleccionar: TMenuItem;
    N2: TMenuItem;
    mnuDeshacer: TMenuItem;
    N3: TMenuItem;
    mnuWindows: TMenuItem;
    N4: TMenuItem;
    chkOnTop: TCheckBox;
    btnMenu: TButton;
    mnuEnviar: TMenuItem;
    mnuBucle: TMenuItem;
    procedure FormCreate(Sender: TObject);
    procedure EnviarClick(Sender: TObject);
    procedure mnuDeshacerClick(Sender: TObject);
    procedure mnuCortarClick(Sender: TObject);
    procedure mnuCopiarClick(Sender: TObject);
    procedure mnuPegarClick(Sender: TObject);
    procedure mnuEliminarClick(Sender: TObject);
    procedure mnuSeleccionarClick(Sender: TObject);
    procedure mnuWindowsClick(Sender: TObject);
    procedure chkOnTopClick(Sender: TObject);
    procedure btnMenuClick(Sender: TObject);
    procedure mnuBucleClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  frmMain: TfrmMain;
 
implementation
 
{$R *.dfm}
 
uses Printers, WinSpool, Clipbrd, ufrmBucle;
 
function RawToWindows(Str: String): String;
var
  i: Integer;
begin
  Result:= '<';
  while Length(Str) > 0 do
  begin
    if Copy(Str, 1, 1) = '\' then
    begin
      if Uppercase(Copy(Str, 2, 1)) = 'X' then
        Str[2]:= '$';
      if not TryStrToInt(Copy(Str, 2, 3),i) then
        Continue;
      Delete(Str, 1, 3);
    end else i:= Byte(Str[1]);
    Delete(Str,1,1);
    Result:= Result + IntToHex(i,2);
  end;
  Result:= Result + '>';
end;  
 
function WriteRawDataToPrinter(PrinterName: String; Str: String): Boolean;
var
  PrinterHandle: THandle;
  DocInfo: TDocInfo1;
  i: Integer;
  B: Byte;
  Escritos: DWORD;
begin
  Result:= FALSE;
  if OpenPrinter(PChar(PrinterName), PrinterHandle, nil) then
  try
    FillChar(DocInfo,Sizeof(DocInfo),#0);
    with DocInfo do
    begin
      pDocName:= PChar('Printer Test');
      pOutputFile:= nil;
      pDataType:= 'RAW';
    end;
    if StartDocPrinter(PrinterHandle, 1, @DocInfo) <> 0 then
    try
      if StartPagePrinter(PrinterHandle) then
      try
        while Length(Str) > 0 do
        begin
          if Copy(Str, 1, 1) = '\' then
          begin
            if Uppercase(Copy(Str, 2, 1)) = 'X' then
              Str[2]:= '$';
            if not TryStrToInt(Copy(Str, 2, 3),i) then
              Exit;
            B:= Byte(i);
            Delete(Str, 1, 3);
          end else B:= Byte(Str[1]);
          Delete(Str,1,1);
          WritePrinter(PrinterHandle, @B, 1, Escritos);
        end;
        Result:= TRUE;
      finally
        EndPagePrinter(PrinterHandle);
      end;
    finally
      EndDocPrinter(PrinterHandle);
    end;
  finally
    ClosePrinter(PrinterHandle);
  end;
end;
 
procedure TfrmMain.btnMenuClick(Sender: TObject);
var
  P: TPoint;
begin
  GetCursorPos(P);
  mnuMain.Popup(P.X, P.Y);
end;
 
procedure TfrmMain.chkOnTopClick(Sender: TObject);
begin
  if chkOnTop.Checked then
    FormStyle:= fsStayOnTop
  else
    FormStyle:= fsNormal;
end;
 
procedure TfrmMain.EnviarClick(Sender: TObject);
begin
  if cmbPrinters.ItemIndex >= 0 then
  begin
    WriteRawDataToPrinter(cmbPrinters.Items[cmbPrinters.ItemIndex],
      txtSecuencia.Text);
  end;
end;
 
procedure TfrmMain.FormCreate(Sender: TObject);
begin
  cmbPrinters.Items.Assign(Printer.Printers);
end;
 
procedure TfrmMain.mnuCopiarClick(Sender: TObject);
begin
  txtSecuencia.CopyToClipboard;
end;
 
procedure TfrmMain.mnuCortarClick(Sender: TObject);
begin
  txtSecuencia.CutToClipboard;
end;
 
procedure TfrmMain.mnuDeshacerClick(Sender: TObject);
begin
  txtSecuencia.Undo;
end;
 
procedure TfrmMain.mnuEliminarClick(Sender: TObject);
begin
  txtSecuencia.ClearSelection;
end;
 
procedure TfrmMain.mnuPegarClick(Sender: TObject);
begin
  txtSecuencia.PasteFromClipboard;
end;
 
procedure TfrmMain.mnuBucleClick(Sender: TObject);
begin
  with TfrmBucle.Create(Self) do
  try
    ShowModal;
  finally
    Free;
  end;
end;
 
procedure TfrmMain.mnuSeleccionarClick(Sender: TObject);
begin
  txtSecuencia.SelectAll;
end;
 
procedure TfrmMain.mnuWindowsClick(Sender: TObject);
begin
  ClipBoard.AsText:= RawToWindows(txtSecuencia.SelText);
end;
 
end.

El código lo puedes bajar de aquí, y el programa ya compilado de aquí.

Actualización: A raíz de una petición formulada en uno de los comentarios he decidido crear este pequeño ejecutable que permite usar la función WriteRawDataToPrinter directamente desde la linea de comandos, de esta manera se puede utilizar en archivos .bat, en accesos directos, etc ... El programa se puede descargar de aquí.

Un ejemplo de como usarlo:

rawprint "nombre_de_la_impresora" "\x1B@\x0A\x0D\027\112\000\100\250\x0A\x0D"

Comentarios

Vaya, tanto tiempo sin escribir nada, se ve que has estado ocupado.
Saluditos.

Que buen aporte... gracias :)

Hey amigo.

Me da gusto que hayas publicado algo mas en esta página :)

Muy intersante forma de imprimir, yo normalmente uso el canvas, así que probaré tu code.

Salud OS

Muchas gracias, es un buen aporte, yo trabajo mucho con impresoras TM-U de Epson.

Hola!
gran trabajo, nos ira perfecto para probar las impresoras TM de Epson.
Solo tengo una duda:
intento imprimir un logotipo gráfico previamente grabado en la memoria de la impresora y no lo consigo.
Los codigos ESC que tengo para esto son:
para la EPSON TM-T88, T88II y T88III : 28,112,1,0
para la EPSON TM-T88IV : 29,40,76,6,0,48,69,x,x,1,1 donde x es el numero que sale en el programa de carga del logo.
Saludos.

Yo para Epson tengo la siguiente secuencia:

\x1C\x70\x01\x30\x0A\x0D

Como ves solo se diferencia de la tuya en que, en vez de terminar en 0 termina en 30h que se corresponde con el valor ASCII de "0". Ademas he añadido un "retorno de carro" al final, a lo mejor no es necesario pero tampoco hace daño.

En cuanto a la TM-T88IV creo que sirve la misma secuencia que para las demás TM-T88, pruebalo.

Gracias por compartir este código.

Interesante codigo. Por cierto, tal vez puedas ayudarme. Tengo una impresora termica hasar conectada por USB a la maquina, con estas lineas que compartiste en este post puedo enviar correctamente comandos a la impresora, pero no se como leer o interpretar las respuestas. Por ejemplo, una consulta de estado al impresora. Podras ayudarme con eso?? muchas gracias desde ya.

Pues la verdad es que nunca he probado, pero me imagino que, al igual que podemos usar la función WritePrinter para enviar datos a la impresora, podemos recibir información de la impresora con la función ReadPrinter. Yo empezaría a buscar por ahí.

Gracias por tan inmediata respuesta. Te comento que es la primera vez que tratamos con este tipo de impresoras. Intentamos usar la funcion ReadPrinter pero no obtuvimos resultados muy claros. Te pregunte porque tal vez alguna vez habias tratado con algo similar. Desde ya agradezco tu aporte y veremos como seguimos. Suerte.

Hola buen día, en caso de querer enviar el documento a imprimir con cierta fuente y tamaño de letra, diferente tamaño y orientación de papel, que comandos se utilizan??

El código esta pensado solo para impresoras de tickets.

Si necesitas imprimir en una impresora normal, con diferentes fuentes y orientaciones del papel, debes de utilizar el "Canvas" del objeto Printer.

Gracias por tu respuesta, no tendrás un código o ejemplo de Canvas??

Pues tengo este ejemplo que puse hace tiempo en un foro:

type
  TPrinterCaps = record
    ppx: integer;
    ppy: integer;
    pox: integer;
    poy: integer;
  end;
 
  TPrinterContext = record
    Caps: TPrinterCaps;
    Cursor: TPoint;
    Inter: Double;
  end;
 
function CreatePrinterContext: TPrinterContext;
begin
  FillChar(Result,Sizeof(Result),0);
  with Result.Caps do
  begin
    ppx:= GetDeviceCaps(Printer.Handle, LOGPIXELSX);
    ppy:= GetDeviceCaps(Printer.Handle, LOGPIXELSY);
    pox:= GetDeviceCaps(Printer.Handle, PHYSICALOFFSETX);
    poy:= GetDeviceCaps(Printer.Handle, PHYSICALOFFSETY);
  end;
  Result.Inter:= 1.5;
end;
 
function ToPixelsX(X: Double; Context: TPrinterContext): Integer;
begin
  with Context.Caps do
    Result:= Round((X / 25.4) * ppx) - pox;
end;
 
function ToPixelsY(Y: Double; Context: TPrinterContext): Integer;
begin
  with Context.Caps do
    Result:= Round((Y / 25.4) * ppy) - poy;
end;
 
procedure SetPrinterCursor(X, Y: Double; var Context: TPrinterContext);
begin
  with Context do
  begin
    if X > 0 then Cursor.X:= ToPixelsX(X,Context);
    if Y > 0 then Cursor.Y:= ToPixelsY(Y,Context);
  end;
end;
 
procedure IncPrinterCursor(var Context: TPrinterContext);
begin
  with Context, Context.Caps do
  begin
    Cursor.Y:= Cursor.Y + Round(Inter*(Printer.Canvas.Font.Size * ppy) / 72);
  end;
end;
 
procedure DecPrinterCursor(var Context: TPrinterContext);
begin
  with Context, Context.Caps do
  begin
    Cursor.Y:= Cursor.Y - Round(Inter*(Printer.Canvas.Font.Size * ppy) / 72);
  end;
end;
 
procedure PrinterWrite(Width: Double; alRight: Boolean; Str: string;
  Context: TPrinterContext);
var
  Rectangle: TRect;
begin
  with Context, Context.Caps do
  begin
    Rectangle.Left:= Cursor.X;
    Rectangle.Top:= Cursor.Y;
    Rectangle.Right:= Cursor.X + Round((Width / 25.4) * ppx);
    if alRight then Cursor.X:= Rectangle.Right - Printer.Canvas.TextWidth(Str);
    Rectangle.Bottom:=
      Cursor.Y + Round(Inter*(Printer.Canvas.Font.Size * ppy) / 72);
    Printer.Canvas.TextRect(Rectangle,Cursor.X,Cursor.Y,Str);
  end;
end;
 
procedure PrinterTextOut(X, Y: Double; Str: string; Context: TPrinterContext);
begin
  Printer.Canvas.Textout(ToPixelsX(X,Context),ToPixelsY(Y,Context),Str);
end;
 
procedure PrinterCenterText(X, Y: Double; Str: string;
  Context: TPrinterContext);
begin
  Printer.Canvas.Textout(
    ToPixelsX(X,Context) - (Printer.Canvas.TextWidth(Str) div 2),
    ToPixelsY(Y,Context) - (Printer.Canvas.TextHeight(Str) div 2),
    Str);
end;
 
procedure PrinterMoveTo(X, Y: Double; Context: TPrinterContext);
begin
  Printer.Canvas.MoveTo(ToPixelsX(X,Context),ToPixelsY(Y,Context));
end;
 
procedure PrinterLineTo(X, Y: Double; Context: TPrinterContext);
begin
  Printer.Canvas.LineTo(ToPixelsX(X,Context),ToPixelsY(Y,Context));
end;
 
procedure PrinterLine(X1,Y1,X2,Y2: Double; Context: TPrinterContext);
begin
  PrinterMoveTo(X1,Y1,Context);
  PrinterLineTo(X2,Y2,Context);
end;
 
 
// Y uso las funciones anteriores de esta manera
var
  i: Integer;
  Context: TPrinterContext;
begin
  with TPrinterSetupDialog.Create(nil) do
  try
    if Execute then
    with Printer do
    begin
      Printer.Orientation := poPortrait;
      Printer.BeginDoc;
      try
        Context:= CreatePrinterContext;
        Canvas.Font.Name:= 'Arial';
        Canvas.Font.Color:= clBlack;
        Canvas.Font.Size:= 10;
        SetPrinterCursor(20,20,Context);
        for i:= 1 to 20 do
        begin
          PrinterWrite(100,FALSE,'Linea ' + IntToStr(i),Context);
          incPrinterCursor(Context);
        end;
        Printer.EndDoc;
      except
        Printer.Abort;
      end;
    end;
  finally
    Free;
  end;
end;

Las medidas (X,Y) son en milímetros medidas desde la esquina superior izquierda de la hoja de papel.

Recuerda añadir Printers a las uses

Muchas gracias.

Muy buenas,

Vaya por delante que no tengo ni idea de programación.

El hecho es que usando el PrintTest para abrir el cajon monedero (con una Epson TM-T20 y Win7 SP1, conectada por USB) me abre el cajon pero me hace un molestisimo feed (un avance de papel). Finalmente lo he resuelto metiendo el siguiente codigo con el susodicho programa: "\027\112\000\100\250" (sin comillas) y perfecto, me abre el cajon y NO hay feed.

Como podria traspasar este codigo a un fichero bat. Ya que el programa que uso tira de un bat para abrir el cajon pulsando un boton (imprimiendo si me abre, pero quiero poder usar el boton existente).

De momento con el programa solo he podito conseguir abrir el cajon y que me haga un feed tambien. Esto lo he conseguido instlando unos drivers para de manera virtual decirle que el puerto usb de la impresora esta en el LPT1. Y el programa me dice que en el bat le lance el siguiente codigo: "echo ←p0 5 > lpt2" (sin comillas) y lo dicho, el boton funciona pero me hace un puñetero feed.

Que puedo hacer para evitar este feed??. Porque con el PrintTest no me hace el feed y encima no necesito virtualizar nada??.

Si podeis ayudarme alguien por favor, os lo agradeceria, llevo dias buscando por internet y no consigo arreglarlo.

Un saludo,

El comando "echo" siempre envía un avance de linea al final, por eso seguramente te esta imprimiendo una linea vacía.

No lo he probado, pero se me ocurre que puedes utilizar el comando "type" en vez de "echo". Lo que tendrías que hacer es crear un archivo de texto y meter solo esa linea, asegurándote de que no hay ninguna linea en blanco. Luego solo tendrías que sustituir el comando que usas por esto otro:

type archivo.txt>lpt2

Efectivamente. si señor!!. Ademas justo a medio dia he encontrado por internet alguien que habia dejado un bat y un txt preparados asi, lo he probado y funciona:

En el bat: type open.txt > lpt2
Y en el txt: ←p

Nota: La flecha tiene que ser copy-paste del txt que he bajado, si hago Alt+27 me sale una flecha algo distinta que no me deja guardar (no se si porque es formato unicode y guardo en ansi, en fin, no me lio mas).

Lo cierto es que sois unos cracks, tu y el que haya dejado los ficheros preparados. Porque vamos, los del programa no tienen ni idea, los de Epson tampoco, y las soluciones que habia por la red no funcionaba ninguna, y he probado varias.

Muuuchisimas gracias.

Hola,
He probado el printtest con la cadena de apertura de cajon y funciona perfecto. No soy programador por lo que necesitaria vuestra ayuda. Podrias pasarme un archivo en el que le mande la cadena de apertura a una impresora con el nombre TICKETS, para ejecutar el .exe y sin necesidad de seleccionar impresora abrir el cajon.
Muchas gracias.
Un saludo.

Si puedes esperar unos dias creo que podre subir el programa que necesitas

Claro, puedo esperar, muchas gracias.

Pues te dejo el programa arriba, al final del articulo, para que lo descargues.

Pruebalo, y ya me contaras si te fue útil.

Muchas gracias, funciona perfecto. Tenia problemas con la apertura de cajon con las impresoras en Windows 7, y con esto me funciona perfectamente.

Un saludo.

Amigo su programa printtest me abre el cajon perfectamente, hay alguna manera que yo pueda inluir el comando en un archivo y ejecutarlo mediante DOS, estoy con una impresora EPSON TM-U210A GRACIAS.!!

Si, precisamente al final del articulo veras que he publicado una actualización, que no es mas que un programa de consola que permite pasar los parámetros desde la linea de comandos.

Muchas gracias, fue de mucha utilidad, si funciona... Felicitaciones.

Muchas Gracias, fue de gran utilidad, pero tengo un detalle al imprimir las palabras con Acentos, "ñ" y las diericis. Alguien me pude ayudar al respecto?

El problema es que no estas usando el mismo juego de caracteres que la impresora, revisa su configuracion para saber que juego de carscteres esta usando.
Otra solucion es mandar imprimir todos los caracteres, desde el \032 al \255, hasta averiguar que codigo se corresponde con cada caracter

Hola
Tengo una impresora Zebra Gk420t como puedo imprimir una etiqueta con codigo de barra y darle el tamano q necesito con esta funcion

Muchas gracias

Nunca he trabajado con impresoras Zebra así que no puedo ayudarte, pero supongo que si revisas el manual encontraras los comandos necesarios.

También puedes probar a preguntar en este foro, se que algún compañero ha utilizado impresoras Zebra antes y quizá pueda ayudarte:
http://www.delphiaccess.com/forum/forum/

Hola, ante todo muchas gracias por compartir tus conocimientos, realmente me han servido de mucho.
Estoy tratando de usar el programa rawprint para una aplicación con la impresora Zebra GK420t, hasta ahora las pruebas van bien, solo me falta una cosa, enviarle el contenido de un archivo como parámetro, es posible ? Podrías subir el código fuente del rawprint?
Desde muchísimas gracias.

El códgio de "rawprint" es muy sencillo:

program rawprint;
 
uses
  Windows,
  SysUtils,
  WinSpool;
 
function WriteRawDataToPrinter(PrinterName: String; Str: String): Boolean;
var
  PrinterHandle: THandle;
  DocInfo: TDocInfo1;
  i: Integer;
  B: Byte;
  Escritos: DWORD;
begin
  Result:= FALSE;
  if OpenPrinter(PChar(PrinterName), PrinterHandle, nil) then
  try
    FillChar(DocInfo,Sizeof(DocInfo),#0);
    with DocInfo do
    begin
      pDocName:= PChar('rawprint');
      pOutputFile:= nil;
      pDataType:= 'RAW';
    end;
    if StartDocPrinter(PrinterHandle, 1, @DocInfo) <> 0 then
    try
      if StartPagePrinter(PrinterHandle) then
      try
        while Length(Str) > 0 do
        begin
          if Copy(Str, 1, 1) = '\' then
          begin
            if Uppercase(Copy(Str, 2, 1)) = 'X' then
              Str[2]:= '$';
            if not TryStrToInt(Copy(Str, 2, 3),i) then
              Exit;
            B:= Byte(i);
            Delete(Str, 1, 3);
          end else B:= Byte(Str[1]);
          Delete(Str,1,1);
          WritePrinter(PrinterHandle, @B, 1, Escritos);
        end;
        Result:= TRUE;
      finally
        EndPagePrinter(PrinterHandle);
      end;
    finally
      EndDocPrinter(PrinterHandle);
    end;
  finally
    ClosePrinter(PrinterHandle);
  end;
end;
 
begin
  if ParamCount = 2 then
    WriteRawDataToPrinter(ParamStr(1),ParamStr(2));
end.

Espero que te sirva de ayuda.

Saludos

Solo quería que supieras que pude hacer lo que necesitaba.
Muchísimas gracias por compartir y ayudar a los que sabemos menos.

que codigo tendria que utilizar en el printext para seleccionar un logo almacenado en mi impresora epson tm88v

Prueba con esto:

\x1C\x70\x01\x00

Puede que tengas que incluir la secuencia de inicio y un avance de linea:

\x1B@\x1C\x70\x01\x00\x0\x0A\x0D

buen aporte lo acabo de calar en win7 de 64 bits y jalo.... bien, en una impresora Zebra ZM600, nada mas tengo que adecuarlo a mi programa y listo.... gracias Seoane

Se agradece el ejemplo que pusiste. Me fue de utilidad para hacer una impresión en una EPSON TM-C3400.

Lo único que no entendí fue para que usas la funcion PrinterLine. Dime si es que la usas para digujar una línea y asi hacer más comodo el uso de las otras dos funciones que incluye.

Comento por si a alguno le interesa. La impresora mensionada no usa fuentes propias, sino las que tenga instaladas Windows. Si se quiere imprimir un código de barras lo unico que se tiene que hacer es llamar a la fuente correspondiente para generar el código y mandar a imprimir el texto que debe generar este código de barras.

Exacto "PrinterLine" es un atajo para las otras dos funciones.

que codigo necesitaría para abrir el cajón de dinero de una GP-U80300II??

Yo he probado en ena GP con
\x1B@\x0A\x0D\027\112\000\100\250\x0A\x0D
y funciona (quizá se pueda eliminar el salto de línea final \x0A\x0D

Buena Tarde. tengo una impresora QL-700. ya realice varios intentos, inclusive este ejemplo y al mandar a imprimir me marca error de comunicación entre el PC y la Brother QL. Lo extraño es que desde el software que trae "PTLITE10" si puedo imprimir etiquetas sin problema. Espero me puedan Apoyar. de antemano gracias.

una consulta, en base a su código me encuentro imprimiendo a la perfección mis facturas pero ahora necesito incluir código qr al final, sera que me pueda dar alguna pista ?