Windows Printing using win32print

Geoffrey Gerrietts geoff at homegain.com
Tue Jun 26 16:10:14 EDT 2001


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 <geoff at 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)




More information about the Python-list mailing list