Files
Fulfillment-Backend/barcodes/pdf/generator.py

192 lines
7.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
from io import BytesIO
from typing import List
from reportlab.graphics.barcode import code128
from reportlab.lib.pagesizes import mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfgen import canvas
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak
from barcodes.pdf.pdf_maker import PdfMaker
from barcodes.types import PdfBarcodeImageGenData, PdfBarcodeGenData
from constants import APP_PATH
class PDFGenerator:
def __init__(self):
ASSETS_FOLDER = os.path.join(APP_PATH, 'assets')
FONTS_FOLDER = os.path.join(ASSETS_FOLDER, 'fonts')
FONT_FILE_PATH = os.path.join(FONTS_FOLDER, 'DejaVuSans.ttf')
self.page_width = 58 * mm
self.page_height = 40 * mm
self.number_of_spacing_pages = 2
pdfmetrics.registerFont(TTFont('DejaVuSans', FONT_FILE_PATH))
# Get standard styles and create a new style with a smaller font size
styles = getSampleStyleSheet()
self.small_style = ParagraphStyle(
'Small',
parent=styles['Normal'],
fontName='DejaVuSans', # Specify the new font
fontSize=6,
leading=7,
spaceAfter=2,
leftIndent=2,
rightIndent=2,
)
def generate_small_text(self, barcode_value, text, num_duplicates=1):
buffer = BytesIO()
# Create document with specified page size
doc = SimpleDocTemplate(
buffer,
pagesize=(self.page_width, self.page_height),
rightMargin=1 * mm,
leftMargin=1 * mm,
topMargin=1 * mm,
bottomMargin=1 * mm
)
# Create paragraph with new style
paragraph = Paragraph(text, self.small_style)
# Get the width and height of the paragraph
paragraph_width, paragraph_height = paragraph.wrap(self.page_width - 2 * mm, self.page_height)
# Create barcode
barcode_height = (self.page_height - paragraph_height * mm)
available_width = self.page_width - 2 * mm # Adjust for margins
# Approximate number of elements in a Code 128 barcode for average length
num_elements = 11 * len(barcode_value) # Rough estimate: 11 elements per character
# Calculate barWidth
bar_width = available_width / num_elements
barcode = code128.Code128(
barcode_value,
barWidth=bar_width,
barHeight=barcode_height,
humanReadable=True
)
# Function to draw barcode on canvas
def add_barcode(canvas, doc):
barcode_width = barcode.width
barcode_x = (self.page_width - barcode_width) / 2 # Center the barcode
barcode.drawOn(canvas, barcode_x, self.page_height - barcode.height - 5)
# Create list of elements to add to the document
elements = []
for _ in range(num_duplicates):
elements.append(Spacer(1, barcode_height + 2 * mm))
elements.append(paragraph)
elements.append(PageBreak())
# Build document
doc.build(elements[:-1], onFirstPage=add_barcode, onLaterPages=add_barcode) # Remove the last PageBreak
buffer.seek(0)
return buffer
# Создание документа с указанным размером страницы
def _create_doc(self, buffer):
return SimpleDocTemplate(
buffer,
pagesize=(self.page_width, self.page_height),
rightMargin=1 * mm,
leftMargin=1 * mm,
topMargin=1 * mm,
bottomMargin=1 * mm
)
def _generate_for_one_product(self, barcode_data: PdfBarcodeGenData) -> BytesIO:
buffer = BytesIO()
doc = self._create_doc(buffer)
# Создаем абзац с новым стилем
paragraph = Paragraph(barcode_data['text'], self.small_style)
# Получаем ширину и высоту абзаца
paragraph_width, paragraph_height = paragraph.wrap(self.page_width - 2 * mm, self.page_height)
# Рассчитываем доступное пространство для штрихкода
human_readable_height = 6 * mm # Высота human-readable текста
space_between_text_and_barcode = 4 * mm # Отступ между текстом и штрихкодом
barcode_height = self.page_height - paragraph_height - human_readable_height - space_between_text_and_barcode - 4 * mm # Учитываем поля и отступы
# Создаем штрихкод
available_width = self.page_width - 2 * mm # Учитываем поля
# Приблизительное количество элементов в штрихкоде Code 128 для средней длины
num_elements = 11 * len(barcode_data['barcode_value']) # Примерная оценка: 11 элементов на символ
# Рассчитываем ширину штриха
bar_width = available_width / num_elements
barcode = code128.Code128(
barcode_data['barcode_value'],
barWidth=bar_width,
barHeight=barcode_height,
humanReadable=True
)
# Добавление штрихкодов в список элементов документа
elements = []
for _ in range(barcode_data['num_duplicates']):
elements.append(paragraph)
elements.append(Spacer(1, space_between_text_and_barcode)) # Отступ между текстом и штрихкодом
elements.append(PageBreak())
# Функция для отрисовки штрихкода на canvas
def add_barcode(canvas, doc):
barcode_width = barcode.width
barcode_x = (self.page_width - barcode_width) / 2 # Центрируем штрихкод
barcode_y = human_readable_height + 2 * mm # Размещаем штрихкод снизу с учетом отступа
barcode.drawOn(canvas, barcode_x, barcode_y)
# Создаем документ
doc.build(elements, onFirstPage=add_barcode, onLaterPages=add_barcode) # Убираем последний PageBreak
buffer.seek(0)
return buffer
def _generate_for_one_product_using_img(self, barcode_data: PdfBarcodeImageGenData) -> BytesIO:
with open(barcode_data["barcode_image_url"], 'rb') as pdf_file:
pdf_bytes = pdf_file.read()
pdf_maker = PdfMaker((self.page_width, self.page_height))
for _ in range(barcode_data['num_duplicates']):
pdf_maker.add_pdfs(BytesIO(pdf_bytes))
return pdf_maker.get_bytes()
def _generate_spacers(self) -> BytesIO:
buffer = BytesIO()
doc = self._create_doc(buffer)
elements = []
for _ in range(self.number_of_spacing_pages):
elements.append(PageBreak())
doc.build(elements)
buffer.seek(0)
return buffer
def generate(self, barcodes_data: List[PdfBarcodeGenData | PdfBarcodeImageGenData]) -> BytesIO:
pdf_maker = PdfMaker((self.page_width, self.page_height))
pdf_files: list[BytesIO] = []
for barcode_data in barcodes_data:
if "barcode_value" in barcode_data:
pdf_files.append(self._generate_for_one_product(barcode_data))
else:
pdf_files.append(self._generate_for_one_product_using_img(barcode_data))
pdf_files.append(self._generate_spacers())
for file in pdf_files[:-1]:
pdf_maker.add_pdfs(file)
return pdf_maker.get_bytes()