Python静默打印PDF文件到指定打印机

25

我有一个PDF文档,希望使用我的Python应用程序打印它。

我已经尝试了这里的解决方案(使用Python的win32print模块打印PDF文档?),但是当我安装实际版本的Ghostscript 9.15时,它没有gsprint.exe

目前我使用的方法是使用命令os.startfile('PDFfile.pdf', "print"),但它会打开默认查看器(我的是Adobe Reader),打印后它仍然保持打开状态,使用os.system("TASKKILL /F /IM AcroRD32.exe") 杀死进程会关闭其他打开的窗口,我不想这样。

使用下面的命令也可以打印,但是它会让Adobe Reader保持打开状态。

currentprinter = win32print.GetDefaultPrinter()
win32api.ShellExecute(0, "print", 'PDFfile.pdf', '/d:"%s"' % currentprinter, ".", 0)

我也看到了这个答案,但他们建议再次使用gsprint.exe

有人有gsprint.exe文件或其他解决方案吗?

注意:当我使用其他默认程序打开PDF文件,如Chrome或Windows Reader时,总是会出现执行上述命令时的异常'(31, 'ShellExecute', 'A device attached to the system is not functioning。')[Error 1155] No application is associated with the specified file for this operation: 'PDFfile.pdf'使用startfile命令

9个回答

25

经过数小时的寻找正确的文件,我终于找到了解决我的问题的答案。

您可以在这里下载GSPRINT。

您可以在这里下载Ghostscript GPL。

有了这些提取出来的文件在您的PC(Windows)上,您可以使用此命令打印您的PDF文件。

GHOSTSCRIPT_PATH = "C:\\path\\to\\GHOSTSCRIPT\\bin\\gswin32.exe"
GSPRINT_PATH = "C:\\path\\to\\GSPRINT\\gsprint.exe"

# YOU CAN PUT HERE THE NAME OF YOUR SPECIFIC PRINTER INSTEAD OF DEFAULT
currentprinter = win32print.GetDefaultPrinter()

win32api.ShellExecute(0, 'open', GSPRINT_PATH, '-ghostscript "'+GHOSTSCRIPT_PATH+'" -printer "'+currentprinter+'" "PDFFile.pdf"', '.', 0)

GhostScript也可以在官方网站这里找到。

我在这里找到了64位系统的gsprint.exe这里

希望这能帮到你。


2
gsprint.exe的64位链接已失效(2017年10月),但32位的这个链接还可以使用:http://pages.cs.wisc.edu/~ghost/gsview/gsprint.htm - Martin Zaske
1
对于 Windows 系统,这是旧链接。而对于 Ubuntu 或 Linux 系统,该软件包不存在,这是针对 Windows 的特定答案。 - Arpit Singh
这不会悄悄地打印文件 - 它会打开一个窗口大约一秒钟,然后再关闭! - user1251007
gsprint的链接失效了,我找不到它 :( - user16822867
这是gsprint的新链接:https://fossies.org/linux/misc/old/ghost/ghostgum/ - Robin

5

我知道这是一个老问题,但如果有人在寻找解决方案,这里是我是如何解决的。

我正在使用 python 3.8 和 gs9.52 在 Windows 10 64 位系统上,并使用了 python3-ghostscript 库,您可以使用 pip install python3-ghostscript 进行安装。我还使用 pypiwin32 来获取默认打印机名称,您可以使用以下命令进行安装 pip install pypiwin32

下面是可用的脚本:

import tempfile
import win32print
import locale
import ghostscript
import render_to_pdf

pdf = render_to_pdf('print/slip.html', context)
temp1 = tempfile.mktemp('.pdf')
f1 = open(temp1, 'ab')
f1.write(pdf)
f1.close()

args = [
        "-dPrinted", "-dBATCH", "-dNOSAFER", "-dNOPAUSE", "-dNOPROMPT"
        "-q",
        "-dNumCopies#1",
        "-sDEVICE#mswinpr2",
        f'-sOutputFile#"%printer%{win32print.GetDefaultPrinter()}"',
        f'"{temp1}"'
    ]

encoding = locale.getpreferredencoding()
args = [a.encode(encoding) for a in args]
ghostscript.Ghostscript(*args)

需要注意的一点是,我使用'#'而不是'=',因为由于某些原因,'='不起作用。

如果这对你不起作用,请尝试将-sDEVICE开关更改为您的打印机类型,例如当我使用HP LaserJet时,它会提示我,所以我将我的-sDEVICE更改为laserjet,然后它就可以工作了。您可以通过在终端中运行gs -h来获取设备列表。


如果您已经有一个要打印的pdf文件... 您可以将行f'"{temp1}"'替换为f'"{f}"',其中f = os.path.join(os.getcwd(), 'testprint.pdf').replace('\\', '\\\\')。当然,在顶部确保导入import os。如果您满足所有这些要求,则不需要任何内容args = [...]直到import ghostscript行,因为import render_to_pdf未在任何地方定义,也不需要import win32print。感谢您提供这个伟大的代码! - Shmack
1
此外,使用"-dFIXEDMEDIA""-dPSFitPage"作为额外的参数将有助于将内容居中在页面上! - Shmack
忘了提到Ghostscript是在GPL下发布的 - 这意味着如果您将其链接到您的代码中使用或分发它,您必须公开源代码,或者让任何要求副本的人都可以获得它。 - Shmack

3
这里有一种方法可以在与您的Python脚本相同的目录中静默打印PDF,而无需使用gsprint和win32api。它允许更多的GhostScript自定义,比如选择宽度/高度等。
import os
import subprocess
import sys

if sys.platform == 'win32':
    args = '"C:\\\\Program Files\\\\gs\\\\gs9.23\\\\bin\\\\gswin64c" ' \
           '-sDEVICE=mswinpr2 ' \
           '-dBATCH ' \
           '-dNOPAUSE ' \
           '-dFitPage ' \
           '-sOutputFile="%printer%myPrinterName" '
    ghostscript = args + os.path.join(os.getcwd(), 'myFile.pdf').replace('\\', '\\\\')
    subprocess.call(ghostscript, shell=True)

如果您正在使用32位版本的GhostScript,则应使用gswin32c

1
这不会悄悄地打印文件 - 它会打开一个窗口大约一秒钟,然后再关闭! - user1251007
@bdoubleu gswin64cgswin64有什么区别?谢谢。 - xralf
'gswin64c' 是一个控制台应用程序;'gswin64' 使用 Windows 显示其信息。尝试在每个可执行文件中使用参数 -h(获取帮助),您将看到它们之间的区别。 - Red

2
根据之前的回答和其他帖子,我编写了以下脚本,从 Laravel 网站打印 .pdf 和 .ps 文件。
我使用了 python 3.9 和 Ghostscript 9.54 (64位版本)。同时还需要 pywin32 和 python3-ghostscript 库。
import os
import sys
import win32print
import win32api
import ghostscript
import locale

USELESS_PRINTER = ['OneNote for Windows 10', 'OneNote (Desktop)', 'Microsoft XPS Document Writer',
                   'Microsoft Print to PDF', 'Fax']

HELP = """pyPrinter - Author: Arthur SICARD - Date: 19/05/2021
\n-help
\tDisplay this message.
\n-list [-virtual]
\tReturn list of available printer (excepted: """ + ", ".join(USELESS_PRINTER) + """)
\n-file filepath [-printer printer_name] [-virtual]
\tPrint specified file on default system printer. Use -printer to specify printer to use. Printer name must be available un -list response.
\n-batch filepath [-printer printer_name] [-virtual]
\tPrint each document specified un batch file on default system printer. Batch file must be a .txt. Each file to print must be write on its own line.
\tUse -printer to specify printer to use. Printer name must be available un -list response.
\n-virtual
\tUse this option after all other arguments to enable printing on virtual printer 'Microsoft Print to PDF'
"""


# Safe accessor to argv. Return None if index is not set
def getArgv(index):
    try:
        return (sys.argv[1:])[index]
    except:
        return None


# Return list of local printer available without "virtual printer" define in USELESS_PRINTER list.
def getAvailablePrinters():
    printers = win32print.EnumPrinters(win32print.PRINTER_ENUM_LOCAL)
    printer_list = []
    for x in range(len(printers)):
        if printers[x][2] not in USELESS_PRINTER:
            printer_list.append(printers[x][2])
    return printer_list


# Return printer name to use. If -printer is set it will return this value only if value match with available
# printers list. Return a error if -printer not in list. If no printer specified, retrieve default printer and return
# its name. Sometime default printer is on USELESS_PRINTER list so first printer return by getAvailablePrinters() is
# return. If no printer is return display an error.
def getPrinter():
    default_printer = win32print.GetDefaultPrinter()
    if default_printer in USELESS_PRINTER:
        if len(getAvailablePrinters()) == 0:
            print("No printer available, unable to print. Use -virtual if you want enable virtual printer.")
            sys.exit(1801)
        default_printer = getAvailablePrinters()[0]
    if getArgv(2) is not None:
        if getArgv(2) == "-printer":
            printer = getArgv(3)
            if printer in getAvailablePrinters():
                return printer
            else:
                if printer is not None:
                    print("Given printer not found. Defaut printer configured: ", default_printer)
    return default_printer


# Use GhostScript API to silent print .pdf and .ps. Use win32api to print .txt. Return a error if printing failed or
# file ext doesn't match.
def printFile(filepath):
    try:
        if os.path.splitext(filepath)[1] in [".pdf", ".ps"]:
            args = [
                "-dPrinted", "-dBATCH", "-dNOSAFER", "-dNOPAUSE", "-dNOPROMPT"
                                                                  "-q",
                "-dNumCopies#1",
                "-sDEVICE#mswinpr2",
                f'-sOutputFile#"%printer%{getPrinter()}"',
                f'"{filepath}"'
            ]

            encoding = locale.getpreferredencoding()
            args = [a.encode(encoding) for a in args]
            ghostscript.Ghostscript(*args)
        elif os.path.splitext(filepath)[1] in [".txt"]:
            # '"%s"' % enable to encapsulate string with quote
            win32api.ShellExecute(0, "printto", '"%s"' % filepath, '"%s"' % getPrinter(), ".", 0)
        return True

    except:
        print("Printing error for file: ", '"%s"' % filepath, "| Printer: ", '"%s"' % getPrinter())
        return False


def main(argv):
    if len(argv) in [1, 2, 4, 5]:
        cmd1 = getArgv(0)
        filepath = getArgv(1)

        if argv[-1] == "-virtual":
            USELESS_PRINTER.remove('Microsoft Print to PDF')

        # Batch printing mode
        if cmd1 == "-batch" and len(argv) in [2, 4, 5]:
            if not os.path.isfile(filepath) and not os.path.exists(filepath):
                print("Path provide for batch file is not a valid file path or doesn't exist.")
                sys.exit(2)
            if os.path.splitext(filepath)[1] in [".txt"]:
                with open(filepath) as fp:
                    line = fp.readline().strip('\n')
                    while line:
                        if not os.path.isfile(line) and not os.path.exists(line):
                            print("Path provide is not a valid file path or doesn't exist: ", line)
                        else:
                            printFile(line)
                        line = fp.readline().strip('\n')
                fp.close()
            else:
                print("Not supported file format for batch printing.")
                sys.exit(50)

        # Single file printing mode
        elif cmd1 == "-file" and len(argv) in [2, 4, 5]:
            if not os.path.isfile(filepath) and not os.path.exists(filepath):
                print("Path provide is not a file path.")
                sys.exit(2)
            if not printFile(filepath):
                sys.exit(1)

        # Get printers list
        elif cmd1 == "-list" and len(argv) in [1, 2]:
            for printer in getAvailablePrinters():
                print(printer)

        # Display help
        elif cmd1 == "-help" and len(argv) in [1]:
            print(HELP)
            sys.exit(0)
        else:
            print("Unknow option. Use -help to obtain more informations about supported options.")
            sys.exit(50)
    else:
        print("Wrong arguments number. Use -help to obtain more informations about supported options.")
        sys.exit(50)
    exit(0)


if __name__ == '__main__':
    main(sys.argv[1:])

以下命令说明如何使用它: python main.py -help
pyPrinter - Author: Arthur - Date: 19/05/2021

-help
    Display this message.

-list [-virtual]
    Return list of available printer (excepted: OneNote for Windows 10, OneNote (Desktop), Microsoft XPS Document Writer, Microsoft Print to PDF, Fax)

-file filepath [-printer printer_name] [-virtual]
    Print specified file on default system printer. Use -printer to specify printer to use. Printer name must be available un -list response.

-batch filepath [-printer printer_name] [-virtual]
    Print each document specified un batch file on default system printer. Batch file must be a .txt. Each file to print must be write on its own line.
    Use -printer to specify printer to use. Printer name must be available un -list response.

-virtual
    Use this option after all other arguments to enable printing on virtual printer 'Microsoft Print to PDF'

打印一个文件到打印机HP1FF6CC(HP OfficeJet Pro 6970) python main.py -file "D:\my\system\path\to\file\pattern.ps" -printer "HP1FF6CC(HP OfficeJet Pro 6970)" 将一个文件打印到虚拟打印机Microsoft Print to PDF(通常用于文本目的,纸张很快就会昂贵) python main.py -file "D:\my\system\path\to\file\pattern.ps" -printer "Microsoft Print to PDF" -virtual

我很喜欢使用ghostscript模块的这个完整工具。有趣的工作。 但是,我尝试使用您的打印命令来使用该模块,但总是遇到获取pdf文件路径的问题。 由于ghostscript可执行文件找不到pdf文件,会出现打印对话框,起始页设置为0,结束页设置为0。只能通过直接将参数传递给gswin64c.exe来解决此问题,而不是使用ghostscript模块(使用subprocess.Popen())。 - Red

2

如果您想打印特定页面和其他一些参数,您应该在gsprint的参数中指定它们,如下所示:

import win32print
import win32api

GHOSTSCRIPT_PATH = "C:\\path\\to\\GHOSTSCRIPT\\bin\\gswin32.exe"
GSPRINT_PATH = "C:\\path\\to\\GSPRINT\\gsprint.exe"

params = '-ghostscript "'+ GHOSTSCRIPT_PATH  +'" -printer "'+currentprinter+'" -from 1 -to 3 -landscape -copies 1 "1.pdf "'
print(params)

win32api.ShellExecute(0, 'open', GSPRINT_PATH, params, '.',0)

2
以下代码将阻塞当前任务。
for i in range(10):
    currentprinter = win32print.GetDefaultPrinter()
    win32api.ShellExecute(0, "print", 'PDFfile.pdf', '/d:"%s"' % currentprinter, ".", 0)

在输出帮助信息后结束当前任务并不会阻止当前任务的执行

os.system("TASKKILL /F /IM AcroRD32.exe") 

但它也会关闭其他的PDF文件。

如果您无法使用gsprint,请使用Acrobat命令。

import win32print
import subprocess
import time
pdf_file  = 'D:\d1\d1.pdf'
acrobat = 'C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe'
name = win32print.GetDefaultPrinter()
cmd = '"{}" /n /o /t "{}" "{}"'.format(acrobat, pdf_file, name)
for i in range(10)):
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

它不会阻止当前任务并关闭其他PDF文件。

这对我需要的功能运作良好,然而,即使关闭了窗口,我的 Adobe Acrobat 仍然保持开启。是否有一种方法来确保它也关闭? - NMALM
尝试以下代码: cmd = '"{}" /n /o /t "{}" "{}"'.format(acrobat, file_path, printer_name) proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() proc.terminate() - Jisson
我似乎仍然无法使其工作。它关闭了PDF文件,但Adobe Acrobat本身仍然打开着。 - NMALM
尝试最小化它,我想我不会再回来了。对我来说,Adobe出现在任务栏中,但不会处于打开状态。 - Jisson
Adobe闪存打开文件后立即关闭,但Adobe程序仍保持在任务栏中。这就是您所描述的吗?我想知道如何完全关闭Adobe程序。不过,我猜这会关闭用户打开的任何其他文件,这是不好的。有没有符合我所描述的解决方案? - NMALM
1
我相信Adobe会一直停留在任务栏中,我们无法改变它。 - Jisson

1

除了Adobe Reader之外,还有一种方法可以将文件发送到打印机,即使用SumatraPDF。

安装SumatraPDF应用程序并将SumatraPDF.exe的位置添加到路径中。

# import subprocess and os
import subprocess
import os

# file path
file_name = "Document.pdf"
if file_name:
    print("exist")

# send data to the printer

try:
    subprocess.call(['SumatraPDF.exe', '-print-to', "Barcode",
                     '-print-settings', "1x", file_name])

except BaseException as msg:
    print(msg)

无需安装 Adobe Reader,每次打开 PDF 文件也无需使用 Adobe Reader,但需要注意页面方向和页面大小设置问题。如果这些参数一直保持不变的话,我们可以在打印机属性中直接更改。只需将 exe 路径添加到系统文件路径中即可。如下图所示:enter image description here完成翻译。

在折腾了半天之后,这是我能够让它正常工作的唯一解决方案。非常感谢!请注意,您还可以获取便携二进制文件并将其放入项目的根目录中。 - Rex Schrader

0

所以这并不是完全静默的,但它会自动关闭对话框并打印,同时还有一个奇怪的依赖于selenium,这可能不是你所期望的,但对我来说确实起作用了,因为我处在一个不能下载ghostscript或adobe pdf阅读器的世界中。我认为这可能会帮助其他人,在某个地方、某个时间...

from selenium import webdriver
import win32com.client
import win32print
import time

def printpdf(pdf,printer):
    current_printer = win32print.GetDefaultPrinter()
    win32print.SetDefaultPrinter(printer)
    driver = webdriver.Chrome()
    driver.get(pdf)
    time.sleep(1) #Adjust as necessary
    shell = win32com.client.Dispatch("WScript.Shell")
    shell.SendKeys('^p')
    time.sleep(1) #Adjust as necessary
    shell.SendKeys('{ENTER}') #dismiss the print dialog box
    driver.close()
    win32print.SetDefaultPrinter(current_printer)
    

    

0
如果您有Adobe,请尝试这个:
import win32api
import winreg
import subprocess
import time

def get_adobe_executable():
    with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as conn:
        with winreg.OpenKey(conn, r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AcroRd32.exe', 0, winreg.KEY_READ) as hkey:
            value = winreg.QueryValue(hkey, None)
            if value:
                value = '"{}"'.format(value)
                return value.strip()
    return None

def print_pdf_file(file, printer_name=None, secs=5):
    cmd = get_adobe_executable()
    if cmd is None:
        return False
    if printer_name:
        cmd = '{} /h /t "{}" "{}"'.format(cmd, file, printer_name)
    else:
        cmd = '{} /p /h "{}"'.format(cmd, file)
    proc = subprocess.Popen(cmd)
    time.sleep(secs)
    proc.kill()
    
    return True

if __name__ == "__main__":
    print_pdf_file("doc.pdf") # print with default printer
    print_pdf_file("doc.pdf", "HP LaserJet Pro M102") # setting the printer name

get_adobe_executable

从注册表中获取Adobe(您也可以获取打印命令,例如右键单击PDF文件并从菜单中选择“打印”,但我只想获取路径,然后根据打印机配置进行配置)

print_pdf_file

如果您没有设置printer_name变量,则Adobe将使用默认打印机进行打印

执行打印命令后,它将等待5秒钟,然后关闭Adobe程序。 Adobe目前没有命令行选项可在打印文件后退出,在这里您可以查看命令行选项。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接