使用Python和HTMLDOC动态地将html转换为pdf

使用Python和HTMLDOC动态地将html转换为pdf,python,django,htmldoc,Python,Django,Htmldoc,大约一年前,我为一个客户构建了一个Django应用程序。他现在已经把申请转售给了一些超级秘密的政府机构,他们甚至不告诉我申请者的名字 应用程序的一部分使用python库xhtml2pdf(pisa)动态生成PDF文件。政府不喜欢这个图书馆,他们不会告诉我为什么,他们说我必须使用它来生成pdf 关于这个库没有太多文档,但是通过阅读PHP示例,看起来您可以通过shell与它通信,因此它应该可以与Python一起使用。然而,我很难将html传递给HTMLDOC。看起来HTMLDOC只接受一个文件,但我

大约一年前,我为一个客户构建了一个Django应用程序。他现在已经把申请转售给了一些超级秘密的政府机构,他们甚至不告诉我申请者的名字

应用程序的一部分使用python库xhtml2pdf(pisa)动态生成PDF文件。政府不喜欢这个图书馆,他们不会告诉我为什么,他们说我必须使用它来生成pdf

关于这个库没有太多文档,但是通过阅读PHP示例,看起来您可以通过shell与它通信,因此它应该可以与Python一起使用。然而,我很难将html传递给HTMLDOC。看起来HTMLDOC只接受一个文件,但我确实需要将html作为字符串传递,因为它是动态生成的。(或者将html字符串写入临时文件,然后将该临时文件传递给HTMLDOC)

我原以为StringIO可以解决这个问题,但我错了。以下是我所拥有的:

def render_to_pdf(template_src, context_dict):
    template = get_template(template_src)
    context = Context(context_dict)
    html  = template.render(context)
    result = StringIO.StringIO(html.encode("utf-8"))
    os.putenv("HTMLDOC_NOCGI", "1")

    #this line throws "[Errno 2] No such file or directory"
    htmldoc = subprocess.Popen("htmldoc -t pdf --quiet '%s'" % result, stdout=subprocess.PIPE).communicate()

    pdf = htmldoc[0]
    result.close()
    return HttpResponse(pdf, mimetype='application/pdf')
任何想法、建议或帮助都将不胜感激

谢谢

更新

堆栈跟踪:

Environment:


Request Method: GET
Request URL: (redacted)

Django Version: 1.3 alpha 1 SVN-14921
Python Version: 2.6.5
Installed Applications:
['django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.sites',
 'django.contrib.messages',
 'django.contrib.admin',
 'application']
Installed Middleware:
('django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware')


Traceback:
File "/usr/local/lib/python2.6/dist-packages/django/core/handlers/base.py" in get_response

  111. response = callback(request, *callback_args, **callback_kwargs)

File "/usr/local/lib/python2.6/dist-packages/django/contrib/auth/decorators.py" in _wrapped_view

  23. return view_func(request, *args, **kwargs)

File "/home/ascgov/application/views/pdf.py" in application_pdf

  90. 'user':owner})

File "/home/ascgov/application/views/pdf.py" in render_to_pdf

  53. htmldoc = subprocess.Popen("/usr/bin/htmldoc -t pdf --quiet '%s'" % result, stdout=subprocess.PIPE).communicate()

File "/usr/lib/python2.6/subprocess.py" in __init__

  633. errread, errwrite)

File "/usr/lib/python2.6/subprocess.py" in _execute_child

  1139. raise child_exception

Exception Type: OSError at /pdf/application/feed-filtr/
Exception Value: [Errno 2] No such file or directory

布莱格。多么可怕的要求,多么可怕的计划


似乎没有任何方法可以将要转换的内容作为命令行选项。不过,它似乎确实接受URL。因此,可以想象,您可以传递一个引用同一服务器上的视图的构造URL,并在第二个视图中输出呈现的模板,然后由从第一个视图运行的HTMLDOC拾取该模板。请注意,这在开发服务器上不起作用,因为它是单线程的,因此视图将永远相互等待。

Blergh。多么可怕的要求,多么可怕的计划


似乎没有任何方法可以将要转换的内容作为命令行选项。不过,它似乎确实接受URL。因此,可以想象,您可以传递一个引用同一服务器上的视图的构造URL,并在第二个视图中输出呈现的模板,然后由从第一个视图运行的HTMLDOC拾取该模板。请注意,这对开发服务器不起作用,因为它是单线程的,因此视图将永远等待彼此。

首先,
子流程。Popen
的第一个参数通常应该是一个列表(除非您还传递了
shell=True
)。
没有这样的文件或目录
几乎肯定是由于系统上没有名为
“htmldoc-t pdf--quiet”…
的文件造成的(它试图查找并运行以整个字符串值命名的程序)

第二,如果你在它的stdin上给htmldoc一些html,它会在stdout上吐出一个pdf,这样就不需要临时文件了

尝试一下(未经测试):

注意:将系统上htmldoc的实际路径替换为
/usr/bin/htmldoc

htmldoc程序的
-
参数告诉它从stdin读取。您将传递html字符串值(
html
)到htmldoc的
stdin
作为
htmldoc.communicate
调用的参数。生成的pdf输出应在
stdout
中提供,并在
stderr
中提供任何其他消息或统计信息

编辑:文档看起来确实有点不可靠,但有相当一部分不可靠。您可能会更幸运地使用或版本,或


另外,请确保向htmldoc进程的stdin传递字符串或类似内容。正如我前面的代码片段所暗示的那样,直接传递StringIO对象将不起作用。

首先,
子进程。Popen
的第一个参数通常应该是一个列表(除非同时传递
shell=True
)几乎可以肯定的是,系统上没有名为“htmldoc-t pdf--quiet”…的文件(它正试图查找并运行以整个字符串值命名的程序),这就导致了
没有这样的文件或目录

第二,如果你在它的stdin上给htmldoc一些html,它会在stdout上吐出一个pdf,这样就不需要临时文件了

尝试一下(未经测试):

注意:将系统上htmldoc的实际路径替换为
/usr/bin/htmldoc

htmldoc程序的
-
参数告诉它从stdin读取。您将把html字符串值(
html
)传递给htmldoc的
stdin
,作为
htmldoc.communicate
调用的参数。生成的pdf输出应在
stdout
中可用,而在
stderr
中则应提供任何其他消息或统计信息

编辑:文档看起来确实有点不可靠,但有相当多的内容。您可能会更幸运地使用或版本,或


另外,请确保向htmldoc进程的stdin传递字符串或类似内容。正如我前面的代码片段所暗示的那样,直接传递StringIO对象是行不通的

您是否尝试指定
htmldoc
可执行文件的绝对路径,例如
/usr/local/bin/htmldoc
?建议不错。尝试过之后,我仍然得到“[Errno 2]没有这样的文件或目录”。不过还是要感谢。
Errno 2
强烈建议,python端没有问题,但shell/callout端没有问题。(顺便说一句,如果你想在
子流程
周围有一个漂亮的包装器,请看一下)你是否尝试过指定
htmldoc
可执行文件的绝对路径,例如
/usr/local/bin/htmldoc
?建议不错。尝试过之后,我仍然得到“[Errno 2]没有这样的文件或目录”。不过还是要感谢。
Errno 2
强烈建议,python端没有问题,但shell/callout端没有问题。(顺便说一句,如果你想在
子流程
周围有一个漂亮的包装器,请看一下)Marty这是一个很好的解释。你的解决方案有效。我不理解波彭。谢天谢地,这是一个很好的解释。你的解决方案有效。我不理解波彭。谢谢
htmldoc = subprocess.Popen(
  ['/usr/bin/htmldoc', '-t', 'pdf', '--webpage', '-'], 
  stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
stdout, stderr = htmldoc.communicate(html)