jsmitter
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
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
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
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.Hand le, Passthrough, 0, @buff, nil);
Result := True;
end
else
Result := False;
end;
// example:
printer.BeginDoc;
try
printer.printerindex := printer.printers.IndexOf(' PrinterNam e');
directoprinter('This text ');
finally
printer.EndDoc;
end;
Happy holidays...
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.Hand
Result := True;
end
else
Result := False;
end;
// example:
printer.BeginDoc;
try
printer.printerindex := printer.printers.IndexOf('
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!
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);
directoprinter(#27#64);
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!
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...
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.
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
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
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
ASKER
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!
ASKER
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.Hand le, 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,Tru e);
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'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.Hand
Result := True;
end
else
Result := False;
end;
__________________________
******* My implentation from within another procedure is -
printer.BeginDoc;
try
printer.printerindex := printer.printers.IndexOf('
DirectToPrinter(PrtStr,Tru
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
ASKER
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
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,Tru e);
finally
printer.EndDoc;
end;
it should be
printer.printerindex := printer.printers.IndexOf(' EPSON LQ-590 ESC/P2');
printer.BeginDoc;
try
DirectToPrinter(PrtStr,Tru e);
finally
printer.EndDoc;
end;
You first select the printer and then start a printout to it...
printer.BeginDoc;
try
printer.printerindex := printer.printers.IndexOf('
DirectToPrinter(PrtStr,Tru
finally
printer.EndDoc;
end;
it should be
printer.printerindex := printer.printers.IndexOf('
printer.BeginDoc;
try
DirectToPrinter(PrtStr,Tru
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...
ASKER
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
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
ASKER
One more observation.
Interesting... Setting the boolean value from 'true' to 'false' in the 'DirectToPrinter(PrtStr,Tr
Hi again,
try changing the paper size in the device settings page, in the driver properties, to not available
try changing the paper size in the device settings page, in the driver properties, to not available
ASKER
No difference.
ASKER
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,
ASKER
Correction - the sentence where I state that the CR was necessary should have read - "The CR was not necessary..."
Sorry for the confusion
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)
....
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;
ASKER
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...
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('
printer.BeginDoc;
try
for i := 0 to StringList.Count -1
DirectToPrinter(Stringlist
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...
ASKER
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
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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!