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 constants import APP_PATH from schemas.barcode import PdfBarcodeGenData 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(self, barcodes_data: List[PdfBarcodeGenData]) -> BytesIO: buffer = BytesIO() # Создаем документ с указанным размером страницы doc = self._create_doc(buffer) # Список элементов для добавления в документ elements = [] product_barcode_canvases = [] for barcode_data in barcodes_data: # Создаем абзац с новым стилем 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 ) product_barcode_canvases.append(barcode) # Добавление штрихкодов в список элементов документа for _ in range(barcode_data.num_duplicates): elements.append(paragraph) elements.append(Spacer(1, space_between_text_and_barcode)) # Отступ между текстом и штрихкодом elements.append(PageBreak()) # Добавление спейсеров for _ in range(self.number_of_spacing_pages): elements.append(PageBreak()) # Удалить последние спейсеры elements = elements[:-2] product_counter, product_barcodes_counter = 0, 0 # Функция для отрисовки штрихкода на canvas def add_barcode(canvas, doc): nonlocal product_barcodes_counter, product_counter barcode_canvas = product_barcode_canvases[product_counter] product_barcodes_counter += 1 # Если данная страница это спейсер, то оставить пустой num_duplicates = barcodes_data[product_counter].num_duplicates if product_barcodes_counter > num_duplicates: if product_barcodes_counter >= num_duplicates + self.number_of_spacing_pages: product_barcodes_counter = 0 product_counter += 1 return # Отрисовка штрихкода barcode_width = barcode_canvas.width barcode_x = (self.page_width - barcode_width) / 2 # Центрируем штрихкод barcode_y = human_readable_height + 2 * mm # Размещаем штрихкод снизу с учетом отступа barcode_canvas.drawOn(canvas, barcode_x, barcode_y) # Создаем документ doc.build(elements[:-1], onFirstPage=add_barcode, onLaterPages=add_barcode) # Убираем последний PageBreak buffer.seek(0) return buffer def generate_barcode_image(self, barcode_image_url: str, path_to_save_pdf: str): print(type(path_to_save_pdf)) print(path_to_save_pdf) c = canvas.Canvas(path_to_save_pdf, pagesize=(self.page_width, self.page_height)) c.drawImage(barcode_image_url, 0, 0, width=self.page_width, height=self.page_height) c.save()