Last active
June 24, 2024 13:38
-
-
Save firdavsDev/873c71a44546c8630060f232ece3f367 to your computer and use it in GitHub Desktop.
Generate PDF + QrCode .pdf file in Django projects. (hard codeing)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from pdf_generator import PDFGenerator | |
instance = Model.objacts.get(id=1) #example | |
context = { | |
"data": instance | |
} #use as {{ data }} in html_file_path | |
#Create object | |
pdf = PDFGenerator( | |
html_file_path="pdf.html", | |
css_file_path="css/bootstrap.min.css", | |
img_file_paths=[ | |
"assets/img/logo.png", # use as {{ logo }} in html_file_path. {{ file_name}} | |
"assets/img/gerb.png" | |
# ..... | |
], | |
context=context, | |
options={ | |
"page-size": "A4", | |
"orientation": "portrait", | |
}, | |
) | |
#Generate pdf file | |
pdf_file_path = pdf.generate_pdf( | |
filename=f"pdf_file_name", | |
output_path="output_folder", | |
unique_code_len=6, #randomly prefix for file name | |
) | |
# You can also save to FileField | |
# instance.pdf_file = pdf_file_path | |
# instance.save() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import base64 | |
import os | |
import platform | |
import subprocess | |
from asyncio.log import logger | |
from io import BytesIO | |
from typing import List | |
from uuid import uuid4 | |
import pdfkit # pip install pdfkit | |
import qrcode # pip install qrcode | |
from django.conf import settings | |
from django.template.loader import get_template | |
from PyPDF2 import PdfMerger, PdfReader #pip install PyPDF2 | |
class PDFGenerator: | |
""" | |
HTML (string) to PDF file Generator class in Django | |
* sudo apt-get install -y wkhtmltopdf | |
Note: | |
- You can use {{ qr_code_img }} in html file. | |
Args: | |
* html_file_path (str): html file path in templates folder | |
* css_file_path (str): css file path in STATIC_ROOT folder | default None | |
* img_file_paths (List(str)): img file path in STATIC_ROOT folder | default None | in template html file you can use it as {{file_name}} | |
* context (dict): context data | default empty dict | QuerySet | | |
""" | |
def __init__( | |
self, | |
html_file_path: str, | |
css_file_path: str = None, | |
img_file_paths: List[str] = None, | |
context: dict = None, | |
options: dict = None, | |
): | |
if context is None: | |
context = {} | |
if options is None: | |
options = {"page-size": "A4", "encoding": "UTF-8", "quiet": True} | |
self.html_path = html_file_path | |
self.css_path = css_file_path | |
self.img_paths = img_file_paths | |
self.data = context | |
self._options = options | |
def _get_pdfkit_config(self): | |
""" | |
wkhtmltopdf lives and functions differently depending on Windows or Linux. We | |
need to support both since we develop on windows or Linux but deploy on Server. | |
Returns: | |
A pdfkit configuration | |
""" | |
if platform.system() == "Windows": | |
return pdfkit.configuration( | |
wkhtmltopdf=os.environ.get("WKHTMLTOPDF_BINARY", "C:\\Program Files\\wkhtmltopdf\\bin\\wkhtmltopdf.exe") | |
) | |
WKHTMLTOPDF_CMD = ( | |
subprocess.Popen(["which", os.environ.get("WKHTMLTOPDF_BINARY", "wkhtmltopdf")], stdout=subprocess.PIPE) | |
.communicate()[0] | |
.strip() | |
) | |
return pdfkit.configuration(wkhtmltopdf=WKHTMLTOPDF_CMD) | |
def output_file_path_generator(self, filename: str, unique_code: int, output_path: str) -> str: | |
"""Generate path for pdf file save | |
Args: | |
* filename (str): file_name | |
* unique_code (int): uuid length | |
* output_path (str): save folder path | |
Returns: | |
output_for_pdfkit (str): path for pdfkit save pdf file | |
output_for_model (str): path for save Model field | |
""" | |
uuid_code = str(uuid4().hex)[:unique_code] | |
if output_path is not None: | |
# cheking output_path folder exists or not | |
if not os.path.exists(f"{settings.MEDIA_ROOT}{output_path}"): | |
os.makedirs(f"{settings.MEDIA_ROOT}{output_path}") | |
output_for_pdfkit = f"{settings.MEDIA_ROOT}/{output_path}/{filename}_{uuid_code}.pdf" | |
else: | |
output_for_pdfkit = f"{settings.MEDIA_ROOT}/{filename}_{uuid_code}.pdf" | |
output_for_model = f"{output_path}/{filename}_{uuid_code}.pdf" | |
return output_for_pdfkit, output_for_model | |
def convert_image_file_to_base64_data(self, file_path: str) -> str: | |
"""Get image file as base64 data | |
Args: | |
file_path (str): image file path | |
Returns: | |
str: base64 data | |
""" | |
with open(file_path, "rb") as image_file: | |
return base64.b64encode(image_file.read()).decode("utf-8") | |
def generate_qr_code_as_base64_data(self, pdf_file_url: str) -> str: | |
"""Generate pdf url as qr code | |
Args: | |
pdf_file_url (str): pdf_file_path in media url | |
Returns: | |
str: base64 data | |
""" | |
pdf_file_url = "https://" + os.environ["SERVER_DOMAIN"] + "/media/" + pdf_file_url | |
qr_code_IO = BytesIO() | |
qr_code_img = qrcode.make(pdf_file_url) | |
qr_code_img.save(qr_code_IO, "PNG") | |
return base64.b64encode(qr_code_IO.getvalue()).decode("utf-8") | |
def generate_pdf(self, filename: str, output_path: str = None, unique_code_len: int = 4) -> str: | |
"""Generate pdf file & save | |
Args: | |
* filename (str): pdf file name. Saved as <filename>_<uuid>.pdf | |
* unique_code_len: uuid4 code length. Default 4 | |
* output_path (str): save pdf file path folder in settings.MEDIA_ROOT. Default save in settings.MEDIA_ROOT/<filename>_<uuid>.pdf | |
Raises: | |
e: Description of Error & Logging | |
Returns: | |
str: file saved path for save in model | |
""" | |
try: | |
if self.css_path is not None: | |
self.css_path = f"{settings.STATIC_ROOT}/{self.css_path}" | |
if self.img_paths is not None and len(self.img_paths) > 0: | |
for img_path in self.img_paths: | |
img_file_path = f"{settings.STATIC_ROOT}/{img_path}" | |
img_file_name = img_path[img_path.rfind("/") :].split(".")[0].replace("/", "") | |
self.data[img_file_name] = self.convert_image_file_to_base64_data(img_file_path) | |
# calling output_file_path_generator | |
output_for_pdfkit, output_for_model = self.output_file_path_generator( | |
filename=filename, unique_code=unique_code_len, output_path=output_path | |
) | |
# set qr code img | |
self.data["qr_code_img"] = self.generate_qr_code_as_base64_data(output_for_model) | |
# render data to html | |
template = get_template(self.html_path) | |
html = template.render(self.data) | |
# calling pdfkit package's from_string function | |
if pdfkit.from_string( | |
input=html, | |
output_path=output_for_pdfkit, | |
# configuration=self._get_pdfkit_config(), | |
options=self._options, | |
css=self.css_path, | |
): | |
return output_for_model | |
return None | |
except Exception as e: | |
logger.error(e) | |
raise e | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pdfkit==1.0.0 #pip install pdfkit | |
Pillow==9.2.0 #pip install Pillow | |
PyPDF2 | |
qrcode |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment