Python's win32api only printing to default printer
Asked Answered
A

4

6

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?

Anathema answered 27/9, 2012 at 17:28 Comment(7)
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 ...Bram
@JoranBeasley - Huh. Any other approaches you know of then, or am I SOL as far as you know?Anathema
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 thereBram
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 ...Bram
@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.Anathema
yeah it wont work with this method at all :/ ... I haave not found a viable alternative ...Bram
THIS IS THE BEST ANSWER, THIS SOLVES IT!! https://mcmap.net/q/1682394/-python-39-s-win32api-only-printing-to-default-printerHartsell
A
5

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.

Anathema answered 28/9, 2012 at 14:29 Comment(1)
What does the /T do?Pirtle
S
3

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)
Skeie answered 30/3, 2021 at 14:54 Comment(5)
this is not working on 2 printers it will always print to the default printerFrivolous
I seem to still be using this method to print to various printers. Verify the syntax and the printer name. It must be a valid printer name; sometimes those can be tricky to get just right, especially if they're shared or networked printers.Skeie
thank you the name was the issue this approach working great but the printer name shouldn't have spaces. but can you help me I need to print text not a file? I tried io.BytesIO but can't get it to workFrivolous
Without doing a bunch of research and assuming your project has few users, I would just write to a file first. Then print that file.Skeie
thank you I already did this and it's working fineFrivolous
D
1

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.

Doering answered 6/10, 2020 at 11:11 Comment(0)
S
1

The best way I found is:

  1. set the default printer to the printer you need

    current_printer = win32print.GetDefaultPrinter()

    os.system(f"RUNDLL32 PRINTUI.DLL,PrintUIEntry /y /n {name of needed printer}")

  2. Print file

    win32api.ShellExecute(0, "print", "{document}", '/d:"{name of printer}"', ".", 0)

  3. Restore old printer as the default

    time.sleep(3)

    os.system(f"RUNDLL32 PRINTUI.DLL,PrintUIEntry /y /n {current_printer}")

Sora answered 23/6, 2022 at 17:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.