|

Introduction
This article presents a printer class that supports the LPR print protocol in C#. With this class, it is possible to send a print file in ASCII, PostScript, PCL, et cetera, directly to a network printer or print server that communicates by means of the LPR (LPD) protocol. The printer class also implements the LPQ, the LPRM, and the Restart request.
Background
The LPR/LPD protocol is a 15 year old print protocol from the TCP/IP suite that is still important in the area of network printers and print servers. It is described in detail in RFC 1179. Augmented variations of the protocol exist, like LPRNG also known as LPR Next Generation.
In an application, I needed to send PostScript files to an LPD enabled printer. It was possible to use the command line lpr.exe which is included in Windows but I didn't want to be dependant on lpr.exe, so I searched for a free C# implementation of LPR. As I could not find one, it was time to build such a class myself.
How to use the code
The printer class is straightforward. The constructor is called with three parameters, the hostname, the queue name, and the username. As these parameters are reused again and again with every LPR and LPQ request, I decided to place them in the constructor. Just a choice.
To print a file, one only needs to call LPR with the filename as argument, and to get the content of the spool queue, just call LPQ. The boolean parameter of LPQ indicates a long or small listing. The output format of LPQ depends on the implementation of the LPD daemon in the printer so there might be no difference.
So the core code to print a postscript file could look like: if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
LPD.Printer printer1 =
new LPD.Printer("saturnus","queue","rob");
string fname = openFileDialog1.FileName;
if (fname.EndsWith(".ps"))
{
printer1.LPR(fname);
textBox1.Text = printer1.LPQ(false);
}
else
{
}
}
Points of interest
To use the printer class, one should have a network printer with an LPD daemon, or start the TCP/IP Print Service on a computer (print server). Then, you can send files to any Windows printer defined on the computer. Use the PC name as hostname and the name of the printer as queue name. Be aware that the name of the printers may not contain spaces as the LPR/LPD protocol uses spaces as separators.
LPR does not wait until the file is printed, it starts a thread in the background for every file. LPQ and LPRM do not use a background thread.
The Restart method seems not to be supported in the Windows LPD daemon as I get no acknowledge. Nevertheless, I kept it in the code (use at own risk :).
Some things to improve the class include: (no deadline)
- improve error handling
- implement status and some other properties
- fix some todo's in the code
- overload LPR to print from a stream
- call back when file prints (e.g. for progress indicator)
- refactor ad fundum
History
- 2006/12/24 - Version 1.06 - Added some comments, a status string,
InternalQueueSize, and filesSend.
- 2006/12/24 - Version 1.03 - Added delete flag (thanks to Dion Slijp).
- 2006/11/09 - Version 1.02 - Patched code with remarks of Karl Fleischmann.
- 2006/01/14 - Version 1.01 - Added host, queue + user name to demo
- 2006/01/02 - Version 1.00 - published on CodeProject.
- 2006/01/02 - Version 0.96 - added Restart, fixed minor bugs, updated CP page.
- 2005/12/31 - Version 0.92 - added
WriteLog, added LPRM.
- 2005/12/30 - Version 0.90 - refactoring protocode, writing initial CP page.
- 2005/??/?? - started with the Printer class.
Usage rights
Everybody is granted to use this code as long as you refer to the original work, and I would appreciate that enhancements are published at CodeProject too.
| You must Sign In to use this message board. |
|
| | Msgs 1 to 20 of 20 (Total in Forum: 20) (Refresh) | FirstPrevNext |
|
 |
|
|
I love this article because it is very close as I'm looking for. I'd like to know if is it possible to use as a webservice, where an application will get the response of one webpage, and then, will print a page without user commands ?
Thanks in advance, Best regards
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Malukinho,
Don't know your requirements in detail but to keep things nicely separated I propose to make one class that generates the file to be printed. When the file is generated it can be offered to the LPR class. Similar to the thread "Printing contents of a textbox to a Network printer using ASP.net" just below.
If you don't have high performance requirements you can store the file as a temporary file and use the LPR class to print it. Note that most of the time the speed of the printer will be the performance bottleneck. This way you can use the LPR client class just as is. If you need more performance the class should be rewritten so it exposes lower level methods so you can open the printer, fill a buffer, send, ...., and close printer yourself.
Hopes this helps,
regards, Rob Tillaart
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Rob, The class was excellent it works great for a windows application. Can you please help me implement it for a web application. The Description for the Project i'm doing is as below:
I have to print a label to a Datamax printer by a ASP.Net web application (C#). The contents to be printed will be given in a Text box.
The Datamax label printer is a Network shared printer whose IP address is known to the web application Developer.
The Web application should use this IP address to communicate to the printer, so that later when the printer is changed we need to change only the IP address in the Web app.
Any one in the network who opens this web application and clicks Print should be able to print a label to that particular printer without any change in printer settings or any installations.
It is a single web application no extra forms can be added.
Kindly Help me in this issue. Thanks in Advance
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Member4088318,
Excuses for the long delay for the answer. Many threads ask for high priority 
The simplest way to solve the problem is to store the text to print in a temporary file and print this file. This could also be part of the printer class. I have made a first implementation which stores the text in a file with a random name to minimize the chance of collisions. Then this temporary file is printed as usual. Because it is a temporary file I give the flag deleteafterprint the value true. Setting this flag to false may help to debug it.
Just include the code snippet below in the printer class. Change the value of temppath to fit your need & system. Temppath should be a property of the printer class ...
public void LPRtext(string text) { Random r = new Random(); string temppath = @"C:\"; string fn = temppath + "lpr" + r.Next(999999999).ToString(); StreamWriter sw = new StreamWriter(fn); sw.Write(text); sw.Close(); // print and delete afterwards.. LPR(fn, true); }
Hopes this solves your need.
regards, rob
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
First off, thank you for the code--it's an excellent LPR implementation.
Any idea how to implement a "real" status request (i.e. query the printer for current state, errors, etc.) Perhaps I missed this in RFC 1179.
Thanks.
Michael Shearer
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Michael,
In RFC 1179 there is no printer state defined, only the queue state by means of the LPQ (short or long) command. The data and its formatting LPQ returns is not specified. This makes it hard to parse output from different printers or devices behaving like printers e.g. print server (windows/linux/..), fax server etc.
My preferred way to interrogate printers about their state is SNMP (Simple Network Management Protocol) and to use the printer Mib RFC 3805 (replaces 1759). With this one can see if the device is idle or running or in error, also the number of papertrays and the amount of paper in them can be requested. But be aware that this only works for printers that support the printer MIB. Note that some printers support the PM partially.
There are several articles about using SNMP in C# e.g. http://www.codeproject.com/useritems/SNMPDLL.asp[^].
Additional to SNMP one can use the PING command to see if the printer is reachable in the first place.
Succes,
Rob Tillaart
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Your code was really great!
but, in my work, i have to print in text mode only (just send txt file to the printer).
can you give me some comment?
ps. sorry if this is the stupid question.
thx in adv vinai
-:I'm a really Newbie:-
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Vinai,
Look for the follow piece of code in the file lprdemo.cs: (approx line 250)
void Button1Click(object sender, System.EventArgs e) { if ((HostName.Text == "") || (QueueName.Text == "") || (UserName.Text == "")) { MessageBox.Show("Please fill in host, queue and username"); return; } if (openFileDialog1.ShowDialog() == DialogResult.OK) { textBox1.Text = DateTime.Now.ToString() + " busy"; Printer printer1 = new Printer(HostName.Text,QueueName.Text,UserName.Text); printer1.LogFile = @"c:\lpr.log"; string fname = openFileDialog1.FileName; if (fname.EndsWith(".ps") || fname.EndsWith(".pdf")) { printer1.LPR(fname, false); textBox1.Text = DateTime.Now.ToString(); textBox1.Text += printer1.ErrorMsg; } else { // error message textBox1.Text = "File does not end with .ps or .pdf."; } } }
Change the line that tests the extention:
if (fname.EndsWith(".ps") || fname.EndsWith(".pdf"))
in
if (fname.EndsWith(".ps") || fname.EndsWith(".pdf") || fname.EndsWith(".txt"))
and you can select *.txt files too and print them. You can also restrict the LPR client to ".txt" only. Then the line should become:
if (fname.EndsWith(".txt"))
Except for the end of the name you could also restrict files to be printed in other ways. E.g. you could only restrict printing to filenames matching some regular expression, or if the files are at least 2 days old, have a filesize > 0, etc.
You should also edit the errormessage. In theory you can send all filetypes by means of the LPR client (adapt changes of Karl Fleishman for binary files, see forum) but be aware that the receiving device should support the fileformat. There are PostScript printers that does not support .txt. Printers that support PCL will print .txt files as plain text is a subset of PCL.
Succes, Rob PS, There exist no stupid questions only stupid answers 
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
Hi Rob, Can you also through some light how to write the server side of the code as well.
jayprakashshetty@hotmail.com CANADA
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Jay,
Sorry it took some time to answer. Currently I am busy writing a prototype of an LPD server and it will be posted as a separate article to CP soon. Do you need special functionality? Let me know and maybe I can include it.
regards, Rob
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
I guess I'm missing something. Exactly which scenario I would want to use this code?
Regards, Shital. http://www.ShitalShah.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Shital,
The LPR class can be used when you want to send a print-ready file to a printer. This file can be a PostScript, PDF, PCL or even a txt file depending on the capabilities of the (network)printer. One might have created this file by means of the 'print to file' option in a printerdriver dialog.
With this class one could build a simple commandline tool that e.g. sends all PDF files in a directory (CD) to a printer.
The class can also be used when you generate the PostScript yourself from within your application. OK, with the current implementation you need to write the PostScript to a file first. A stream interface would be a very nice enhancement.
Major problem for the stream interface is that the size of the data file is unknown and therefor the SubCommand 3 'RECEIVE DATA FILE' cannot give a correct filesize as parameter. In this case one should give 0xFFFFFFFF as filesize, but some printers do not cope with this very well as they allocate memory for the datafile in one chunk => an alloc of 4GB => POOOF!!
A well known way to circumvent this problem is to make a local copy of the PostScript on disk first and only send the file to the printer when the stream is closed. Then the size is known and the memory problem does not occur. OK, when the PostScript file is very large the problem still exists, but these are rare. FYI the largest PostScript file I encountered was approx. 1.2 GB.
Hopes this answers your question,
Regards, Rob Tillaart
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hey Rob, Great class, I'm using it quite a bit and it's been working great. However recently we experienced a problem with the byte streaming portion of the class. Here's what we discovered and how we worked around it.
It appears that if the file your trying to print has a byte that can't convert into a character the "while (br.PeekChar() > -1)" line in the LPD.Printer.SendFile() method will throw a "Conversion Buffer Overflow" exception. To work around this you can use the following code which is an alternative way to stream the bytes over the NetworkStream without the conversion error. The commented code is the way it was originally.
// use BinaryReader as print files may contain non ASCII characters. long totalbytes = 0; int bytesRead=0; byte[] buff = new Byte[2048]; FileStream fstream = new FileStream(fname, FileMode.Open); while ( (readed=fstream.Read(buffer, 0, BUFSIZE)) > 0 ) { totalbytes += bytesRead; nws.Write(buffer, 0, bytesRead); nws.Flush(); } fstream.Close();
//FileStream fs = new FileStream(fname, FileMode.Open, FileAccess.Read); //long totalbytes = 0; //BinaryReader br = new BinaryReader(fs); //while (br.PeekChar() > -1) //{ // int n = br.Read(buffer, 0, BUFSIZE); // totalbytes += n; // nws.Write(buffer, 0, n); // nws.Flush(); //} //br.Close(); //fs.Close();
Here's the web page where we found the solution to this problem. http://www.codecomments.com/message507645.html[^]
Have a Great Day!
www.karlfleischmann.com It's better out here.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Karl,
It is good to hear the project is used. As I used only text files I didn't encounter this problem. Thanx for the patch, when I find some time I will update it in the source. Think the 'readed' variable in the while loop should be bytesRead otherwise no bytes are written. So it becomes:
long totalbytes = 0; int bytesRead = 0; byte[] buff = new Byte[2048]; FileStream fstream = new FileStream(fname, FileMode.Open); while ( (bytesRead = fstream.Read(buffer, 0, BUFSIZE)) > 0 ) { totalbytes += bytesRead; nws.Write(buffer, 0, bytesRead); nws.Flush(); } fstream.Close();
Thanx again for the patch,
rob tillaart
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
It limits the size of lines with 80. If I have a file which has some lines exceed 80 characters, it prints left of the line (after 80th chr.) into new line.
Is there any way that I can customize formats of the documents
-- modified at 14:47 Friday 21st April, 2006
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi nkabiloglu,
Is this problem still unsolved?
I do not understand your problem in context of the LPR class as it does not truncate lines. However some applications like NotePad do truncate lines (wordwrap) in their window and therefor the lines will appear truncated when printed. Also some ascii to PostScript converters do this.
Which application do you use? Has it a wordwrap option?
You might try to 'debug' this problem by printing from the application to PostScript file and read it with GhostScript - GhostView. If lines are truncated on screen the problem lies in the application or the printerdriver and not in the printer or the LPR class.
Regards, Rob Tillaart
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
This one seems to be missing a little. Like maybe the whole article.
Paul Kennedy
Father/Son/Husband/C Programmer What else could you ask for?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Paul,
The article is available at this moment, or are you still missing something?
I saw that the demo app must be updated as you cannot fill in the hostname and queuename in the app. Will try to fix this this weekend. I will save the old code in the My Mistakes folder 
regards, rob
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Rob, you probably need to bulk up the article with some insides on have the LPR protocol works (diagrams etc) and maybe show how you're code implements it.
Blogless
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
General News Question Answer Joke Rant Admin
|