0

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

部分应用程序使用 python 库 xhtml2pdf (pisa) 动态生成 PDF 文件。政府不喜欢这个库,他们不会告诉我为什么,他们说我必须使用HTMLDOC来生成 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
4

2 回答 2

3

首先,subprocess.Popen的第一个参数通常应该是一个列表(除非你也通过了shell=True)。这No such file or directory几乎可以肯定是由于系统上缺少一个命名的文件"htmldoc -t pdf --quiet '...(它试图找到并运行以整个字符串值命名的程序)。

其次,如果你在它的标准输入上给 htmldoc 一些 html,它会在它的标准输出上输出一个 pdf,从而避免需要一个临时文件。

试试这个(未经测试):

htmldoc = subprocess.Popen(
  ['/usr/bin/htmldoc', '-t', 'pdf', '--webpage', '-'], 
  stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
stdout, stderr = htmldoc.communicate(html)

注意:替换/usr/bin/htmldoc系统上 htmldoc 的真实路径。

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

编辑:文档看起来确实有点不靠谱,但是有很多。使用一页pdf版本中的 html 或手册页可能会更好。

此外,请务必将字符串或类似内容传递给 htmldoc 进程的标准输入。正如我之前的代码片段所暗示的那样,直接传递一个 StringIO 对象是行不通的。

于 2012-01-16T22:19:24.243 回答
0

布莱尔。多么可怕的要求,多么可怕的程序。

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

于 2012-01-16T22:07:49.183 回答