Link to home
Start Free TrialLog in
Avatar of jsmitter
jsmitterFlag for United States of America

asked on

Line printer problems

In my Delphi 7 app, I'm trying to get a dot-matrix printer (Epson LQ590) to operate like a line printer, in other words, just to print one lie at a time without executing a form feed. Currently, the printer prints one line then form feeds so I only get one line per page.

I'm using the Printers unit and the fowing code;

 Printer.BeginDoc;
  Printer.Canvas.Pen.Color  := 0;
  Printer.Canvas.Font.Name  := 'Times New Roman';
  Printer.Canvas.Font.Size  := 12;
  //Printer.Canvas.TextOut(0, 100, #10+PrtStr+#13); // This didn't work
  Printer.Canvas.TextOut(0, 100, PrtStr);
  Printer.EndDoc;

The Epson is set up as the default system printer. Is it because I'm printing @0,100 each time? Or, Are there special control codes that I need to send with the print string? Or, do I need to modify the system printer properties?

Anybody got any suggestions?

Thanks
Avatar of atul_parmar
atul_parmar
Flag of India image

Hi, I suggest you to refer the printer manual where you will find the escape sequences. For better control open the printer port directly and print using WriteLn/Write method.

There is an optional rs-232 module available for this printer. It's very possible that it will be very easy to achieve what you want with this module. It's more or less the same as atul_parmar says (open lpt direct), but much easier to achieve.
Though keep in mind that if you go direct, you probably loose the ability to select a font (Times in this case) just like that, the printer will probably print in a default font.
Hi, the best way to print to a dot-matrix printer is to use the ESCAPE function. With this one you bypass the windows driver internal printing methods and print directly to the printer. Here is the code:

type
  TPrnBuffRec = record
  bufflength: Word;
  Buff_1: array[0..255] of Char;
end;

function DirectToPrinter(S: string; NextLine: Boolean): Boolean;
var
  Buff: TPrnBuffRec;
  TestInt: Integer;
begin
  TestInt := PassThrough;
  if Escape(Printer.Handle, QUERYESCSUPPORT, SizeOf(TESTINT), @testint, nil) > 0 then
  begin
    if NextLine then  S := S + #13 + #10;
    StrPCopy(Buff.Buff_1, S);
    Buff.bufflength := StrLen(Buff.Buff_1);
    Escape(Printer.Canvas.Handle, Passthrough, 0, @buff, nil);
    Result := True;
  end
  else
    Result := False;
end;

//  example:
printer.BeginDoc;
try
  printer.printerindex := printer.printers.IndexOf('PrinterName');
  directoprinter('This text ');
finally
  printer.EndDoc;
end;

Happy holidays...
You know it's a good day if it starts with learning something completely new (and that's not sarcasm), thanks twinsoft :)

Please let me know if it works like you want jsmitter!
Futhermore, if you need to change the font size, enable condessed mode etc you should use Escape Sequences and send them to the printer like this

directoprinter(#27#64);

Avatar of jsmitter

ASKER

Thanks for the input. If I bypass the Windows printer drivers, like Twinsoft suggests, how do I "bypass the windows driver internal printing methods and print directly to the printer". The printer connects through a USB port.
Do I still install the Epson drivers? Also, will it create a problem if the printer goes "off line", runs out of paper, how are printer errors handled, etc?
Thanks again!
Hi again,
you do not bypass the windows driver, you bypass the internal printing methods. Yes, you install the windows driver for the printer or a Generic Text driver, if you do not have it. Everything else is working as expected, the printouts are spooled, offline and out of paper situations are handled by the windows spooler as usual. You can also select the printer port from within the driver properties (the virtual USB port in your case). All this is tested in live installations for over a decate in commercial applications that we develop...
One other point.  If I remember correctly from when I was messing with Epson dot matrix printers, the BEGIN DOC and END DOC functions are causing the single line to be treated as a complete document . . . which causes the form feed at the end of printing that line (or, more precisely, at the END DOC).
If you know all the lines that need to be printed, loop through them where you have the print statement for the single line.
Could you try?

var
  myFile : TextFile;
begin
  // Try to open a printer file
    AssignPrn(myFile);

    // Now prepare to write to the printer
    ReWrite(myFile);

    // Write a couple of well known words to this file -
    // they will be printed instead
    WriteLn(myFile, 'Hello');
    WriteLn(myFile, 'World');

    // Close the file
    CloseFile(myFile);
end;

HTH
Ashok
Could you try?

Note: Add Printers in uses clause.

uses Printers;

var
  myFile : TextFile;
begin
  // Try to open a printer file
    AssignPrn(myFile);

    // Now prepare to write to the printer
    ReWrite(myFile);

    // Write a couple of well known words to this file -
    // they will be printed instead
    WriteLn(myFile, 'Hello');
    WriteLn(myFile, 'World');

    // Close the file
    CloseFile(myFile);
end;

HTH
Ashok
I am off for New Years and will be back on the project by Tues. Looking forward to trying these recommendations. Happy New Year, everyone!
Hi everyone. I'm back. I lost my printer and had to go out and buy another one.
I've been playing with twinsoft's approach. I get a compile but then I get a "Debugger Exception Message - 'Printing In Progress'"  when my app tries to print. My Delphi 7 help file has nothing on the Escape Function.Therefore, I've enclosed my code to see if maybe I didn't understand something properly.
I'm not sure where the type statements go. Framing it with "Type" and "End" threw me off. Right now, I've got it placed under implentation.
implementation

type  
  TPrnBuffRec = record
  bufflength: Word;
  Buff_1: array[0..255] of Char;
end;
{$R *.dfm}
___________________________________________________________________
******* I declared the function under TForm1 Class Private left the function as it was in the twinsoft's example.
function TForm1.DirectToPrinter(S: string; NextLine: Boolean): Boolean;
var
  Buff: TPrnBuffRec;
  TestInt: Integer;
begin
  TestInt := PassThrough;
  if Escape(Printer.Handle, QUERYESCSUPPORT, SizeOf(TESTINT), @testint, nil) > 0 then
  begin
    if NextLine then  S := S + #13 + #10;
    StrPCopy(Buff.Buff_1, S);
    Buff.bufflength := StrLen(Buff.Buff_1);
    Escape(Printer.Canvas.Handle, Passthrough, 0, @buff, nil);
    Result := True;
  end
  else
    Result := False;
end;
____________________________________________________________________________
******* My implentation from within another procedure is -
  printer.BeginDoc;
try
  printer.printerindex := printer.printers.IndexOf('EPSON LQ-590 ESC/P2');
  DirectToPrinter(PrtStr,True);
finally
  printer.EndDoc;
end;
_____________________________________________________________________________
I replaced PrinterName in twinsoft's example with the WinXP printer name 'EPSON LQ-590 ESC/P2' found in "Printers and Faxes". This printer is also selected as the system default printer.
I really appreciate all the help. Thanks
I have one more observation to add
When the "Printer In Progress" message appears, the printer queue icon appears in the systray. When opening it, the print job is listed but the file size is 0. So, it appears that the string is not being passed to the procedure. I'll bet its got something to do with the "type" statement being in the wrong place? The only two places I could get the app to compile is placing it right above the implentaion line or below it. Either place yeilds the same result. Where is the right place for it?
Thanks again
Hi, the problem is here:

 printer.BeginDoc;
try
 printer.printerindex := printer.printers.IndexOf('EPSON LQ-590 ESC/P2');
 DirectToPrinter(PrtStr,True);
finally
 printer.EndDoc;
end;

it should be

 printer.printerindex := printer.printers.IndexOf('EPSON LQ-590 ESC/P2');
 printer.BeginDoc;
try
 DirectToPrinter(PrtStr,True);
finally
 printer.EndDoc;
end;

You first select the printer and then start a printout to it...
Hmm, i just saw that the mistake was mine ... (in the previous post). Sorry...
Thanks twinsoft,
The change worked. However, it still only prints one line per 11" page. I checked the printer settings and auto LF and auto CR are disabled and there is no auto form feed. What next?
Thanks

One more observation.
Interesting... Setting the boolean value from 'true' to 'false' in the 'DirectToPrinter(PrtStr,True)' statement does not change the result. It prints just like it did when it was set to 'true'. Hope this helps. Thanks
Hi again,
  try changing the paper size in the device settings page, in the driver properties, to not available
No difference.

twinsoft,

Your routine works great and I like the versitality that it provides. However, as I stated, a FF was executed after the line was printed. I resolved this by appendinding the string with the following ESC/P codes;
 
if NextLine then  S := S + #27+#67+#1;
the 27,67,1 overrides the FF by setting the FF to one line.
The CR is necessary because the printer is a bidirectional printer. The print function now works perfectly. However, there is one last issue. The buffer fills up and the app errors out.
Last question - What is the best way to clear the buffer after the line prints? And I still need an answer to one of my earlier questions - Where should I put the type/end statement. I currently have it as the last declaration before the "implementation" section. Is that correct?
 
Many thanks,

Correction - the sentence where I state that the CR was necessary should have read - "The CR was not necessary..."
Sorry for the confusion
Hi,
  1) change the length of the buffer

    Buff_1: array[0..511] of Char;

  2) The best way is to create a component that will do the job. I am sending you the declaration class of the component that i've created, to have something as a prototype (It is a comp specific to the thermal printers i use). If you don't want to use a component, the declaration must be after the first type in your unit and before the declaration of your form class. Something like this:

type
  TPrnBuffRec = record
  bufflength: Word;
  Buff_1: array[0..511] of Char;

  TForm1 = class(TForm)
  ....


  TXSPrintFormII = class(TComponent)
  private
    CurrPage: Integer;
    TotalPages: Integer;
    FPrintType: TPrintTypes;
    FTitle: string;
    FAsciiForm: string;
    FMemoryForm: TStrings;
    FCurrentPrinter: string;
    FLinesPerPage: integer;
    FIsSlipPrinter: boolean;
    FEnableCutter: boolean;
    FPrintFooter: boolean;
    FOnFieldData: TNotifyOnGetFieldData;
    FCharPitch: TCharPitch;
    FMyPrinters: TStrings;
    FOnPrintProgress: TNotifyOnPrintProgress;
    FLineSpacing: TLineSpacing;
    FHexFldNumbers: Boolean;
    FPrintToFile: Boolean;
    FPrintFileName: String;
    FHandle: TextFile;
    FooterBlock: Boolean;
    FOnPrintForm: TNotifyOnPrintForm;
    FBrcData: String;
    FPrintToMemory: Boolean;
    FUse437Greek: Boolean;
    FOnPrintDetails: TNotifyOnPrintDetails;
    FOnPrintDetailsEnd: TNotifyOnPrintDetailsEnd;
    FPrintDataset: TDataset;
    FIsLaserPrinter: boolean;
    FCurrentFont: String;
    FIs350Plus: Boolean;
    FIsTextPrinter: Boolean;
    FUseUnicode: Boolean;
    FRawPrintForm: Boolean;
    FPrintBlocks: Boolean;
    FReversePrint: Boolean;
    FEnableReverse: Boolean;

    procedure SetAsciiForm(const Value: string);
    procedure SetMemoryForm(const Value: TStrings);
    procedure SetCurrentPrinter(const Value: String);
    procedure PrepareForm(f: TStringList; DetailRecs: Variant);
    procedure SetPrintDataset(const Value: TDataset);
    procedure Print(LastPage: Boolean);
    procedure LoadPrinters;

    function SearchCode(line: string; NewCommand, IsDetail: boolean; DetailCount: Integer = 0): string;
    function DirectToPrinter(s: string; ChangeLine: boolean): Boolean;
    function PrintBarcode(Barcode: String; BrcType: TBrcType; Height: Integer; Width: TBrcWidth; HRI: THRI): String;
  protected
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure PrintForm(DetailRecs: Variant);
    procedure RefreshPrinters;
    procedure SetReverseFont(const aIsReverse: Boolean);
    function GetStandardFont(const Value: Integer): String;
    function GetFontEscSeq(aPrintType: TPrintTypes): String;
    function GetCurrFontEscSeq: TPrintTypes;
    function ConvertToDos(s: string): string;
    function SendPulse: String;
    function SendString(aString: String): Boolean;
    property Printers: TStrings read FMyPrinters;
    property CurrentFont: String read FCurrentFont;
  published
    property AsciiForm: string read FAsciiForm write SetAsciiForm;
    property CharPitch: TCharPitch read FCharPitch write FCharPitch;
    property MemoryForm: TStrings read FMemoryForm write SetMemoryForm;
    property CurrentPrinter: string read FCurrentPrinter write SetCurrentPrinter;
    property FormTitle: string read FTitle write FTitle;
    property PrintFooter: boolean read FPrintFooter write FPrintFooter;
    property PrintBlocks: boolean read FPrintBlocks write FPrintBlocks;
    property EnableCutter: boolean read FEnableCutter write FEnableCutter;
    property IsSlipPrinter: boolean read FIsSlipPrinter write FIsSlipPrinter;
    property IsLaserPrinter: boolean read FIsLaserPrinter write FIsLaserPrinter;
    property IsTextPrinter: boolean read FIsTextPrinter write FIsTextPrinter;
    property LinesPerPage: integer read FLinesPerPage write FLinesPerPage;
    property LineSpacing: TLineSpacing read FLineSpacing write FLineSpacing;
    property HexFldNumbers: Boolean read FHexFldNumbers write FHexFldNumbers;
    property PrintToFile: Boolean read FPrintToFile write FPrintToFile;
    property PrintToMemory: Boolean read FPrintToMemory write FPrintToMemory;
    property PrintFileName: String read FPrintFileName write FPrintFileName;
    property RawPrintForm: Boolean read FRawPrintForm write FRawPrintForm;
    property OnFieldData: TNotifyOnGetFieldData read FOnFieldData write FOnFieldData;
    property OnPrintProgress: TNotifyOnPrintProgress read FOnPrintProgress write FOnPrintProgress;
    property OnPrintForm: TNotifyOnPrintForm read FOnPrintForm write FOnPrintForm;
    property OnPrintDetails: TNotifyOnPrintDetails read FOnPrintDetails write FOnPrintDetails;
    property OnPrintDetailsEnd: TNotifyOnPrintDetailsEnd read FOnPrintDetailsEnd write FOnPrintDetailsEnd;
    property Use437Greek: Boolean read FUse437Greek write FUse437Greek;
    property UseUnicode: Boolean read FUseUnicode write FUseUnicode;
    property PrintDataset: TDataset read FPrintDataset write SetPrintDataset;
    property Is350Plus: Boolean read FIs350Plus write FIs350Plus;
    property EnableReverse: Boolean read FEnableReverse write FEnableReverse;
  end;

Open in new window

My app runs 24/7. No matter how large I make the buffer, it will evenually max out and cause the app to error out. I don't need to retain the data. Is there a way to clear or reset the buffer after I print the string?
Hi again,
  what you should do is create your printout in a TStringList by calling StringList.Append() for each line that you want to print. Then parse the StringList like this:

 printer.printerindex := printer.printers.IndexOf('EPSON LQ-590 ESC/P2');
 printer.BeginDoc;
try
 for i := 0 to StringList.Count -1
  DirectToPrinter(Stringlist[i],True);
finally
 printer.EndDoc;
end;

That way each buffer will only hold data for one line and you will not consume any memory. This is the way i am doing it...
I'm so close but yet so far!
I placed the type/end statement where you suggested - right above 'TForm1 = class(TForm)'. The interesting thing is, that the statement 'type' is not required (it would be redundant), but the statement 'end' is. This looks strange to me and I don't think its right. It's the only way that the program compiles. I would think that it would fit in with the rest of the type declarations.
______________________________________________________
type
  TPrnBuffRec = record
  bufflength: Word;
  Buff_1: array[0..511] of Char;
  end;                                       // Is this right?

  TForm1 = class(TForm)
________________________________________________________
I must be missing something fundamental here. Help!
Thanks

 
ASKER CERTIFIED SOLUTION
Avatar of twinsoft
twinsoft
Flag of Greece image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks twinsoft, The printer is now printing properly and I have complete control of it with the esc/p commands. Have a great new year!