Merge pull request #31069 from maharshivpatel/add_pdf_backend_hook
feat: added hook to use chrome pdf generator
This commit is contained in:
commit
e34a820256
10 changed files with 152 additions and 60 deletions
|
|
@ -11,7 +11,6 @@ be used to build database driven apps.
|
|||
Read the documentation: https://frappeframework.com/docs
|
||||
"""
|
||||
|
||||
import copy
|
||||
import functools
|
||||
import importlib
|
||||
import inspect
|
||||
|
|
@ -1673,14 +1672,22 @@ def get_file_json(path):
|
|||
return json.load(f)
|
||||
|
||||
|
||||
def read_file(path, raise_not_found=False):
|
||||
"""Open a file and return its content as Unicode."""
|
||||
def read_file(path, raise_not_found=False, as_base64=False):
|
||||
"""Open a file and return its content as Unicode or Base64 string."""
|
||||
if isinstance(path, str):
|
||||
path = path.encode("utf-8")
|
||||
|
||||
if os.path.exists(path):
|
||||
with open(path) as f:
|
||||
return as_unicode(f.read())
|
||||
if as_base64:
|
||||
import base64
|
||||
|
||||
with open(path, "rb") as f:
|
||||
content = f.read()
|
||||
return base64.b64encode(content).decode("utf-8")
|
||||
else:
|
||||
with open(path) as f:
|
||||
content = f.read()
|
||||
return as_unicode(content)
|
||||
elif raise_not_found:
|
||||
raise OSError(f"{path} Not Found")
|
||||
else:
|
||||
|
|
@ -2076,52 +2083,6 @@ def format(*args, **kwargs):
|
|||
return frappe.utils.formatters.format_value(*args, **kwargs)
|
||||
|
||||
|
||||
def get_print(
|
||||
doctype=None,
|
||||
name=None,
|
||||
print_format=None,
|
||||
style=None,
|
||||
as_pdf=False,
|
||||
doc=None,
|
||||
output=None,
|
||||
no_letterhead=0,
|
||||
password=None,
|
||||
pdf_options=None,
|
||||
letterhead=None,
|
||||
):
|
||||
"""Get Print Format for given document.
|
||||
|
||||
:param doctype: DocType of document.
|
||||
:param name: Name of document.
|
||||
:param print_format: Print Format name. Default 'Standard',
|
||||
:param style: Print Format style.
|
||||
:param as_pdf: Return as PDF. Default False.
|
||||
:param password: Password to encrypt the pdf with. Default None"""
|
||||
from frappe.utils.pdf import get_pdf
|
||||
from frappe.website.serve import get_response_without_exception_handling
|
||||
|
||||
original_form_dict = copy.deepcopy(local.form_dict)
|
||||
try:
|
||||
local.form_dict.doctype = doctype
|
||||
local.form_dict.name = name
|
||||
local.form_dict.format = print_format
|
||||
local.form_dict.style = style
|
||||
local.form_dict.doc = doc
|
||||
local.form_dict.no_letterhead = no_letterhead
|
||||
local.form_dict.letterhead = letterhead
|
||||
|
||||
pdf_options = pdf_options or {}
|
||||
if password:
|
||||
pdf_options["password"] = password
|
||||
|
||||
response = get_response_without_exception_handling("printview", 200)
|
||||
html = str(response.data, "utf-8")
|
||||
finally:
|
||||
local.form_dict = original_form_dict
|
||||
|
||||
return get_pdf(html, options=pdf_options, output=output) if as_pdf else html
|
||||
|
||||
|
||||
def attach_print(
|
||||
doctype,
|
||||
name,
|
||||
|
|
@ -2376,5 +2337,6 @@ from frappe.config import get_common_site_config, get_site_config
|
|||
from frappe.core.doctype.system_settings.system_settings import get_system_settings
|
||||
from frappe.utils import parse_json
|
||||
from frappe.utils.error import log_error
|
||||
from frappe.utils.print_utils import get_print
|
||||
|
||||
frappe._optimizations.optimize_all()
|
||||
|
|
|
|||
|
|
@ -242,3 +242,4 @@ execute:frappe.db.set_single_value("Workspace Settings", "workspace_setup_comple
|
|||
frappe.patches.v16_0.add_app_launcher_in_navbar_settings
|
||||
frappe.desk.doctype.workspace.patches.update_app
|
||||
frappe.patches.v16_0.move_role_desk_settings_to_user
|
||||
frappe.printing.doctype.print_format.patches.sets_wkhtmltopdf_as_default_for_pdf_generator_field
|
||||
|
|
|
|||
0
frappe/printing/doctype/print_format/patches/__init__.py
Normal file
0
frappe/printing/doctype/print_format/patches/__init__.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
"""sets "wkhtmltopdf" as default for pdf_generator field"""
|
||||
for pf in frappe.get_all("Print Format", pluck="name"):
|
||||
frappe.db.set_value("Print Format", pf, "pdf_generator", "wkhtmltopdf", update_modified=False)
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
"standard",
|
||||
"custom_format",
|
||||
"disabled",
|
||||
"pdf_generator",
|
||||
"section_break_6",
|
||||
"print_format_type",
|
||||
"raw_printing",
|
||||
|
|
@ -255,12 +256,19 @@
|
|||
"fieldtype": "Select",
|
||||
"label": "Page Number",
|
||||
"options": "Hide\nTop Left\nTop Center\nTop Right\nBottom Left\nBottom Center\nBottom Right"
|
||||
},
|
||||
{
|
||||
"default": "wkhtmltopdf",
|
||||
"fieldname": "pdf_generator",
|
||||
"fieldtype": "Select",
|
||||
"label": "PDF Generator",
|
||||
"options": "wkhtmltopdf"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-print",
|
||||
"idx": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-23 16:03:34.964767",
|
||||
"modified": "2025-02-14 14:49:39.181074",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Printing",
|
||||
"name": "Print Format",
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ class PrintFormat(Document):
|
|||
page_number: DF.Literal[
|
||||
"Hide", "Top Left", "Top Center", "Top Right", "Bottom Left", "Bottom Center", "Bottom Right"
|
||||
]
|
||||
pdf_generator: DF.Literal["wkhtmltopdf"]
|
||||
print_format_builder: DF.Check
|
||||
print_format_builder_beta: DF.Check
|
||||
print_format_type: DF.Literal["Jinja", "JS"]
|
||||
|
|
|
|||
|
|
@ -33,9 +33,11 @@ PDF_CONTENT_ERRORS = [
|
|||
]
|
||||
|
||||
|
||||
def pdf_header_html(soup, head, content, styles, html_id, css):
|
||||
def pdf_header_html(soup, head, content, styles, html_id, css, path=None):
|
||||
if not path:
|
||||
path = "templates/print_formats/pdf_header_footer.html"
|
||||
return frappe.render_template(
|
||||
"templates/print_formats/pdf_header_footer.html",
|
||||
path,
|
||||
{
|
||||
"head": head,
|
||||
"content": content,
|
||||
|
|
@ -75,8 +77,10 @@ def _guess_template_error_line_number(template) -> int | None:
|
|||
return frame.lineno
|
||||
|
||||
|
||||
def pdf_footer_html(soup, head, content, styles, html_id, css):
|
||||
return pdf_header_html(soup=soup, head=head, content=content, styles=styles, html_id=html_id, css=css)
|
||||
def pdf_footer_html(soup, head, content, styles, html_id, css, path=None):
|
||||
return pdf_header_html(
|
||||
soup=soup, head=head, content=content, styles=styles, html_id=html_id, css=css, path=path
|
||||
)
|
||||
|
||||
|
||||
def get_pdf(html, options=None, output: PdfWriter | None = None):
|
||||
|
|
@ -316,7 +320,8 @@ def prepare_header_footer(soup: BeautifulSoup):
|
|||
toggle_visible_pdf(content)
|
||||
id_map = {"header-html": "pdf_header_html", "footer-html": "pdf_footer_html"}
|
||||
hook_func = frappe.get_hooks(id_map.get(html_id))
|
||||
html = frappe.get_attr(hook_func[-1])(
|
||||
html = frappe.call(
|
||||
hook_func[-1],
|
||||
soup=soup,
|
||||
head=head,
|
||||
content=content,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import json
|
|||
import os
|
||||
import uuid
|
||||
from io import BytesIO
|
||||
from typing import Literal
|
||||
|
||||
from pypdf import PdfWriter
|
||||
|
||||
|
|
@ -218,14 +219,28 @@ from frappe.deprecation_dumpster import read_multi_pdf
|
|||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def download_pdf(
|
||||
doctype: str, name: str, format=None, doc=None, no_letterhead=0, language=None, letterhead=None
|
||||
doctype: str,
|
||||
name: str,
|
||||
format=None,
|
||||
doc=None,
|
||||
no_letterhead=0,
|
||||
language=None,
|
||||
letterhead=None,
|
||||
pdf_generator: Literal["wkhtmltopdf", "chrome"] | None = None,
|
||||
):
|
||||
doc = doc or frappe.get_doc(doctype, name)
|
||||
validate_print_permission(doc)
|
||||
|
||||
with print_language(language):
|
||||
pdf_file = frappe.get_print(
|
||||
doctype, name, format, doc=doc, as_pdf=True, letterhead=letterhead, no_letterhead=no_letterhead
|
||||
doctype,
|
||||
name,
|
||||
format,
|
||||
doc=doc,
|
||||
as_pdf=True,
|
||||
letterhead=letterhead,
|
||||
no_letterhead=no_letterhead,
|
||||
pdf_generator=pdf_generator,
|
||||
)
|
||||
|
||||
frappe.local.response.filename = "{name}.pdf".format(name=name.replace(" ", "-").replace("/", "-"))
|
||||
|
|
|
|||
89
frappe/utils/print_utils.py
Normal file
89
frappe/utils/print_utils.py
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
from typing import Literal
|
||||
|
||||
import frappe
|
||||
|
||||
|
||||
def get_print(
|
||||
doctype=None,
|
||||
name=None,
|
||||
print_format=None,
|
||||
style=None,
|
||||
as_pdf=False,
|
||||
doc=None,
|
||||
output=None,
|
||||
no_letterhead=0,
|
||||
password=None,
|
||||
pdf_options=None,
|
||||
letterhead=None,
|
||||
pdf_generator: Literal["wkhtmltopdf", "chrome"] | None = None,
|
||||
):
|
||||
"""Get Print Format for given document.
|
||||
:param doctype: DocType of document.
|
||||
:param name: Name of document.
|
||||
:param print_format: Print Format name. Default 'Standard',
|
||||
:param style: Print Format style.
|
||||
:param as_pdf: Return as PDF. Default False.
|
||||
:param password: Password to encrypt the pdf with. Default None
|
||||
:param pdf_generator: PDF generator to use. Default 'wkhtmltopdf'
|
||||
"""
|
||||
|
||||
"""
|
||||
local.form_dict.pdf_generator is set from before_request hook (print designer app) for download_pdf endpoint
|
||||
if it is not set (internal function call) then set it
|
||||
"""
|
||||
import copy
|
||||
|
||||
from frappe.utils.pdf import get_pdf
|
||||
from frappe.website.serve import get_response_without_exception_handling
|
||||
|
||||
local = frappe.local
|
||||
if "pdf_generator" not in local.form_dict:
|
||||
# if arg is passed, use that, else get setting from print format
|
||||
if pdf_generator is None:
|
||||
pdf_generator = (
|
||||
frappe.get_cached_value("Print Format", print_format, "pdf_generator") or "wkhtmltopdf"
|
||||
)
|
||||
local.form_dict.pdf_generator = pdf_generator
|
||||
|
||||
original_form_dict = copy.deepcopy(local.form_dict)
|
||||
try:
|
||||
local.form_dict.doctype = doctype
|
||||
local.form_dict.name = name
|
||||
local.form_dict.format = print_format
|
||||
local.form_dict.style = style
|
||||
local.form_dict.doc = doc
|
||||
local.form_dict.no_letterhead = no_letterhead
|
||||
local.form_dict.letterhead = letterhead
|
||||
|
||||
pdf_options = pdf_options or {}
|
||||
if password:
|
||||
pdf_options["password"] = password
|
||||
|
||||
response = get_response_without_exception_handling("printview", 200)
|
||||
html = str(response.data, "utf-8")
|
||||
finally:
|
||||
local.form_dict = original_form_dict
|
||||
|
||||
if not as_pdf:
|
||||
return html
|
||||
|
||||
if local.form_dict.pdf_generator != "wkhtmltopdf":
|
||||
hook_func = frappe.get_hooks("pdf_generator")
|
||||
for hook in hook_func:
|
||||
"""
|
||||
check pdf_generator value in your hook function.
|
||||
if it matches run and return pdf else return None
|
||||
"""
|
||||
pdf = frappe.call(
|
||||
hook,
|
||||
print_format=print_format,
|
||||
html=html,
|
||||
options=pdf_options,
|
||||
output=output,
|
||||
pdf_generator=local.form_dict.pdf_generator,
|
||||
)
|
||||
# if hook returns a value, assume it was the correct pdf_generator and return it
|
||||
if pdf:
|
||||
return pdf
|
||||
|
||||
return get_pdf(html, options=pdf_options, output=output)
|
||||
|
|
@ -72,6 +72,9 @@ def get_context(context) -> PrintContext:
|
|||
|
||||
print_format = get_print_format_doc(None, meta=meta)
|
||||
|
||||
make_access_log(
|
||||
doctype=frappe.form_dict.doctype, document=frappe.form_dict.name, file_type="PDF", method="Print"
|
||||
)
|
||||
body = get_rendered_template(
|
||||
doc,
|
||||
print_format=print_format,
|
||||
|
|
@ -99,6 +102,7 @@ def get_context(context) -> PrintContext:
|
|||
"print_format": getattr(print_format, "name", None),
|
||||
"letterhead": letterhead,
|
||||
"no_letterhead": frappe.form_dict.no_letterhead,
|
||||
"pdf_generator": frappe.form_dict.get("pdf_generator", "wkhtmltopdf"),
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -173,7 +177,7 @@ def get_rendered_template(
|
|||
|
||||
template = None
|
||||
if hook_func := frappe.get_hooks("get_print_format_template"):
|
||||
template = frappe.get_attr(hook_func[-1])(jenv=jenv, print_format=print_format)
|
||||
template = frappe.call(hook_func[-1], jenv=jenv, print_format=print_format)
|
||||
|
||||
if template:
|
||||
pass
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue