Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Windows Printing using win32print

2,912 views
Skip to first unread message

Timothy Grant

unread,
Jun 25, 2001, 6:24:04 PM6/25/01
to Python People
I'm not much of a windows programmer, but I am in a situation
where I need to write a test programme that does a lot of very
small print jobs from a Windows box. I have downloaded
win32all, and cobbled together a little app that I think
should, maybe work, but doesn't.

In my first iteration, I simply wrote a text file, then used
os.system('copy filename lpt1:') to print it. This worked just
fine, but one of the variables I need to test is how an
interaction with the actual Windows printers works. To that end
I started reading up on win32print. I was able to come up with
the following...


import win32print

text2print = """
This is a test to see if I can get this text to actually print\n

It will be multiple lines...\n

Watch and see\n
"""
printer = win32print.OpenPrinter('Tektronix Phaser 600 Extended')
jid = win32print.StartDocPrinter(printer, 1, ('TEST DOC', 'c:\\test\\test.tjg', 'RAW'))
bytes = win32print.WritePrinter(printer, text2print)
win32print.EndDocPrinter(printer)
win32print.ClosePrinter(printer)
print bytes


Now this is where my understanding of how things should work
gets a bit shaky. I have read that the WritePrinter() function
should send printer ready data. So since text2print is not in
ps or pcl native format, I'm curious as to whether I'm even
close.

If I do need to send printer ready data, how do I create the
printer ready data from my print string?

Thanks for any assistance you might be able to provide.

--
Stand Fast,
tjg.

Timothy Grant www.hyperlinq.net
Chief Technology Officer t...@hyperlinq.net
HyperLINq Technologies, Inc. <>< (503) 246-3630
>>>>>>>>>>>>>Linux, because rebooting is *NOT* normal<<<<<<<<<
>>>>This machine was last rebooted: 20 days 22:08 hours ago<<

Emile van Sebille

unread,
Jun 25, 2001, 10:35:59 PM6/25/01
to
I'm not sure if this helps with your specific problem is, but I can say that
you need to figure out the name the printer goes by. I've seen different
names for printers working with various applications in windows. Here are
two examples that I'm currently using.

destPrinter = r'NEC Silentwriter 95 v2011.111 on LPT1:'
destPrinter = r"\\NT401\HP Laserjet 4000N Front Desk on NE02:"

I got the names by going into immediate mode from within Office 2000. Here
are my notes.

## to figure out the printer name recognized in MS Office apps,
## - go into the app
## - print a document to the target printer
## - alt-F11 into visual basic
## - go into immediate mode
## - print Application.ActivePrinter
## - copy and paste the name in

Again, not sure it'll help, but hope it does.

--

Emile van Sebille
em...@fenx.com

---------
"Timothy Grant" <t...@hyperlinq.net> wrote in message
news:mailman.993507818...@python.org...

Timothy Grant

unread,
Jun 25, 2001, 11:41:05 PM6/25/01
to pytho...@python.org
Thanks for the input I really appreciate it.

However, The printer name I am using is correct. In fact, when
single stepping through the code in the debugger, I can
actually see the job appear and then disappear from the queue.
Unfortunately nothing prints.

> --
> http://mail.python.org/mailman/listinfo/python-list

--
Stand Fast,
tjg.

Timothy Grant www.hyperlinq.net
Chief Technology Officer t...@hyperlinq.net
HyperLINq Technologies, Inc. <>< (503) 246-3630
>>>>>>>>>>>>>Linux, because rebooting is *NOT* normal<<<<<<<<<

>>>>This machine was last rebooted: 21 days 3:38 hours ago<<

Alan Miller

unread,
Jun 26, 2001, 1:03:06 PM6/26/01
to
Timothy Grant (t...@hyperlinq.net) wrote:
>I'm not much of a windows programmer, but I am in a situation
>where I need to write a test programme that does a lot of very
>small print jobs from a Windows box. I have downloaded
>win32all, and cobbled together a little app that I think
>should, maybe work, but doesn't.
>
>printer = win32print.OpenPrinter('Tektronix Phaser 600 Extended')
>jid = win32print.StartDocPrinter(printer, 1, ('TEST DOC', 'c:\\test\\test.tjg', 'RAW'))
>bytes = win32print.WritePrinter(printer, text2print)
>win32print.EndDocPrinter(printer)
>win32print.ClosePrinter(printer)

I'm not sure of the details because StartDocPrinter and EndDocPrinter
appear to be new additions to win32print and they aren't covered in the
documentation on the web site.

In a VB program I did some time ago, I wasn't initializing the data type
(you're passing 'RAW') and had no problems at all. Looking at some of
the online docs from MS, it's not clear that anything other than an
empty string is required - they have examples that theoretically do the
same thing, but some pass RAW and some pass an empty string.

I'd try passing an empty string in that tuple.

ajm

KellyK

unread,
Jun 26, 2001, 1:52:10 PM6/26/01
to
On Mon, 25 Jun 2001 15:24:04 -0700, Timothy Grant <t...@hyperlinq.net>
wrote:

>In my first iteration, I simply wrote a text file, then used
>os.system('copy filename lpt1:') to print it. This worked just
>fine, but one of the variables I need to test is how an
>interaction with the actual Windows printers works. To that end
>I started reading up on win32print. I was able to come up with
>the following...
<snip>

Here is what I use to send HPGL jobs to an HP 1055 plotter. The PJL
commands set the job name (so the jobname appears on the printer's LCD
screen) and switch the printer into HPGL mode. It has been a while but
I seem to remember that I needed to have the switch to HPGL mode even
though the "copy file lpt1" style worked. Perhaps you will need
something similar?

import win32print, os

UEL= "\x1b" "%-12345X" # control code to get to PJL
p= win32print.OpenPrinter(r"\\server\design10")
filename= "blah.000"
jobname= os.path.splitext(os.path.split(filename)[1])[0]
j= win32print.StartDocPrinter(p, 1, (jobname, None, None))
f= open(filename, "rb")
buffer_size= 100000
t= f.read(buffer_size)
win32print.WritePrinter(p, UEL + "@PJL JOB NAME = \"" + jobname +
"\"\n@PJL ENTER LANGUAGE = HPGL2\n")
while t:
win32print.WritePrinter(p, t)
t= f.read(buffer_size)
f.close()
win32print.WritePrinter(p, UEL + "@PJL EOJ \"" + jobname + "\"\n" +
UEL)
win32print.EndDocPrinter(p)
win32print.ClosePrinter(p)

KellyK

unread,
Jun 26, 2001, 2:11:58 PM6/26/01
to
On Mon, 25 Jun 2001 19:35:59 -0700, "Emile van Sebille"
<em...@fenx.com> wrote:

>I'm not sure if this helps with your specific problem is, but I can say that
>you need to figure out the name the printer goes by. I've seen different
>names for printers working with various applications in windows. Here are
>two examples that I'm currently using.

<snip>

Something like this will list the names of network connected and local
printers:

for p in win32print.EnumPrinters(win32print.PRINTER_ENUM_CONNECTIONS):
print p[2]

for p in win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL):
print p[2]

Full documentation on EnumPrinters is on Microsoft's site
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/hh/gdi/prntspol_9fjn.asp

Geoffrey Gerrietts

unread,
Jun 26, 2001, 4:10:14 PM6/26/01
to Timothy Grant, pytho...@python.org
It's funny, I was asking a question about printing thru a different
mechanism prior to this discussion coming up. I'm using the Windows GDI
calls to paint text on printer device context.

I've been writing code to dump a simple text file to the printer. I think
that I've seen a half-dozen people talking about ways to do it, but none of
them are very simple or straightforward.

The solution I arrived at is below -- it's pretty easy to change the
file.readlines() call to a string.split() call and make this print multiline
strings for you. It's not elegant code, or even something I particularly
want my name associated with just yet, but it does do the trick.

The big "issue" right now is that it seems to underrun its pages somewhat,
and nobody seems able to tell me how to retrieve the page dimensions from
the device context.

Anyone who can help with /that/, I'd appreciate greatly. :)

(code follows .sig)

Thanks,
--G.

---
Geoff Gerrietts <ge...@homegain.com>
Software Engineer, HomeGain.com
510-655-0800 x4320

import win32ui, win32con
import math, string, sys

# 20 TWIPS per point
# 72 points per inch
# 1440 TWIPS per inch
INCH = 1440

MAXW = int(INCH * 8.5)
BUFW = INCH / 4

MAXH = int(INCH * 11)
BUFH = INCH / 4

fonts = {}
metrics = {}
files = []


def createFonts():
global fonts
normal = {
'name': 'Courier New',
'size': 12,
'weight': 400
}
bold = {
'name': 'Courier New',
'size': 12,
'weight': 700
}
italic = {
'name': 'Courier New',
'size': 12,
'weight': 400,
'italic': 1
}
for i in ['normal', 'bold', 'italic']:
d = locals().get(i)
f = win32ui.CreateFont(d)
fonts[i] = f

def prepareDevice():
global metrics
fonts['orig'] = dc.SelectObject(fonts['normal'])
m = dc.GetTextMetrics()
metrics['width'] = w = m['tmMaxCharWidth']
metrics['height'] = h = m['tmHeight']
lines = int((MAXH - (2 * BUFH))/ (h * 1.5))
lines = lines - 4
cols = (MAXW - (2 * BUFW)) / w
metrics['lines'] = lines
metrics['cols'] = cols


def processFile(file):
global pages, metrics
col = metrics['cols']
lin = metrics['lines']
# in a real app we wouldn't use bigtext
# we'd use readlines here
lines = file.readlines()
formlines = []

# here we break our input into page-oriented lines.
# if we were going to do line numbering and such, this would be the
# place to do it.
for line in lines:
line = line[:-1] # snip the end of line
while len(line) > col:
try:
idx = string.rindex(line," ",0,col-1)
except ValueError:
idx = col-1
formlines.append(line[:idx])
line = line[idx:]
# repeat shortening line until it fits on one line
formlines.append(line)
# do this for all lines in the input

# clean up our memory
del lines

# now paginate
totlines = len(formlines)
pagecount = int(math.ceil(totlines/float(lin)))
pages = []
for i in range(0,pagecount*lin,lin):
page = formlines[i:i+lin]
pages.append(page)

# subtract 'height' increments, and textout at BUFW.

def setup():
global dc, fn, oldfn
dc = win32ui.CreateDC()
dc.CreatePrinterDC()
dc.SetMapMode(win32con.MM_TWIPS)
createFonts()
prepareDevice()

def printPage(page, header):
dc.StartPage()
# would do the page header & footer here ordinarily...
h = metrics['height']
hpos = int((BUFH + (h * 1.5)) * -1)
f = dc.SelectObject(fonts['bold'])
dc.TextOut(BUFW, hpos, header)
dc.SelectObject(f)
# body starts 2 lines + bufh + h down the page
pos = ((h * 3) + BUFH + h) * -1
for line in page:
# print in bufw chars
dc.TextOut(BUFW, pos, line)
pos = pos - int(h * 1.5)
dc.EndPage()

def printDocument(name):
global pages
dc.StartDoc(name)
# calculate the length (in chars) of the page info
s = "%d" % len(pages)
plen = (2 * len(s)) + 4
# make a dict: space reserved for name, and for page info
d = {'name': metrics['cols'] - plen, 'page': plen }
# create a header template based on that
hdrtmpl = "%%-%(name)d.%(name)ds %%%(page)d.%(page)ds" % d

pp = "%d of " + str(len(pages))
for pageno in range(len(pages)):
page = pages[pageno]
p = pp % (pageno + 1)
hdr = hdrtmpl % (name, p)
printPage(page, hdr)
dc.EndDoc()

def openFiles():
global files
flist = sys.argv[1:]

for file in flist:
try:
f = open(file)
files.append(f)
except:
print "Could not open %s" % f

# setup opens the dc, creates the fonts, calculates the metrics
# processFile reads file, handles linebreaks, and paginates
# printDocument prints a parsed document

setup()
openFiles()
for file in files:
processFile(file)
printDocument(file.name)

Alan Miller

unread,
Jun 26, 2001, 6:52:29 PM6/26/01
to
Geoffrey Gerrietts (ge...@homegain.com) wrote:
>It's funny, I was asking a question about printing thru a different
>mechanism prior to this discussion coming up. I'm using the Windows GDI
>calls to paint text on printer device context.
>
>I've been writing code to dump a simple text file to the printer. I think
>that I've seen a half-dozen people talking about ways to do it, but none of
>them are very simple or straightforward.

Actually, I think we're talking about different things - the techniques
in this thread have focused on how to dump a file (text or not, doesn't
really matter) to a specified print queue without changing it in any way
(as in the case where you have something generating PCL or PS and want
to send it to a print queue without having to do it from a command
line).

What you're talking about is how to take a file, format and wrap the
text, and send the resulting output to the printer - really a completely
different beast.

For the question of sizing, I'm not noticing any calls to GetDeviceCaps
or DeviceCapabilities in your code.

GetDeviceCaps can provide information on paper size and non-printable
area size (at least for the top and left - not sure about ways to get
similar values for the bottom and right), though it presumably works
from the default tray selected in the driver.

DeviceCapabilities doesn't seem to provide the info you're looking for
directly, though it does return some information in a DEVMODE structure
that might be useful (items like paper size). I didn't notice anything
about offsets in those docs.

DocumentProperties looks like a way to modify the properties for a
specific print job, but doesn't have any additional information.

ajm

Geoffrey Gerrietts

unread,
Jun 27, 2001, 3:11:39 PM6/27/01
to Alan Miller, pytho...@python.org
Yes, what's being talked about in this thread is win32print.

The original question, though, asked about printing using win32print, when
in fact his output was not printer-ready -- it was a simple text string he
was trying to write to the printer. Consequently, even though he's asking
about win32print, win32print is not the most effective way to manage his
output -- unless he speaks PostScript or HPL (isn't that what HP printers
speak? I forget...) more fluently than he speaks Python, which I'm doubting.

Hence my answer. I don't think I was misconstruing the issue; I think that
the question was not closely read. Maybe I'm wrong, but the information is
out there.

Thanks for the pointer to the device caps stuff -- that's definitely worth
looking deeper into.

different beast.

ajm
--
http://mail.python.org/mailman/listinfo/python-list

Alan Miller

unread,
Jun 27, 2001, 5:59:20 PM6/27/01
to
Geoffrey Gerrietts (ge...@homegain.com) wrote:
>The original question, though, asked about printing using win32print, when
>in fact his output was not printer-ready -- it was a simple text string he
>was trying to write to the printer. Consequently, even though he's asking
>about win32print, win32print is not the most effective way to manage his
>output -- unless he speaks PostScript or HPL (isn't that what HP printers
>speak? I forget...) more fluently than he speaks Python, which I'm doubting.

Right, but any printer that I've run into (plotters are an exception)
can take a raw ASCII data stream and print it as plain text. Postscript
or PCL are only needed if you want something other than fixed-width
Courier.

ajm

Chris Gonnerman

unread,
Jun 27, 2001, 6:22:02 PM6/27/01
to pytho...@python.org
----- Original Message -----
From: "Alan Miller" <a...@enteract.com>> Right, but any printer that I've run
into (plotters are an exception)
> can take a raw ASCII data stream and print it as plain text. Postscript
> or PCL are only needed if you want something other than fixed-width
> Courier.

Not entirely true. Many, many modern inkjet printers don't do raw ASCII.


0 new messages