First, let's take a look at the overall scheme of operation. We will use Samba to provide a "pseudo-printer" service (it will look like a standard printer to clients) that will use Ghostscript to create a PDF document out of any Postscript printer job that is queued onto it. We will then configure the Windows machines to use this shared printer and send jobs to it in Postscript form.
If this is your first time installing Samba, you will want to review/edit some of the basic configuration options in the smb.conf (look in /etc or /etc/samba) configuration file. The main things to watch in order to get your services up and running are the security policy (security=share or security=user) and the "guest account" setting. For details of configuring Samba, refer to the Samba documentation at www.samba.org or the SMB HOWTO. A complete sample (low-security) configuration file will be shown later.
It is probably advisable to test your connection and authentication method (if any) by creating a simple file share for your clients. In any event, once your clients are able to connect to your Samba server, we are ready to create the PDF "pseudo-printer". First, though, let's make sure we have the right utilities to actually produce the PDF documents.
The Ghostscript package includes a script called ps2pdf that makes the conversion of Postscript to PDF quite easy. Now that we have this utility available, we can begin the creation of our PDF service on Samba.
[hpdeskjet] path = /tmp printable = yes writeable = no create mask = 0700 guest ok = yes printer name = lp
(Note the silent "e" in writeable
. The configuration file has
it even though the ordinary word doesn't. The same applies to
browseable
below. Actually, Samba accepts it either way, but
Samba's manpages use writeable
.)
Normally, when a print job is spooled to this share, a command such as lpr is run to transfer the job to the Linux printing system. Our method here is to use the excellent configurability of Samba to specify an alternate printing command in place of lpr. Specifically, the configuration variable is called "print command". The specified command is executed, and any occurrence of %f or %s in the "print command" variable will be replaced by the name of the printer spool file that was sent in by the windows client. For example, to simply discard any print jobs, this line could be placed in the above printer configuration:
print command = /bin/rm %fThis brings up another important point: whatever print command is specified must delete the spool files, or else they will eventually pile up and fill your disk.
chown nobody /shr/pdfdropbox chmod u+rwx /shr/pdfdropboxHere is the complete, yet simple print script, called printpdf, also available in text format. On our Linux system, we'll place the script at /usr/bin/printpdf
#!/bin/sh # Simple script to convert a specified postscript file into a PDF document # and place it in a location that is shared by the Samba server. # # Arguments: # 1st - The name of the spool file # # John Bright, 2001, jbright@winfordeng.com # We will create the pdf into a temporary file based upon the current date and time. # After we are finished, we'll rename it to a file with the same date, but ending # in .pdf. We do this because if a user tries to open a PDF that is still being written, # they will get a message that it is corrupt, when it is actually just not done yet. DATE=`date +%b%d-%H%M%S` # Directory in which to place the output # Be sure this directory exists and is writable by the user that Samba # is running as (for example, the nobody user) OUTDIR=/shr/pdfdropbox ps2pdf $1 $OUTDIR/$DATE.temp mv $OUTDIR/$DATE.temp $OUTDIR/$DATE.pdf rm $1
I said it was simple, right? There's really not much to it once we have all of the tools together.
[global] guest account = nobody invalid users = root ; Tighten security just a little: only allow local access interfaces = 127.0.0.1 eth0 bind interfaces only = Yes ; This assumes you are on a local network with 192.168.x.x IP addresses hosts allow = 192.168. ; Share-level security is generally easier, although not as secure security=share workgroup=WORKGROUP ; Set up a public share, this will be used to retrieve PDFs ; The name of the share will be seen as "shr" by Windows users [shr] path = /shr browseable = yes writeable = yes guest ok = yes force user = nobody ; Set up our PDF-creation print service [pdf] path = /tmp printable = yes guest ok = yes print command = /usr/bin/printpdf %s ; There is no need to support listing or removing print jobs, ; since the server begins to process them as soon as they arrive. ; So, we set the lpq (list queued jobs) and lprm (remove jobs in queue) ; commands to be empty. lpq command = lprm command =
Of course, you will need to start/restart Samba after you have created/edited the smb.conf configuration file to your liking.
To briefly explain how this is fitting together, the PDF service on your Linux machine is expecting to receive input in Postscript format. Since our printpdf script receives the print job exactly as it was sent by the Windows client, this means we need to have the Windows clients send print jobs in Postscript form. As described above, this is done by selecting a driver for any Postscript printer on the Windows client when the PDF network printer is installed. I generally select some variety of HP Laserjet PS printer from Windows' printer driver list (such as the HP LaserJet 5P/5MP PostScript, as noted above) although it doesn't matter a whole lot because all of the Microsoft-supplied Postscript drivers use the same core driver to generate the Postscript.
Once you have the PDF network printer installed on your Windows machine, simply print anything from any program to your new network printer, and you should have a PDF document waiting for you shortly.
Now we need to find out which driver files must be copied into the printer$ share directory. We also need to give Samba a printer definition so it can tell the client which driver files it needs. It turns out all this is taken care of in one step thanks to a Samba utility called make_printerdef. This utility requires you to have the Windows INF file that defines your printer and know the full title, such as "HP LaserJet 5P/5MP PostScript". You will need to find which INF file your printer is defined in. For example, this LaserJet is defined in C:\WINDOWS\INF\MSPRINT3.INF. Note that C:\WINDOWS\INF is a hidden directory. Copy this file onto your Linux machine and use the make_printerdef utility to create a local printer definition file that Samba will read. For example:
make_printerdef MSPRINT3.INF "HP LaserJet 5P/5MP PostScript" >> /etc/samba/printers.def
Here we redirected standard output to the printers.def file to create the printer configuration. The make_printerdef program also outputs some explanation on standard error which you will see. It should tell you which driver files you need. You can find these in C:\WINDOWS\SYSTEM or C:\WINDOWS and you should copy them into the path of your printer$ share on the Linux machine (in our case, /etc/samba/printerdrivers/). The printers.def file that we have created (or appended to) here does not need to be shared to the Windows machines, it is only read by Samba. Now we just have to tell Samba about the printers.def file and our driver files. This is done with the "printer driver file" setting in the global section and the "printer driver" and "printer driver location" settings in each printer section of smb.conf. The following revised smb.conf file shows how these settings are used, and also shows an example of a printer$ share. You can download this configuration file from here.
[global] guest account = nobody invalid users = root ; Tighten security just a little: only allow local access interfaces = 127.0.0.1 eth0 bind interfaces only = Yes ; This assumes you are on a local network with 192.168.x.x IP addresses hosts allow = 192.168. ; Share-level security is generally easier, although not as secure security=share workgroup=WORKGROUP printer driver file = /etc/samba/printers.def ; Set up a public share, this will be used to retrieve PDFs ; The name of the share will be seen as "shr" by Windows users [shr] path = /shr browseable = yes writeable = yes guest ok = yes force user = nobody ; Set up our PDF-creation print service [pdf] path = /tmp printable = yes guest ok = yes print command = /usr/bin/printpdf %s ; There is no need to support listing or removing print jobs, ; since the server begins to process them as soon as they arrive. ; So, we set the lpq (list queued jobs) and lprm (remove jobs in queue) ; commands to be empty. lpq command = lprm command = ; We already defined the printer driver definition file above. ; Here we need to specify the entry in that file that should be used ; for this printer. printer driver = HP LaserJet 5P/5MP PostScript printer driver location = \\%h\printer$ ; File share to allow clients to download printer drivers [printer$] path = /etc/samba/printdrivers guest ok = yes read only = yes
There are numerous ways that you can print to a Windows printer share from Linux. Probably the best is to list the smbprint script (which uses smbclient) as a filter in an /etc/printcap entry. When this method is used, a Windows shared printer can be used with the standard lpr command that Linux users and applications are accustomed to. You will need to make sure you have both the smbprint and smbclient programs on your computer. The smbclient program is in the "smbclient" package on Debian systems and the "samba-client" package on Red Hat systems. On Red Hat, the smbprint script is included with the "samba-client" package. On Debian, it is included with the "samba-doc" package as well as a different version in the "printfilters-ppd" package and "lprngtool". There are so many different versions floating around that I thought it best to include a copy here. You can download it from here. In any event, I'll assume that you have a working smbprint at /usr/bin/smbprint and that it is executable (chmod +x /usr/bin/smbprint). Here is the smbprint script:
#!/bin/sh # This script is an input filter for printcap printing on a UNIX machine. It # uses the smbclient program to print the file to the specified smb-based # server and service. # For example you could have a printcap entry like this # # smb:lp=/dev/null:sd=/usr/spool/smb:sh:if=/usr/local/samba/smbprint # # which would create a UNIX printer called "smb" that will print via this # script. You will need to create the spool directory /usr/spool/smb with # appropriate permissions and ownerships for your system. # Set these to the server and service you wish to print to # In this example I have a Windows for Workgroups PC called "lapland" that has # a printer exported called "printer" with no password. # # Script further altered by hamiltom@ecnz.co.nz (Michael Hamilton) # so that the server, service, and password can be read from # a /usr/var/spool/lpd/PRINTNAME/.config file. # # In order for this to work the /etc/printcap entry must include an # accounting file (af=...): # # cdcolour:\ # :cm=CD IBM Colorjet on 6th:\ # :sd=/var/spool/lpd/cdcolour:\ # :af=/var/spool/lpd/cdcolour/acct:\ # :if=/usr/local/etc/smbprint:\ # :mx=0:\ # :lp=/dev/null: # # The /usr/var/spool/lpd/PRINTNAME/.config file should contain: # share=PC_SERVER # user="user" # password="password" # # Please, do not modify the order in the file. # Example: # share=\\server\deskjet # user="fred" # password="" # # The last parameter to the filter is the accounting file name. # Extract the directory name from the file name. # Concatenate this with /.config to get the config file. # eval acct_file=\$$# spool_dir=`dirname $acct_file` config_file=$spool_dir/.config # Should read the following variables set in the config file: # share # hostip # user # password eval `cat $config_file` share=`echo $share | sed "s/[\]/\//g"` if [ "$user" != "" ]; then usercmd="-U" else usercmd="" fi if [ "$workgroup" != "" ]; then workgroupcmd="-W" else workgroupcmd="" fi if [ "$translate" = "yes" ]; then command="translate ; print -" else command="print -" fi #echo $share $password $translate $x_command > /tmp/smbprint.log cat | /usr/bin/smbclient "$share" "$password" -E ${hostip:+-I} \ $hostip -N -P $usercmd "$user" $workgroupcmd "$workgroup" \ -c "$command" 2>/dev/null
The next step is to add a new /etc/printcap entry and list the smbprint script as a filter. Here is an example printcap entry (or complete file), also available as a text file:
# PDF Service entry lp|pdf|PDF Printer:\ :lp=/dev/null:sh:\ :sd=/var/spool/lpd/pdf:\ :af=/var/spool/lpd/pdf/acct:\ :mx#0:sh:\ :if=/usr/bin/smbprint:
You will need to create the spool directory /var/spool/lpd/pdf/ (or if you have LPRng, run checkpc -f). Be sure to keep the accounting file line in the printcap entry, and be sure the accounting file is located in the same directory as your .config file, as this is how the smbprint script finds the .config file. Also, it is standard procedure to have the system's default printer named "lp" as shown above. If you already have a /etc/printcap file and would like to retain your existing default printer, you should remove the leading "lp|" from the entry shown above. Next, you need to create a configuration file named ".config". You should create this at /var/spool/lpd/pdf/.config . The .config file defines which server the print job should be sent to. Here is an example:
share=//yourserver/pdf user="" password=""
Here, yourserver should be replaced by the name of the computer providing the PDF service. If you have any trouble with this, make sure that the smbprint script has permission to read the .config file, or you may be scratching your head for a while. Probably the safest way, at least at first, is to give read permission to all, for example: chmod a+r /var/spool/lpd/pdf/.config
Finally, to print to the PDF service from Linux, invoke the command:
lpr -Ppdf file_to_print.pson a Postscript file. This can also be used from within most applications. For example, listing "lpr -Ppdf" as the print command in Netscape will allow you to create a PDF document from a web page.
In my opinion, gnome-gv has the nicest user interface. It is based on GTK+, so things like the mouse scroll wheel work without any special consideration. Unfortunately, it will fail to read some PDF documents and display a nasty-looking error from ghostscript. From my experience, acroread is very good about being able to interpret documents. In the past I have had some trouble with it crashing, but I think it has gotten better since then. I have rarely used gv, but I imagine it has the same problem as gnome-gv since they are both based on ghostscript. Finally, xpdf is a very stable PDF reader. I don't recall every having it crash, and it usually has no problem interpreting documents. Still, there is an occasional problem, and the displayed quality of the document often isn't quite up to par. It doesn't have a full feature list, but it is a good viewer to keep around. All this may sound scary, but be assured that on average, PDF viewing on Linux is not a problem.
Have fun and good luck!
John Bright
John Bright is a partner in
Winford Engineering
and flawlessly performs his assigned programming and Linux
administration duties :). He also administers several Linux/UNIX
computers at a local university and always has several
Linux-related projects to keep him busy.
Copyright © 2001, John Bright.
Copying license http://www.linuxgazette.net/copying.html
Published in Issue 72 of Linux Gazette, November 2001