2012-01-16 31 views
0

私は約1年前にクライアント用のDjangoアプリケーションを作成しました。彼は今、超秘密の政府機関に、彼らの名前を教えてくれないという申請書を転売しました。PythonとHTMLDOCを使用してhtmlからpdfへの変換

アプリケーションの一部は、pythonライブラリxhtml2pdf(pisa)を使用してPDFファイルを動的に生成します。政府はこのライブラリが気に入らず、理由を教えてくれません。私はHTMLDOCをpdf生成に使用しなければならないと言っていました。

このライブラリのドキュメントはあまりありませんが、PHPの例を読むと、シェルを介して通信できるように見えるので、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') 

アイデア、ヒント、またはヘルプは本当にありがたいです。

ありがとうございました。

UPDATE

スタックトレース:

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 
+0

実行ファイル 'htmldoc'の絶対パスを指定しようとしましたか? '/ usr/local/bin/htmldoc'? – miku

+0

良い提案。それを試して、私はまだ "[Errno 2]そのようなファイルやディレクトリ"を取得しています。しかし、ありがとう。 –

+1

'Errno 2'は、Python側では問題ないが、シェル/コールアウト側では問題ないことを強く示唆しています。 (btw、 'subprocess'の周りに素敵なラッパーが必要なら、[envoy](https://github.com/kennethreitz/envoy)を見てください) – miku

答えて

3

まず、subprocess.Popenの最初の引数は一般的にはリストでなければなりません(shell=Trueも渡さない限り)。 No such file or directoryは、システムに"htmldoc -t pdf --quiet '...という名前のファイルがないことが原因です(文字列全体の名前のプログラムを見つけて実行しようとしています)。

第2に、htmldocにstdinのhtmlを与えると、stdoutのpdfが吐き出され、一時ファイルが不要になります。

は、この与える試み(未テスト):

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

NB:お使いのシステム上のHTMLDOCへの実際のパスのため/usr/bin/htmldocに置き換えてください。

htmldocプログラムの引数である-は、stdinから読み取るように指示します。 htmldoc.communicate呼び出しのhtmldocのstdinにhtml文字列の値(html)を渡します。結果のPDF出力はstdoutで、その他のメッセージや統計情報はstderrにあるはずです。

を編集してください。ドキュメントは少しばかり見えますが、かなりあります。 html in one pageまたはpdfのバージョン、またはman pageのバージョンでは、運がよいかもしれません。

また、文字列などをhtmldocプロセスの標準入力に渡すようにしてください。前のコードスニペットで暗示されていたように、StringIOオブジェクトを直接渡すことはできません。

+0

これは素晴らしい説明です。あなたのソリューションは機能します。私はPopenを理解していませんでした。ありがとう –

0

Blergh。どのような恐ろしい要求、そしてひどいプログラム。

変換するコンテンツをコマンドラインオプションとして使用する方法はないようです。しかし、それはURLを受け入れているように見えます。したがって、同じサーバー上のビューを参照する構築済みのURLを渡すことができ、その2番目のビュー出力でレンダリングされたテンプレートが出力され、HTMLDOCによって最初のビューから実行されます。これはシングルスレッドなので、開発サーバーではうまくいかないことに注意してください。そうすれば、ビューは永遠にお互いを待つことになります。

関連する問題