Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions backend/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
7 changes: 6 additions & 1 deletion backend/routes/uploads.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
4 changes: 2 additions & 2 deletions backend/services/label_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down
43 changes: 28 additions & 15 deletions backend/utils/BarcodeGenerator.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,56 @@
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
self.text_line_spacing = self.template['text_line_spacing_inches'] * inch
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]):
Expand Down
4 changes: 2 additions & 2 deletions backend/utils/PDFSheetGenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ 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
labels_per_sheet = self.rows * self.columns
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]
Expand Down
27 changes: 27 additions & 0 deletions frontend-vite/react-ts/src/components/labelUploader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down Expand Up @@ -235,6 +236,7 @@ function LabelUploader() {
text_columns: textColumns,
has_header_row: hasHeaderRow,
}));
formData.append('barcode_type', barcodeType);

setLoading(true);
setError('');
Expand Down Expand Up @@ -448,6 +450,31 @@ function LabelUploader() {
</div>
</div>

{/* Barcode Type */}
<div className="px-6 py-5 border-t border-border">
<h3 className="font-heading text-sm font-semibold text-foreground mb-1">Barcode Type</h3>
<p className="text-xs text-muted-foreground mb-4">Choose the barcode format to generate</p>
<div className="flex gap-3">
{([
{ id: 'code128', label: 'Code 128', desc: 'Standard linear barcode' },
{ id: 'qr', label: 'QR Code', desc: '2D matrix barcode' },
] as const).map((bt) => (
<button
key={bt.id}
onClick={() => setBarcodeType(bt.id)}
className={`flex flex-col items-start p-3 rounded-[16px] border-2 transition-all cursor-pointer bg-card text-left flex-1 ${
barcodeType === bt.id
? 'border-primary shadow-sm'
: 'border-border hover:border-muted-foreground/30'
}`}
>
<span className="font-heading text-xs font-semibold text-foreground">{bt.label}</span>
<span className="text-[11px] text-muted-foreground mt-0.5">{bt.desc}</span>
</button>
))}
</div>
</div>

{/* Column Mapping */}
<div className="px-6 py-5 border-t border-border">
<h3 className="font-heading text-sm font-semibold text-foreground mb-1">Column Mapping</h3>
Expand Down
Loading