```python from flask import Flask, request, jsonify, send_file import os import uuid import pandas as pd import fitz # PyMuPDF from werkzeug.utils import secure_filename import zipfile from io import BytesIO app = Flask(__name__) app.config['UPLOAD_FOLDER'] = 'uploads' app.config['TEMPLATE_FOLDER'] = os.path.join(app.config['UPLOAD_FOLDER'], 'templates') app.config['OUTPUT_FOLDER'] = os.path.join(app.config['UPLOAD_FOLDER'], 'outputs') # Ensure upload directories exist os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) os.makedirs(app.config['TEMPLATE_FOLDER'], exist_ok=True) os.makedirs(app.config['OUTPUT_FOLDER'], exist_ok=True) # Sample field mapping (would normally come from config) FIELD_MAPPING = { "invoice_number": {"page": 0, "x": 150, "y": 200, "font_size": 10}, "therapist_name": {"page": 0, "x": 150, "y": 220, "font_size": 10}, "parent_id": {"page": 0, "x": 300, "y": 240, "font_size": 10} } @app.route('/') def index(): return send_file('index.html') @app.route('/upload_template', methods=['POST']) def upload_template(): if 'template' not in request.files: return jsonify({"error": "No file uploaded"}), 400 file = request.files['template'] if file.filename == '': return jsonify({"error": "No file selected"}), 400 if file and file.filename.lower().endswith('.pdf'): filename = secure_filename("template.pdf") filepath = os.path.join(app.config['TEMPLATE_FOLDER'], filename) file.save(filepath) return jsonify({"message": "Template uploaded successfully", "filename": filename}) return jsonify({"error": "Invalid file type"}), 400 @app.route('/generate', methods=['POST']) def generate_pdf(): data = request.get_json() if not data or 'rows' not in data or 'headers' not in data or 'data' not in data: return jsonify({"error": "Invalid request data"}), 400 template_path = os.path.join(app.config['TEMPLATE_FOLDER'], 'template.pdf') if not os.path.exists(template_path): return jsonify({"error": "Template PDF not found"}), 404 selected_rows = data['rows'] headers = data['headers'] csv_data = data['data'] if len(selected_rows) == 1: # Single PDF generation row_data = csv_data[selected_rows[0]] output_path = generate_single_pdf(template_path, headers, row_data) return send_file(output_path, as_attachment=True) else: # Multiple PDFs - return as ZIP zip_buffer = generate_multiple_pdfs(template_path, headers, csv_data, selected_rows) return send_file( zip_buffer, mimetype='application/zip', as_attachment=True, download_name='generated_pdfs.zip' ) def generate_single_pdf(template_path, headers, row_data): doc = fitz.open(template_path) page = doc[0] for i, header in enumerate(headers): if header in FIELD_MAPPING: mapping = FIELD_MAPPING[header] text = str(row_data[i]) if i < len(row_data) else "" # Create a text annotation (similar to form field) annot = page.add_text_annot( point=(mapping['x'], mapping['y']), text=text ) annot.set_fontsize(mapping['font_size']) output_filename = f"output_{uuid.uuid4().hex[:8]}.pdf" output_path = os.path.join(app.config['OUTPUT_FOLDER'], output_filename) doc.save(output_path) doc.close() return output_path def generate_multiple_pdfs(template_path, headers, csv_data, selected_rows): zip_buffer = BytesIO() with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file: for row_idx in selected_rows: if row_idx < len(csv_data): row_data = csv_data[row_idx] doc = fitz.open(template_path) page = doc[0] for i, header in enumerate(headers): if header in FIELD_MAPPING: mapping = FIELD_MAPPING[header] text = str(row_data[i]) if i < len(row_data) else "" annot = page.add_text_annot( point=(mapping['x'], mapping['y']), text=text ) annot.set_fontsize(mapping['font_size']) pdf_filename = f"output_row_{row_idx + 1}.pdf" pdf_path = os.path.join(app.config['OUTPUT_FOLDER'], pdf_filename) doc.save(pdf_path) doc.close() zip_file.write(pdf_path, pdf_filename) os.remove(pdf_path) zip_buffer.seek(0) return zip_buffer if __name__ == '__main__': app.run(debug=True, port=5000) ```