From 2c01db9fea4d8c71a797ea7e7c42c2f71ca7ee44 Mon Sep 17 00:00:00 2001 From: Scheianu Ionut Date: Sat, 20 Sep 2025 17:31:20 +0300 Subject: [PATCH] uploadte to corect preview and print the labels for 1 row --- py_app/app/__pycache__/routes.cpython-312.pyc | Bin 65588 -> 71792 bytes py_app/app/pdf_generator.py | 357 ++++++++++++++++++ py_app/app/routes.py | 145 ++++++- py_app/app/templates/print_module.html | 292 ++++++++++---- py_app/chrome_extension/background.js | 140 +++++-- py_app/chrome_extension/content.js | 51 ++- 6 files changed, 865 insertions(+), 120 deletions(-) create mode 100644 py_app/app/pdf_generator.py diff --git a/py_app/app/__pycache__/routes.cpython-312.pyc b/py_app/app/__pycache__/routes.cpython-312.pyc index 875aedaf4a2190fd34fe984bc0a04b4fe6c793ad..a6d5be39f56e35ff9245989aed398d204e851d07 100644 GIT binary patch delta 6300 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 HdFcNDwt~%Q delta 765 zcmZ9KT}V@57{}k|oSnH3cUEX_=EtTD+VP@cSdxge>mW!mvo0*0S+J#?&8%oLVi$!M zeZ0iEiW0k9pmG``Z~P$YA`oGaXrpqj%~{H9v`Q#?AC-t+4!`$*o{#_gyoZw;*E|nt z#5X3Bfw0Ag=Pr4d?~7LMw|>G&_28uE`F%BBPnpNk%RRG3IIJSX;|r6>jwEMK_c&=z zZ?*qd()UkE$90hU)dvRgEnHSdKIQ>N>BHm`7*mg~wDO?RJL$VHLCZ6Ch|;D^6eejd zlVnBl)f(F<5aP#!?RNPv+m?*IJ%O(7KJS3%H5;wQ zm#j~Ig&3t5Hg3U!n%anQ+9ixE(vi(-NT}1BS+Kmr$Ol9bvzNFXy-cU`Rgk3b^TRMl zZLvCfE?x@)J@u{R2)}!Ga+5?@AFeiXfCRKVnh*z(Yl9H@Tj${edTJ_E)o3qr-CQT} z6a`@j+IIIr7$n#DPBI9Rj-DtqD!=rwzrLM0pOgf-n3>8V!iMNaI9ZMjx`z5H!}j-~ zKfhNSQ!G;{u!xV)<16*mg5XM+opE#BZB4>lb}uj-oYz(UNEu7N5)fR<9xYTjc&4`e z70l;p=`r#`?{@GroW*V9XUYM!O>jGuya_@@8RjmhLAfDUbKvA5@=4RQXgnA@~ diff --git a/py_app/app/pdf_generator.py b/py_app/app/pdf_generator.py new file mode 100644 index 0000000..43d7e9f --- /dev/null +++ b/py_app/app/pdf_generator.py @@ -0,0 +1,357 @@ +""" +PDF Label Generator for Print Module +Generates 80x110mm labels with sequential numbering based on quantity +""" +from reportlab.lib.pagesizes import letter, A4 +from reportlab.lib.units import mm +from reportlab.lib import colors +from reportlab.pdfgen import canvas +from reportlab.lib.styles import getSampleStyleSheet +from reportlab.platypus import Paragraph +from reportlab.lib.enums import TA_CENTER, TA_LEFT +from reportlab.graphics.barcode import code128 +from reportlab.graphics import renderPDF +from reportlab.graphics.shapes import Drawing +import os +from flask import current_app +import io + + +def mm_to_points(mm_value): + """Convert millimeters to points (ReportLab uses points)""" + return mm_value * mm + + +class LabelPDFGenerator: + def __init__(self): + # Label dimensions: 80mm x 110mm + self.label_width = mm_to_points(80) + self.label_height = mm_to_points(110) + + # Match the HTML preview dimensions exactly + # Preview: 227.4px width x 321.3px height + # Convert to proportional dimensions for 80x110mm + self.content_width = mm_to_points(60) # ~227px scaled to 80mm + self.content_height = mm_to_points(85) # ~321px scaled to 110mm + + # Position content in label, leaving space for barcodes + self.content_x = mm_to_points(3) # 3mm from left edge + self.content_y = mm_to_points(15) # 15mm from bottom (space for bottom barcode) + + # Row dimensions (9 rows total, row 6 is double height) + self.row_height = self.content_height / 10 # 8.5mm per standard row + self.double_row_height = self.row_height * 2 + + # Column split at 40% (90.96px / 227.4px = 40%) + self.left_column_width = self.content_width * 0.4 + self.right_column_width = self.content_width * 0.6 + + # Vertical divider starts from row 3 + self.vertical_divider_start_y = self.content_y + self.content_height - (2 * self.row_height) + + def generate_labels_pdf(self, order_data, quantity): + """ + Generate PDF with multiple labels based on quantity + Creates sequential labels: CP00000711-001 to CP00000711-XXX + """ + buffer = io.BytesIO() + + # Create canvas with label dimensions + c = canvas.Canvas(buffer, pagesize=(self.label_width, self.label_height)) + + # Extract base production order number for sequential numbering + prod_order = order_data.get('comanda_productie', 'CP00000000') + + # Generate labels for each quantity + for i in range(1, quantity + 1): + if i > 1: # Add new page for each label except first + c.showPage() + + # Create sequential label number: CP00000711-001, CP00000711-002, etc. + sequential_number = f"{prod_order}-{i:03d}" + + # Draw single label + self._draw_label(c, order_data, sequential_number, i, quantity) + + c.save() + buffer.seek(0) + return buffer + + def _draw_label(self, canvas, order_data, sequential_number, current_num, total_qty): + """Draw a single label matching the HTML preview layout exactly""" + + # Draw main content border (like the HTML preview rectangle) + canvas.setStrokeColor(colors.black) + canvas.setLineWidth(2) + canvas.rect(self.content_x, self.content_y, self.content_width, self.content_height) + + # Calculate row positions from top + current_y = self.content_y + self.content_height + + # Row 1: Company Header - "INNOFA RROMANIA SRL" + row_y = current_y - self.row_height + canvas.setFont("Helvetica-Bold", 10) + text = "INNOFA RROMANIA SRL" + text_width = canvas.stringWidth(text, "Helvetica-Bold", 10) + x_centered = self.content_x + (self.content_width - text_width) / 2 + canvas.drawString(x_centered, row_y + self.row_height/3, text) + current_y = row_y + + # Row 2: Customer Name + row_y = current_y - self.row_height + canvas.setFont("Helvetica-Bold", 9) + customer_name = str(order_data.get('customer_name', ''))[:30] + text_width = canvas.stringWidth(customer_name, "Helvetica-Bold", 9) + x_centered = self.content_x + (self.content_width - text_width) / 2 + canvas.drawString(x_centered, row_y + self.row_height/3, customer_name) + current_y = row_y + + # Draw horizontal lines after rows 1 and 2 + canvas.setLineWidth(1) + canvas.line(self.content_x, current_y, self.content_x + self.content_width, current_y) + canvas.line(self.content_x, current_y + self.row_height, self.content_x + self.content_width, current_y + self.row_height) + + # Draw vertical divider line (starts from row 3, goes to bottom) + vertical_x = self.content_x + self.left_column_width + canvas.line(vertical_x, current_y, vertical_x, self.content_y) + + # Row 3: Quantity ordered + row_y = current_y - self.row_height + canvas.setFont("Helvetica", 8) + canvas.drawString(self.content_x + mm_to_points(2), row_y + self.row_height/3, "Quantity ordered") + canvas.setFont("Helvetica-Bold", 11) + quantity = str(order_data.get('cantitate', '0')) + q_text_width = canvas.stringWidth(quantity, "Helvetica-Bold", 11) + q_x_centered = vertical_x + (self.right_column_width - q_text_width) / 2 + canvas.drawString(q_x_centered, row_y + self.row_height/3, quantity) + current_y = row_y + + # Row 4: Customer order + row_y = current_y - self.row_height + canvas.setFont("Helvetica", 8) + canvas.drawString(self.content_x + mm_to_points(2), row_y + self.row_height/3, "Customer order") + canvas.setFont("Helvetica-Bold", 10) + customer_order = str(order_data.get('com_achiz_client', 'N/A'))[:15] + co_text_width = canvas.stringWidth(customer_order, "Helvetica-Bold", 10) + co_x_centered = vertical_x + (self.right_column_width - co_text_width) / 2 + canvas.drawString(co_x_centered, row_y + self.row_height/3, customer_order) + current_y = row_y + + # Row 5: Delivery date + row_y = current_y - self.row_height + canvas.setFont("Helvetica", 8) + canvas.drawString(self.content_x + mm_to_points(2), row_y + self.row_height/3, "Delivery date") + canvas.setFont("Helvetica-Bold", 10) + delivery_date = str(order_data.get('data_livrare', 'N/A')) + if delivery_date != 'N/A' and delivery_date: + try: + # Format date if it's a valid date + from datetime import datetime + if isinstance(delivery_date, str) and len(delivery_date) > 8: + delivery_date = delivery_date[:10] # Take first 10 chars for date + except: + pass + dd_text_width = canvas.stringWidth(delivery_date, "Helvetica-Bold", 10) + dd_x_centered = vertical_x + (self.right_column_width - dd_text_width) / 2 + canvas.drawString(dd_x_centered, row_y + self.row_height/3, delivery_date) + current_y = row_y + + # Row 6: Description (double height) + row_y = current_y - self.double_row_height + canvas.setFont("Helvetica", 8) + canvas.drawString(self.content_x + mm_to_points(2), row_y + self.double_row_height/2, "Description") + + # Handle description text wrapping for double height area + canvas.setFont("Helvetica-Bold", 9) + description = str(order_data.get('descr_com_prod', 'N/A')) + max_chars_per_line = 18 + lines = [] + words = description.split() + current_line = "" + + for word in words: + if len(current_line + word + " ") <= max_chars_per_line: + current_line += word + " " + else: + if current_line: + lines.append(current_line.strip()) + current_line = word + " " + if current_line: + lines.append(current_line.strip()) + + # Draw up to 3 lines in the double height area + line_spacing = self.double_row_height / 4 + start_y = row_y + 3 * line_spacing + for i, line in enumerate(lines[:3]): + line_y = start_y - (i * line_spacing) + l_text_width = canvas.stringWidth(line, "Helvetica-Bold", 9) + l_x_centered = vertical_x + (self.right_column_width - l_text_width) / 2 + canvas.drawString(l_x_centered, line_y, line) + + current_y = row_y + + # Row 7: Size + row_y = current_y - self.row_height + canvas.setFont("Helvetica", 8) + canvas.drawString(self.content_x + mm_to_points(2), row_y + self.row_height/3, "Size") + canvas.setFont("Helvetica-Bold", 10) + size = str(order_data.get('dimensiune', 'N/A'))[:12] + s_text_width = canvas.stringWidth(size, "Helvetica-Bold", 10) + s_x_centered = vertical_x + (self.right_column_width - s_text_width) / 2 + canvas.drawString(s_x_centered, row_y + self.row_height/3, size) + current_y = row_y + + # Row 8: Article Code + row_y = current_y - self.row_height + canvas.setFont("Helvetica", 8) + canvas.drawString(self.content_x + mm_to_points(2), row_y + self.row_height/3, "Article Code") + canvas.setFont("Helvetica-Bold", 10) + article_code = str(order_data.get('cod_articol', 'N/A'))[:12] + ac_text_width = canvas.stringWidth(article_code, "Helvetica-Bold", 10) + ac_x_centered = vertical_x + (self.right_column_width - ac_text_width) / 2 + canvas.drawString(ac_x_centered, row_y + self.row_height/3, article_code) + current_y = row_y + + # Row 9: Prod Order (but we'll show sequential number instead) + row_y = current_y - self.row_height + canvas.setFont("Helvetica", 8) + canvas.drawString(self.content_x + mm_to_points(2), row_y + self.row_height/3, "Prod Order") + canvas.setFont("Helvetica-Bold", 10) + # Show original production order + prod_order = str(order_data.get('comanda_productie', 'N/A')) + po_text_width = canvas.stringWidth(prod_order, "Helvetica-Bold", 10) + po_x_centered = vertical_x + (self.right_column_width - po_text_width) / 2 + canvas.drawString(po_x_centered, row_y + self.row_height/3, prod_order) + + # Draw all horizontal lines between rows (from row 3 onwards) + for i in range(6): # 6 lines between 7 rows (3-9) + line_y = self.content_y + self.content_height - (2 + i + 1) * self.row_height + if i == 3: # Account for double height description row + line_y -= self.row_height + canvas.line(self.content_x, line_y, self.content_x + self.content_width, line_y) + + # Bottom horizontal barcode - positioned within label bounds + barcode_area_height = mm_to_points(12) # Reserve space for barcode + barcode_y = mm_to_points(5) # 5mm from bottom of label + barcode_width = self.content_width # Use full content width + barcode_x = self.content_x + + try: + # Create barcode for sequential number + barcode = code128.Code128(sequential_number, + barWidth=0.25*mm, # Adjust bar width for better fit + barHeight=mm_to_points(10)) # Increase height to 10mm + + # Always scale to fit the full allocated width + scale_factor = barcode_width / barcode.width + canvas.saveState() + canvas.translate(barcode_x, barcode_y) + canvas.scale(scale_factor, 1) + barcode.drawOn(canvas, 0, 0) + canvas.restoreState() + + # NO TEXT BELOW BARCODE - Remove all text rendering for horizontal barcode + + except Exception as e: + # Fallback: Simple barcode pattern that fills the width + canvas.setStrokeColor(colors.black) + canvas.setFillColor(colors.black) + bar_width = barcode_width / 50 # 50 bars across width + for i in range(50): + if i % 3 < 2: # Create barcode-like pattern + x_pos = barcode_x + (i * bar_width) + canvas.rect(x_pos, barcode_y, bar_width * 0.8, mm_to_points(8), fill=1) + + # Right side vertical barcode - positioned 3mm further right and fill frame + vertical_barcode_x = self.content_x + self.content_width + mm_to_points(4) # Moved 3mm right (1mm + 3mm = 4mm) + vertical_barcode_y = self.content_y + vertical_barcode_height = self.content_height + vertical_barcode_width = mm_to_points(12) # Increased width for better fill + + try: + # Create vertical barcode code + vertical_code = f"{current_num:03d}-{total_qty:02d}" + + # Create a vertical barcode using Code128 + v_barcode = code128.Code128(vertical_code, + barWidth=0.15*mm, # Thinner bars for better fit + barHeight=mm_to_points(8)) # Increased bar height + + # Draw rotated barcode - fill the entire frame height + canvas.saveState() + canvas.translate(vertical_barcode_x + mm_to_points(6), vertical_barcode_y) + canvas.rotate(90) + + # Always scale to fill the frame height + scale_factor = vertical_barcode_height / v_barcode.width + canvas.scale(scale_factor, 1) + + v_barcode.drawOn(canvas, 0, 0) + canvas.restoreState() + + # NO TEXT FOR VERTICAL BARCODE - Remove all text rendering + + except Exception as e: + # Fallback: Vertical barcode pattern that fills the frame + canvas.setStrokeColor(colors.black) + canvas.setFillColor(colors.black) + bar_height = vertical_barcode_height / 60 # 60 bars across height + for i in range(60): + if i % 3 < 2: # Create barcode pattern + y_pos = vertical_barcode_y + (i * bar_height) + canvas.rect(vertical_barcode_x, y_pos, mm_to_points(8), bar_height * 0.8, fill=1) + + +def generate_order_labels_pdf(order_id, order_data): + """ + Main function to generate PDF for an order with multiple labels + """ + try: + generator = LabelPDFGenerator() + + # Get quantity from order data + quantity = int(order_data.get('cantitate', 1)) + + # Generate PDF + pdf_buffer = generator.generate_labels_pdf(order_data, quantity) + + return pdf_buffer + + except Exception as e: + print(f"Error generating PDF labels: {e}") + raise e + + +def update_order_printed_status(order_id): + """ + Update the order status to printed in the database + """ + try: + from .print_module import get_db_connection + + conn = get_db_connection() + cursor = conn.cursor() + + # Check if printed_labels column exists + cursor.execute("SHOW COLUMNS FROM order_for_labels LIKE 'printed_labels'") + column_exists = cursor.fetchone() + + if column_exists: + # Update printed status + cursor.execute(""" + UPDATE order_for_labels + SET printed_labels = 1, updated_at = NOW() + WHERE id = %s + """, (order_id,)) + else: + # If column doesn't exist, we could add it or use another method + print(f"Warning: printed_labels column doesn't exist for order {order_id}") + + conn.commit() + conn.close() + + return True + + except Exception as e: + print(f"Error updating printed status for order {order_id}: {e}") + return False \ No newline at end of file diff --git a/py_app/app/routes.py b/py_app/app/routes.py index bb9340f..f13794a 100644 --- a/py_app/app/routes.py +++ b/py_app/app/routes.py @@ -1308,14 +1308,155 @@ def view_orders(): @bp.route('/get_unprinted_orders', methods=['GET']) def get_unprinted_orders(): """Get all rows from order_for_labels where printed != 1""" + print(f"DEBUG: get_unprinted_orders called. Session role: {session.get('role')}") + + if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']: + print(f"DEBUG: Access denied for role: {session.get('role')}") + return jsonify({'error': 'Access denied. Required roles: superadmin, warehouse_manager, etichete'}), 403 + + try: + print("DEBUG: Calling get_unprinted_orders_data()") + data = get_unprinted_orders_data() + print(f"DEBUG: Retrieved {len(data)} orders") + return jsonify(data) + + except Exception as e: + print(f"DEBUG: Error in get_unprinted_orders: {e}") + import traceback + traceback.print_exc() + return jsonify({'error': str(e)}), 500 + +@bp.route('/generate_labels_pdf/', methods=['POST']) +def generate_labels_pdf(order_id): + """Generate PDF labels for a specific order""" + print(f"DEBUG: generate_labels_pdf called for order_id: {order_id}") + + if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']: + print(f"DEBUG: Access denied for role: {session.get('role')}") + return jsonify({'error': 'Access denied. Required roles: superadmin, warehouse_manager, etichete'}), 403 + + try: + from .pdf_generator import generate_order_labels_pdf, update_order_printed_status + from .print_module import get_db_connection + from flask import make_response + + # Get order data from database + conn = get_db_connection() + cursor = conn.cursor() + + cursor.execute(""" + SELECT id, comanda_productie, cod_articol, descr_com_prod, cantitate, + data_livrare, dimensiune, com_achiz_client, nr_linie_com_client, customer_name, + customer_article_number, open_for_order, line_number, + printed_labels, created_at, updated_at + FROM order_for_labels + WHERE id = %s + """, (order_id,)) + + row = cursor.fetchone() + conn.close() + + if not row: + return jsonify({'error': 'Order not found'}), 404 + + # Create order data dictionary + order_data = { + 'id': row[0], + 'comanda_productie': row[1], + 'cod_articol': row[2], + 'descr_com_prod': row[3], + 'cantitate': row[4], + 'data_livrare': row[5], + 'dimensiune': row[6], + 'com_achiz_client': row[7], + 'nr_linie_com_client': row[8], + 'customer_name': row[9], + 'customer_article_number': row[10], + 'open_for_order': row[11], + 'line_number': row[12], + 'printed_labels': row[13] if row[13] is not None else 0, + 'created_at': row[14], + 'updated_at': row[15] + } + + print(f"DEBUG: Generating PDF for order {order_id} with quantity {order_data['cantitate']}") + + # Generate PDF + pdf_buffer = generate_order_labels_pdf(order_id, order_data) + + # Update printed status in database + update_success = update_order_printed_status(order_id) + if not update_success: + print(f"Warning: Could not update printed status for order {order_id}") + + # Create response with PDF + response = make_response(pdf_buffer.getvalue()) + response.headers['Content-Type'] = 'application/pdf' + response.headers['Content-Disposition'] = f'inline; filename="labels_order_{order_id}_{order_data["comanda_productie"]}.pdf"' + + print(f"DEBUG: PDF generated successfully for order {order_id}") + return response + + except Exception as e: + print(f"DEBUG: Error generating PDF for order {order_id}: {e}") + import traceback + traceback.print_exc() + return jsonify({'error': str(e)}), 500 + +@bp.route('/get_order_data/', methods=['GET']) +def get_order_data(order_id): + """Get specific order data for preview""" + print(f"DEBUG: get_order_data called for order_id: {order_id}") + if 'role' not in session or session['role'] not in ['superadmin', 'warehouse_manager', 'etichete']: return jsonify({'error': 'Access denied'}), 403 try: - data = get_unprinted_orders_data() - return jsonify(data) + from .print_module import get_db_connection + + conn = get_db_connection() + cursor = conn.cursor() + + cursor.execute(""" + SELECT id, comanda_productie, cod_articol, descr_com_prod, cantitate, + data_livrare, dimensiune, com_achiz_client, nr_linie_com_client, customer_name, + customer_article_number, open_for_order, line_number, + printed_labels, created_at, updated_at + FROM order_for_labels + WHERE id = %s + """, (order_id,)) + + row = cursor.fetchone() + conn.close() + + if not row: + return jsonify({'error': 'Order not found'}), 404 + + order_data = { + 'id': row[0], + 'comanda_productie': row[1], + 'cod_articol': row[2], + 'descr_com_prod': row[3], + 'cantitate': row[4], + 'data_livrare': str(row[5]) if row[5] else 'N/A', + 'dimensiune': row[6], + 'com_achiz_client': row[7], + 'nr_linie_com_client': row[8], + 'customer_name': row[9], + 'customer_article_number': row[10], + 'open_for_order': row[11], + 'line_number': row[12], + 'printed_labels': row[13] if row[13] is not None else 0, + 'created_at': str(row[14]) if row[14] else 'N/A', + 'updated_at': str(row[15]) if row[15] else 'N/A' + } + + return jsonify(order_data) except Exception as e: + print(f"DEBUG: Error getting order data for {order_id}: {e}") + import traceback + traceback.print_exc() return jsonify({'error': str(e)}), 500 @warehouse_bp.route('/create_locations', methods=['GET', 'POST']) diff --git a/py_app/app/templates/print_module.html b/py_app/app/templates/print_module.html index 2d1d575..9c09c66 100644 --- a/py_app/app/templates/print_module.html +++ b/py_app/app/templates/print_module.html @@ -235,14 +235,28 @@ table tbody tr.selected td { -
- - + + +
+
+ 📄 PDF Label Generation
+ Labels will be generated as PDF files for universal printing compatibility.
+ Works with any browser and printer - no extensions needed! +
+
+ + +
+ + +
+
+ Creates sequential labels based on quantity (e.g., CP00000711-001 to CP00000711-063)
- - 🔗 Install Direct Print Extension - + + � PDF labels can be printed directly from your browser or saved for later use +
@@ -281,9 +295,24 @@ table tbody tr.selected td { {% endblock %} \ No newline at end of file diff --git a/py_app/chrome_extension/background.js b/py_app/chrome_extension/background.js index a8f7f23..7b124e9 100644 --- a/py_app/chrome_extension/background.js +++ b/py_app/chrome_extension/background.js @@ -33,10 +33,10 @@ async function handlePrintLabel(printData, sendResponse) { // Check if printing API is available if (!chrome.printing || !chrome.printing.getPrinters) { - console.error('Chrome printing API not available'); + console.error('Chrome printing API not available - browser may be Brave, Edge, or older Chrome'); sendResponse({ success: false, - error: 'Chrome printing API not available. Please ensure you are using Chrome 85+ and the extension has proper permissions.' + error: 'Direct printing API not available in this browser. The extension works best in Google Chrome 85+. Using fallback method instead.' }); return; } @@ -50,13 +50,21 @@ async function handlePrintLabel(printData, sendResponse) { return; } - // Find default printer or use first available - const defaultPrinter = printers.find(p => p.isDefault) || printers[0]; - console.log('Using printer:', defaultPrinter); + // Find selected printer or use default/first available + let selectedPrinter; + if (printData.printerId && printData.printerId !== 'default') { + selectedPrinter = printers.find(p => p.id === printData.printerId); + } + + if (!selectedPrinter) { + selectedPrinter = printers.find(p => p.isDefault) || printers[0]; + } + + console.log('Using printer:', selectedPrinter); // Create print job const printJob = { - printerId: defaultPrinter.id, + printerId: selectedPrinter.id, ticket: { version: '1.0', print: { @@ -100,7 +108,7 @@ async function handlePrintLabel(printData, sendResponse) { lastPrint: { timestamp: Date.now(), jobId: result.jobId, - printer: defaultPrinter.displayName + printer: selectedPrinter.displayName } }); } else { @@ -120,7 +128,7 @@ async function getPrinters(sendResponse) { if (!chrome.printing || !chrome.printing.getPrinters) { sendResponse({ success: false, - error: 'Chrome printing API not available' + error: 'Direct printing API not available in this browser (works in Chrome only)' }); return; } @@ -144,38 +152,108 @@ chrome.runtime.onInstalled.addListener((details) => { }); }); -// Fallback print method using tabs API +// Fallback print method using tabs API (works in Brave, Edge, Chrome) async function handleFallbackPrint(printData, sendResponse) { try { - console.log('Using fallback print method'); + console.log('Using fallback print method for Brave/Edge/Chrome'); - // Create a new tab with the print content + // Create enhanced HTML with better print styles + const enhancedHTML = ` + + + + Quality Recticel Label + + + + + + + + + `; + + // Create a new tab with the enhanced print content const tab = await chrome.tabs.create({ - url: 'data:text/html,' + encodeURIComponent(printData.html), - active: false + url: 'data:text/html;charset=utf-8,' + encodeURIComponent(enhancedHTML), + active: true // Make active so user can see the print dialog }); - // Wait for tab to load, then print - setTimeout(async () => { - try { - await chrome.tabs.executeScript(tab.id, { - code: 'window.print();' - }); - - // Close the tab after printing - setTimeout(() => { - chrome.tabs.remove(tab.id); - }, 1000); - - sendResponse({success: true, method: 'fallback'}); - } catch (error) { - sendResponse({success: false, error: 'Fallback print failed: ' + error.message}); - } - }, 1000); + console.log('Created print tab:', tab.id); + sendResponse({success: true, method: 'fallback_tab', tabId: tab.id}); } catch (error) { console.error('Fallback print error:', error); - sendResponse({success: false, error: error.message}); + sendResponse({success: false, error: 'Browser print fallback failed: ' + error.message}); } } diff --git a/py_app/chrome_extension/content.js b/py_app/chrome_extension/content.js index 71ff214..e106012 100644 --- a/py_app/chrome_extension/content.js +++ b/py_app/chrome_extension/content.js @@ -30,6 +30,9 @@ function initializePrintExtension() { } function addExtensionStatusIndicator() { + // Detect browser type + const browserType = getBrowserType(); + // Create status indicator element const statusIndicator = document.createElement('div'); statusIndicator.id = 'extension-status'; @@ -37,24 +40,39 @@ function addExtensionStatusIndicator() { position: fixed; top: 10px; right: 10px; - background: #28a745; - color: white; - padding: 5px 10px; + background: ${browserType === 'chrome' ? '#28a745' : '#ffc107'}; + color: ${browserType === 'chrome' ? 'white' : 'black'}; + padding: 8px 12px; border-radius: 4px; font-size: 12px; z-index: 9999; box-shadow: 0 2px 4px rgba(0,0,0,0.2); + max-width: 250px; `; - statusIndicator.textContent = '🖨️ Print Extension Active'; + + if (browserType === 'chrome') { + statusIndicator.textContent = '🖨️ Print Extension Active (Direct Print Available)'; + } else { + statusIndicator.innerHTML = `🖨️ Print Extension Active
${browserType.toUpperCase()} - Browser Print Mode`; + } document.body.appendChild(statusIndicator); - // Hide after 3 seconds + // Hide after 4 seconds (longer for non-Chrome browsers) setTimeout(() => { statusIndicator.style.opacity = '0'; statusIndicator.style.transition = 'opacity 0.5s'; setTimeout(() => statusIndicator.remove(), 500); - }, 3000); + }, browserType === 'chrome' ? 3000 : 5000); +} + +// Detect browser type +function getBrowserType() { + const userAgent = navigator.userAgent.toLowerCase(); + if (userAgent.includes('edg/')) return 'edge'; + if (userAgent.includes('brave')) return 'brave'; + if (userAgent.includes('chrome') && !userAgent.includes('edg/')) return 'chrome'; + return 'chromium'; } function overridePrintButton() { @@ -71,9 +89,15 @@ function overridePrintButton() { // Add new event listener for extension printing newPrintButton.addEventListener('click', handleExtensionPrint); - // Update button text to indicate direct printing - newPrintButton.innerHTML = '🖨️ Print Direct'; - newPrintButton.title = 'Print directly to default printer (Chrome Extension)'; + // Update button text based on browser capabilities + const browserType = getBrowserType(); + if (browserType === 'chrome') { + newPrintButton.innerHTML = '🖨️ Print Direct'; + newPrintButton.title = 'Print directly to default printer (Chrome Extension)'; + } else { + newPrintButton.innerHTML = '🖨️ Print Enhanced'; + newPrintButton.title = `Enhanced printing for ${browserType.toUpperCase()} browser - Opens optimized print dialog`; + } console.log('Print button override complete'); } else { @@ -103,12 +127,16 @@ function handleExtensionPrint(event) { // Create complete HTML for printing const printHTML = createPrintableHTML(labelPreview); + // Get selected printer from dropdown + const selectedPrinterId = window.getSelectedPrinter ? window.getSelectedPrinter() : 'default'; + // Try direct printing first, then fallback chrome.runtime.sendMessage({ action: 'print_label', data: { html: printHTML, - timestamp: Date.now() + timestamp: Date.now(), + printerId: selectedPrinterId } }, (response) => { if (response && response.success) { @@ -122,7 +150,8 @@ function handleExtensionPrint(event) { action: 'fallback_print', data: { html: printHTML, - timestamp: Date.now() + timestamp: Date.now(), + printerId: selectedPrinterId } }, handlePrintResponse); }