diff --git a/backend/config/settings.py b/backend/config/settings.py index d56e4c8..894d871 100644 --- a/backend/config/settings.py +++ b/backend/config/settings.py @@ -119,17 +119,17 @@ class Config: '94233': { 'name': '94233 - Rectangle Labels', 'dimensions' : '2 1/2" x 2 1/2"', - 'label_width_inches': 2.5, - 'label_height_inches': 2.5, + 'label_width_inches': 2.35, + 'label_height_inches': 2.3, 'rows': 4, 'columns': 3, - 'margin_top_inches': 0.3, - 'margin_left_inches': 0.3, - 'x_gap_inches': 0.125, - 'y_gap_inches': 0.125, + 'margin_top_inches': 1.05, + 'margin_left_inches': 0.75, + 'x_gap_inches': 0.1, + 'y_gap_inches': 0.0, 'padding_x_inches': 0.15, 'barcode_height_inches': 0.8, - 'barcode_offset_y_inches': 1.1, + 'barcode_offset_y_inches': 1.0, 'text_start_y_inches': 0.85, 'text_line_spacing_inches': 0.2, 'font': 'Helvetica', diff --git a/backend/routes/uploads.py b/backend/routes/uploads.py index 96e9505..7b1a8c4 100644 --- a/backend/routes/uploads.py +++ b/backend/routes/uploads.py @@ -67,8 +67,13 @@ def upload_file(): 'has_header_row': bool(mapping.get('has_header_row', False)) } + # Extract and validate barcode type + barcode_type = request.form.get('barcode_type', 'code128') + if barcode_type not in ('code128', 'qr'): + return jsonify({'error': f"Invalid barcode_type '{barcode_type}'. Valid types: code128, qr"}), 400 + # Process labels - result = process_label_file(file, user_id, secure_filename_result, template_id=template_id, column_mapping=column_mapping) + result = process_label_file(file, user_id, secure_filename_result, template_id=template_id, column_mapping=column_mapping, barcode_type=barcode_type) return jsonify(result) diff --git a/backend/services/label_service.py b/backend/services/label_service.py index feed485..e8af25b 100644 --- a/backend/services/label_service.py +++ b/backend/services/label_service.py @@ -10,7 +10,7 @@ logger = logging.getLogger(__name__) -def process_label_file(file, user_id, secure_filename, template_id=None, column_mapping=None): +def process_label_file(file, user_id, secure_filename, template_id=None, column_mapping=None, barcode_type='code128'): logger.info("Starting process_label_file for user %s", user_id) @@ -80,7 +80,7 @@ def process_label_file(file, user_id, secure_filename, template_id=None, column_ if template_id: template = current_app.config['LABEL_TEMPLATES'][template_id] sheet_gen = PDFSheetGenerator(current_app.config['SHEET_FOLDER'], template) - sheets = sheet_gen.generate_pdf_sheets(labels) + sheets = sheet_gen.generate_pdf_sheets(labels, barcode_type=barcode_type) pdf_time = time.time() - start_time logger.info("PDF generation: %.2fs", pdf_time) diff --git a/backend/utils/BarcodeGenerator.py b/backend/utils/BarcodeGenerator.py index e5c2c94..33dd39a 100644 --- a/backend/utils/BarcodeGenerator.py +++ b/backend/utils/BarcodeGenerator.py @@ -1,9 +1,13 @@ from reportlab.graphics.barcode import code128 +from reportlab.graphics.barcode.qr import QrCodeWidget +from reportlab.graphics import renderPDF +from reportlab.graphics.shapes import Drawing from reportlab.lib.units import inch class BarcodeGenerator: - def __init__(self, template=None): + def __init__(self, template=None, barcode_type='code128'): self.template = template + self.barcode_type = barcode_type self.barcode_height_inches = self.template['barcode_height_inches'] * inch self.barcode_offset_y = self.template['barcode_offset_y_inches'] * inch self.text_start_y = self.template['text_start_y_inches'] * inch @@ -11,33 +15,42 @@ def __init__(self, template=None): self.font = self.template['font'] self.font_size = self.template['font_size'] self.label_width = self.template['label_width_inches'] * inch + self.label_height = self.template['label_height_inches'] * inch self.padding_x = self.template.get('padding_x_inches', 0.1) * inch self.max_text_lines = self.template.get('max_text_lines', 2) self.bar_width = None def calibrate(self, values): - """Calculate a uniform barWidth based on the longest value in the dataset.""" + if self.barcode_type != 'code128': + return longest = max(len(v) for v in values) num_modules = 11 + (longest * 11) + 11 + 13 usable_width = self.label_width - (2 * self.padding_x) - max_barcode_width = usable_width - self.bar_width = max_barcode_width / num_modules + self.bar_width = usable_width / num_modules def draw_label_to_canvas(self, canvas, x, y, barcode_value, text_lines): - # Create barcode with uniform barWidth - barcode = code128.Code128(value=barcode_value, - barHeight=self.barcode_height_inches, - barWidth=self.bar_width - ) + if self.barcode_type == 'qr': + usable_width = self.label_width - (2 * self.padding_x) + available_height = self.label_height - self.barcode_offset_y - (0.05 * inch) + qr_size = min(available_height, usable_width) - # Center barcode horizontally within the label - barcode_width = barcode.width - barcode_x = x + (self.label_width - barcode_width) / 2 + qr = QrCodeWidget(barcode_value) + bounds = qr.getBounds() + raw_w = bounds[2] - bounds[0] + raw_h = bounds[3] - bounds[1] - # Draw barcode centered - barcode.drawOn(canvas, barcode_x, y + self.barcode_offset_y) + d = Drawing(qr_size, qr_size, transform=[qr_size / raw_w, 0, 0, qr_size / raw_h, 0, 0]) + d.add(qr) + + qr_x = x + (self.label_width - qr_size) / 2 + renderPDF.draw(d, canvas, qr_x, y + self.barcode_offset_y) + else: + barcode = code128.Code128(value=barcode_value, + barHeight=self.barcode_height_inches, + barWidth=self.bar_width) + barcode_x = x + (self.label_width - barcode.width) / 2 + barcode.drawOn(canvas, barcode_x, y + self.barcode_offset_y) - # Draw text lines centered in label canvas.setFont(self.font, self.font_size) label_center_x = x + self.label_width / 2 for i, (label, value) in enumerate(text_lines[:self.max_text_lines]): diff --git a/backend/utils/PDFSheetGenerator.py b/backend/utils/PDFSheetGenerator.py index c583486..d84cff6 100644 --- a/backend/utils/PDFSheetGenerator.py +++ b/backend/utils/PDFSheetGenerator.py @@ -20,7 +20,7 @@ def __init__(self, output_folder, template=None): self.rows = self.template['rows'] self.columns = self.template['columns'] - def generate_pdf_sheets(self, labels): + def generate_pdf_sheets(self, labels, barcode_type='code128'): # labels: list of (barcode_value, [(label, value), ...]) tuples # Calculate sheets needed @@ -28,7 +28,7 @@ def generate_pdf_sheets(self, labels): num_sheets = (len(labels) + labels_per_sheet - 1) // labels_per_sheet sheet_files = [] - barcode_gen = BarcodeGenerator(self.template) + barcode_gen = BarcodeGenerator(self.template, barcode_type=barcode_type) # Calibrate uniform barWidth based on longest barcode value all_parts = [bv for bv, _ in labels] diff --git a/frontend-vite/react-ts/src/components/labelUploader.tsx b/frontend-vite/react-ts/src/components/labelUploader.tsx index 3219950..e533f90 100644 --- a/frontend-vite/react-ts/src/components/labelUploader.tsx +++ b/frontend-vite/react-ts/src/components/labelUploader.tsx @@ -40,6 +40,7 @@ function LabelUploader() { { column: 4, label: 'Unit' }, ]); const [hasHeaderRow, setHasHeaderRow] = useState(false); + const [barcodeType, setBarcodeType] = useState<'code128' | 'qr'>('code128'); const templates = [ { id: 'standard_20', name: 'Standard', desc: '20 labels per sheet', size: '1.75" x 1.8"', grid: '5 x 4', rows: 5, cols: 4, maxTextLines: 2 }, @@ -235,6 +236,7 @@ function LabelUploader() { text_columns: textColumns, has_header_row: hasHeaderRow, })); + formData.append('barcode_type', barcodeType); setLoading(true); setError(''); @@ -448,6 +450,31 @@ function LabelUploader() { + {/* Barcode Type */} +
+

Barcode Type

+

Choose the barcode format to generate

+
+ {([ + { id: 'code128', label: 'Code 128', desc: 'Standard linear barcode' }, + { id: 'qr', label: 'QR Code', desc: '2D matrix barcode' }, + ] as const).map((bt) => ( + + ))} +
+
+ {/* Column Mapping */}

Column Mapping