Xhtml2pdf so với weasyprint

Trong một bài viết trước, tôi đã viết một hướng dẫn rất toàn diện về cách hiển thị tệp PDF trong Django bằng các công cụ như reportlab và xhtml2pdf. Mặc dù những công cụ này vẫn còn hiệu lực và hoạt động tốt nhưng tôi đã quyết định rằng việc thiết lập chúng hoạt động đúng cách thường là quá khó khăn

Một cách phổ biến khác để tạo tệp PDF trong thế giới Python là sử dụng thư viện weasyprint. Thật không may, thư viện này có quá nhiều yêu cầu và việc cài đặt nó trên Windows còn tệ hơn cả việc chọc kim vào mắt bạn. Tôi không thích kim tiêm vào mắt mình, cảm ơn bạn rất nhiều

Có nhiều cách khác để tạo tệp PDF như sử dụng máy chủ báo cáo như Japser hoặc SQL Dịch vụ báo cáo của máy chủ nhưng những cách này quá “doanh nghiệp- .

Tôi thực sự rất thất vọng trước tình trạng của thế hệ PDF ngày nay mà trong một số dự án gần đây thay vì PDF file I generated an HTML page with a nice pdf-printing stylesheet and instructed the users to print it as PDF (from the browser) so as to generate the PDF themselves!

Tuy nhiên, gần đây tôi đã tìm ra một cách khác để tạo tệp PDF trong các dự án Django của mình mà tôi muốn chia sẻ với bạn. Sử dụng công cụ wkhtmltopdf. wkhtmltopdf là một chương trình dòng lệnh có các tệp nhị phân cho ít nhiều mọi hệ điều hành. Đó là một tệp nhị phân duy nhất mà bạn có thể tải xuống và đặt nó vào một thư mục, bạn không cần phải chạy một máy chủ khác hoặc bất kỳ cài đặt ưa thích nào. Chỉ một tệp thực thi. Để dùng nó? . //Google. com google. pdf và nó sẽ tải xuống url và tạo pdf. Nó đơn giản như vậy. Công cụ này đã cũ và được sử dụng nhiều nhưng chỉ gần đây tôi mới nghiên cứu khả năng tích hợp của nó với Django

Xin lưu ý rằng thực sự có một thư viện django-wkhtmltopdf để tích hợp wkhtmltopdf với django. Tuy nhiên, tôi không có kết quả tốt khi cố gắng sử dụng nó (có thể do môi trường nhà phát triển Windows của tôi). Ngoài ra, việc tự triển khai tích hợp cho phép tôi hiểu dễ dàng hơn những gì đang xảy ra và gỡ lỗi wkhtmltopdf tốt hơn. Tuy nhiên YMMV , sau khi bạn đọc bài đăng nhỏ này để hiểu cách tích hợp hoạt động, bạn có thể dùng thử django-wkhtmltopdf để xem nó có hoạt động trong trường hợp của bạn không.

Dù bằng cách nào, điều đầu tiên bạn cần làm là tải xuống và cài đặt wkhtmltopdf cho nền tảng của mình và lưu đường dẫn đầy đủ của nó trong cài đặt của bạn. py thích điều này

# For linux
WKHTMLTOPDF_CMD = '/usr/local/bin/wkhtmltopdf'

# or for windows
WKHTMLTOPDF_CMD = r'c:\util\wkhtmltopdf.exe'

Lưu ý rằng tôi đang sử dụng đường dẫn đầy đủ. Tôi đã quan sát thấy rằng ngay cả khi bạn đặt tệp nhị phân vào một thư mục trong hệ thống PATH nó sẽ không được chọn (ít nhất là trong trường hợp của tôi), vì vậy tôi .

Bây giờ, giả sử chúng ta có một Chế độ xem chi tiết (hãy gọi nó là Chế độ xem mẫu) mà chúng tôi muốn hiển thị dưới dạng PDF . Chúng tôi có thể sử dụng CBV sau cho điều đó.

from subprocess import check_output
from django.template import Context, Template
from django.template.loader import get_template
from tempfile import NamedTemporaryFile
import os

class SamplePdfDetailView(SampleDetailView):
  def get_resp_from_file(self, filename, context):
      template = get_template(filename)
      resp = template.render(context)
      return resp

  def get_resp_from_string(self, template_str, context):
      template = Template(template_str)
      resp = template.render(Context(context))
      return resp

  def render_to_response(self, context):
      context['pdf'] = True
      # We can use either
      resp = self.get_resp_from_string("

Hello, world! {{ object }}

"
, context) # or # resp = self.get_resp_from_file('test_pdf.html', context) tempfile = NamedTemporaryFile(mode='w+b', buffering=-1, suffix='.html', prefix='tmp', dir=None, delete=False) tempfile.write(resp.encode('utf-8')) tempfile.flush() tempfile.close() cmd = [ settings.WKHTMLTOPDF_CMD, '--page-size', 'A4', '--encoding', 'utf-8', '--footer-center', '[page] / [topage]', '--enable-local-file-access', tempfile.name, '-'] # print(" ".join(cmd)) out = check_output(cmd) os.remove(tempfile.name) return HttpResponse(out, content_type='application/pdf')

Chúng tôi có thể đặt chế độ xem pdf trên các mẫu url của mình ngay bên cạnh Chế độ xem chi tiết i. e

[
  ...
  path(
      "detail//",
      permission_required("core.user")(
          views.SampleDetailView.as_view()
      ),
      name="detail",
  ),
  path(
      "detail//pdf/",
      permission_required("core.user")(
          views.SamplePdfDetailView.as_view()
      ),
      name="detail_pdf",
  ),
  ...
]

Hãy thử tìm hiểu cách thức hoạt động của tính năng này. Trước hết, hãy lưu ý rằng chúng tôi có hai tùy chọn, tạo PDF từ chuỗi html hoặc từ tệp mẫu thông thường. Đối với tùy chọn đầu tiên, chúng tôi chuyển chuỗi html đầy đủ tới get_resp_from_string và ngữ cảnh và chúng tôi sẽ nhận được html được hiển thị (i. e ngữ cảnh sẽ được áp dụng cho mẫu). Đối với tùy chọn thứ hai, chúng tôi chuyển tên tệp của mẫu django và ngữ cảnh. Lưu ý rằng có một sự khác biệt nhỏ về cách mẫu. phương thức render() được gọi trong hai phương thức.

Sau đó, chúng ta có một tệp html được lưu trong chuỗi resp. Chúng tôi muốn cung cấp cái này cho wkhtmltopdf để được chuyển đổi thành PDF . Để làm điều đó, trước tiên chúng ta tạo một tệp tạm thời bằng cách sử dụng lớp NamedTemporaryFile và ghi tệp tương ứng vào tệp đó. Sau đó, chúng tôi gọi wkhtmltopdf chuyển cho nó tệp tạm thời này. Lưu ý rằng chúng tôi sử dụng quy trình con. hàm check_output sẽ ghi lại đầu ra của lệnh và trả về nó.

Cuối cùng, chúng tôi xóa tệp tạm thời và trả lại pdf dưới dạng HttpResponse

Chúng tôi gọi wkhtmltopdf như thế này

c:\util\wkhtmltopdf.exe --page-size A4 --encoding utf-8 --footer-center [page] / [topage] --enable-local-file-access C:\Users\serafeim\AppData\Local\Temp\tmp_lh5r6f9.html -

Kích thước trang có thể thay đổi thành chữ cái nếu bạn ở Hoa Kỳ . Mã hóa phải là utf-8. Tùy chọn —footer-center thêm chân trang vào trang PDF với trang hiện tại và tổng số trang. —enable-local-file-access rất quan trọng vì nó cho phép wkhtmltopdf truy cập các tệp cục bộ (trong hệ thống tệp) chứ không chỉ các tệp từ xa. Sau đó, chúng tôi đã có đường dẫn đầy đủ của tệp tạm thời của mình và sau đây là - có nghĩa là đầu ra pdf sẽ nằm trên thiết bị xuất chuẩn (vì vậy chúng tôi sẽ chụp nó bằng check_output).

Lưu ý rằng có một lệnh in đã nhận xét trước lệnh gọi check_output. Nếu bạn gặp sự cố, bạn có thể gọi lệnh này từ dòng lệnh của mình để gỡ lỗi lệnh wkhtmltopdf (đừng quên nhận xét os. xóa dòng để giữ tệp tạm thời). Ngoài ra, wkhtmltopdf sẽ xuất một số nội dung trong khi hiển thị lệnh, chẳng hạn như nội dung gì đó như

Loading pages (1/6)
Counting pages (2/6)
Resolving links (4/6)
Loading headers and footers (5/6)
Printing pages (6/6)
Done

Bạn có thể chuyển tùy chọn --quiet để ẩn đầu ra này. Tuy nhiên, đầu ra rất hữu ích để xem wkhtmltopdf đang làm gì trong trường hợp có sự cố, vì vậy tôi khuyên bạn nên để nó trong khi phát triển. Hãy xem xét một đầu ra có vấn đề.

Loading pages (1/6)
Error: Failed to load file:///static/bootstrap/css/bootstrap.min.css, with network status code 203 and http status code 0 - Error opening /static_edla/bootstrap/css/govgr_bootstrap.min.css: The system cannot find the path specified.
[...]
Counting pages (2/6)
Resolving links (4/6)
Loading headers and footers (5/6)
Printing pages (6/6)
Done

Đầu ra ở trên có nghĩa là mẫu của chúng tôi cố tải tệp css mà wkhtmltopdf không thể tìm thấy và bị lỗi. Để hiểu lỗi này, tôi đã có một dòng như thế này trong mẫu của mình


sẽ được chuyển đổi thành một liên kết như `/static/bootstrap/css/bootstrap. tối thiểu. css. Tuy nhiên, lưu ý rằng tôi bảo wkhtmltopdf hiển thị tệp từ thư mục tạm thời của tôi, nó không biết liên kết đó trỏ đến đâu. Theo điều này, bạn cần hết sức cẩn thận để bao gồm mọi thứ trong mẫu HTML -pdf của mình và không sử dụng bất kỳ liên kết bên ngoài nào. Vì vậy, tất cả các kiểu phải được đặt trong mẫu bằng thẻ

, 'rb') as image:
  print(base64.b64encode(image.read()).decode('utf-8'))

Thay vì Chế độ xem chi tiết, chúng tôi có thể sử dụng phương pháp tương tự cho bất kỳ loại CBV nào . Nếu bạn định sử dụng phản hồi PDF cho nhiều CBV, tôi khuyên bạn nên xuất chức năng này sang mixin và cũng kế thừa từ đó (xem CBV guide for more).

Cuối cùng, câu hỏi lớn trong phòng là tại sao tôi nên chuyển đổi mẫu của mình thành tệp và chuyển tệp đó sang wkhtmltopdf, tôi không thể sử dụng URL of my template, i.e pass wkhtmltopdf something like http://example.com/app/detail/321/?

Bằng mọi cách bạn có thể. Điều này cũng sẽ cho phép bạn tránh sử dụng các kiểu nội tuyến và phương tiện. Tuy nhiên, hãy nhớ rằng trường hợp thông thường là chế độ xem này sẽ không công khai nhưng sẽ cần người dùng được xác thực để truy cập vào nó; . Tất nhiên, bạn có thể bắt đầu một cuộc phiêu lưu xác thực nó bằng cách nào đó (và có thể làm điều gì đó ngu ngốc) hoặc bạn có thể làm theo hướng dẫn của tôi và kết xuất nó thành một tệp. )