6
votes

I'm trying to use win32api to output a PDF document to a particular printer.

win32api.ShellExecute(0, "print", filename, '/d:"%s"' % printername, ".", 0)

filename is a full pathname to the file, and printname is the name of the target printer I get by going through the output of win32api.EnumPrinters(6).

The file is sent to the Windows default printer even if printername is the name of a different target (my expectation is that passing a specific printer would send the named file to that printer, rather than the default).

Any hints as to what I'm doing wrong? Is there a different way of generically printing a PDF file to a specific printer? Barring all else, is there a way of temporarily changing the default printer from my program?

3
Ive found that it depends on the filetype... a *.html will prompt for the printer of your choice, a *.pdf just goes to default ... none of the "workarounds" I have seen actually work ... and we use this in a software package that is used by lots of researchers/farmers ...Joran Beasley
@JoranBeasley - Huh. Any other approaches you know of then, or am I SOL as far as you know?Inaimathi
SOL i think ... It gets worse it depends on your default PDF handler (PDFComplete doesnt work at all for example) ... the alternative is to open it in their default pdf viewer and let them print from thereJoran Beasley
If you were to save it as HTML instead of PDF you can get teh printer selection window ... we spent lots of engineering hours to come to that conclusion ...Joran Beasley
@JoranBeasley - That doesn't help. The point of the program would be to route print jobs without supervision; having to pick the printer each time defeats the purpose of using ShellExecute in the first place.Inaimathi

3 Answers

4
votes

MikeHunter's answer was a decent starting point.

The proposed solution is calling out to Acrobat or Acrobat Reader to do the actual printing, rather than going through the win32api. For my purposes, this is sufficient:

from subprocess import call

acrobat = "C:\Program Files\Adobe\Acrobat 7.0\Acrobat.exe" ## Acrobat reader would also work, apparently
file = "C:\path\to\my\file.pdf"
printer = "Printer Name Goes Here"

call([acrobat, "/T", file, printer])

That starts up Acrobat, and prints the given file to the named printer even if it's not the Windows default. The first print job processed this way takes a few seconds (I'm assuming this is the Acrobat service being started and cached in memory), subsequent jobs print instantly. I have not done any kind of load testing on this, but I assume the call is less than trivial, so don't trust it for massive throughput.

1
votes

I'm trying to print any old file to a specific printer, so these answers did not help me. However, I did find the perfect solution. Windows has a canonical verb called printto that does not show up in the context menu. It is used as a way for users to drag and drop a document onto a printer to enable printing in that manner. We can use that feature; the second argument is the name of the printer. I could never get the /d: parameter to work correctly in conjunction with the print canonical verb, but this solution solved it for me. I put the printername in quotes in case there are spaces in it.

win32api.ShellExecute(0, "printto", filename, f'"{printername}"', ".", 0)
0
votes

I use SumatraPDF to achieve a similar solution (Python 3) as user Inaimathi posted:

import time
from subprocess import call

start = time.perf_counter()
sumatra = "C:\\Program Files\\SumatraPDF\\SumatraPDF.exe"
file = "C:\\Users\\spiderman\\Desktop\\report.pdf"

call([sumatra, '-print-to-default', '-silent', file])
end = time.perf_counter()
print("PDF printing took %5.9f seconds" % (end - start))

The list of command-line arguments you can pass to SumatraPDF is here.