287 lines
9.3 KiB
Python
287 lines
9.3 KiB
Python
import os
|
|
import io
|
|
import base64
|
|
import json
|
|
import uuid
|
|
from datetime import datetime
|
|
from PIL import Image, ImageDraw
|
|
import qrcode
|
|
from qrcode.image.styledpil import StyledPilImage
|
|
from qrcode.image.styles.moduledrawers import RoundedModuleDrawer, CircleModuleDrawer, SquareModuleDrawer
|
|
from qrcode.image.styles.colorfills import SolidFillColorMask
|
|
from flask import Flask, request, jsonify, send_file, render_template
|
|
from flask_cors import CORS
|
|
|
|
app = Flask(__name__)
|
|
CORS(app)
|
|
|
|
# Configuration
|
|
UPLOAD_FOLDER = 'static/qr_codes'
|
|
LOGOS_FOLDER = 'static/logos'
|
|
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
|
|
os.makedirs(LOGOS_FOLDER, exist_ok=True)
|
|
|
|
# In-memory storage for QR codes (in production, use a database)
|
|
qr_codes_db = {}
|
|
|
|
class QRCodeGenerator:
|
|
def __init__(self):
|
|
self.default_settings = {
|
|
'size': 10,
|
|
'border': 4,
|
|
'error_correction': qrcode.constants.ERROR_CORRECT_M,
|
|
'foreground_color': '#000000',
|
|
'background_color': '#FFFFFF',
|
|
'style': 'square'
|
|
}
|
|
|
|
def generate_qr_code(self, data, settings=None):
|
|
"""Generate QR code with custom settings"""
|
|
if settings is None:
|
|
settings = self.default_settings.copy()
|
|
else:
|
|
merged_settings = self.default_settings.copy()
|
|
merged_settings.update(settings)
|
|
settings = merged_settings
|
|
|
|
# Create QR code instance
|
|
qr = qrcode.QRCode(
|
|
version=1,
|
|
error_correction=settings['error_correction'],
|
|
box_size=settings['size'],
|
|
border=settings['border'],
|
|
)
|
|
|
|
qr.add_data(data)
|
|
qr.make(fit=True)
|
|
|
|
# Choose module drawer based on style
|
|
module_drawer = None
|
|
if settings['style'] == 'rounded':
|
|
module_drawer = RoundedModuleDrawer()
|
|
elif settings['style'] == 'circle':
|
|
module_drawer = CircleModuleDrawer()
|
|
else:
|
|
module_drawer = SquareModuleDrawer()
|
|
|
|
# Create color mask
|
|
color_mask = SolidFillColorMask(
|
|
back_color=settings['background_color'],
|
|
front_color=settings['foreground_color']
|
|
)
|
|
|
|
# Generate the image
|
|
img = qr.make_image(
|
|
image_factory=StyledPilImage,
|
|
module_drawer=module_drawer,
|
|
color_mask=color_mask
|
|
)
|
|
|
|
return img
|
|
|
|
def add_logo(self, qr_img, logo_path, logo_size_ratio=0.2):
|
|
"""Add logo to QR code"""
|
|
try:
|
|
logo = Image.open(logo_path)
|
|
|
|
# Calculate logo size
|
|
qr_width, qr_height = qr_img.size
|
|
logo_size = int(min(qr_width, qr_height) * logo_size_ratio)
|
|
|
|
# Resize logo
|
|
logo = logo.resize((logo_size, logo_size), Image.Resampling.LANCZOS)
|
|
|
|
# Create a white background for the logo
|
|
logo_bg = Image.new('RGB', (logo_size + 20, logo_size + 20), 'white')
|
|
logo_bg.paste(logo, (10, 10))
|
|
|
|
# Calculate position to center the logo
|
|
logo_pos = (
|
|
(qr_width - logo_bg.width) // 2,
|
|
(qr_height - logo_bg.height) // 2
|
|
)
|
|
|
|
# Paste logo onto QR code
|
|
qr_img.paste(logo_bg, logo_pos)
|
|
|
|
return qr_img
|
|
except Exception as e:
|
|
print(f"Error adding logo: {e}")
|
|
return qr_img
|
|
|
|
# Initialize QR code generator
|
|
qr_generator = QRCodeGenerator()
|
|
|
|
@app.route('/')
|
|
def index():
|
|
"""Serve the main page"""
|
|
return render_template('index.html')
|
|
|
|
@app.route('/api/generate', methods=['POST'])
|
|
def generate_qr():
|
|
"""Generate QR code API endpoint"""
|
|
try:
|
|
data = request.json
|
|
|
|
# Extract QR code content
|
|
qr_type = data.get('type', 'text')
|
|
content = data.get('content', '')
|
|
|
|
# Process content based on type
|
|
if qr_type == 'url':
|
|
qr_content = content if content.startswith(('http://', 'https://')) else f'https://{content}'
|
|
elif qr_type == 'wifi':
|
|
wifi_data = data.get('wifi', {})
|
|
qr_content = f"WIFI:T:{wifi_data.get('security', 'WPA')};S:{wifi_data.get('ssid', '')};P:{wifi_data.get('password', '')};H:{wifi_data.get('hidden', 'false')};;"
|
|
elif qr_type == 'email':
|
|
email_data = data.get('email', {})
|
|
qr_content = f"mailto:{email_data.get('email', '')}?subject={email_data.get('subject', '')}&body={email_data.get('body', '')}"
|
|
elif qr_type == 'phone':
|
|
qr_content = f"tel:{content}"
|
|
elif qr_type == 'sms':
|
|
sms_data = data.get('sms', {})
|
|
qr_content = f"smsto:{sms_data.get('phone', '')}:{sms_data.get('message', '')}"
|
|
elif qr_type == 'vcard':
|
|
vcard_data = data.get('vcard', {})
|
|
qr_content = f"""BEGIN:VCARD
|
|
VERSION:3.0
|
|
FN:{vcard_data.get('name', '')}
|
|
ORG:{vcard_data.get('organization', '')}
|
|
TEL:{vcard_data.get('phone', '')}
|
|
EMAIL:{vcard_data.get('email', '')}
|
|
URL:{vcard_data.get('website', '')}
|
|
END:VCARD"""
|
|
else: # text
|
|
qr_content = content
|
|
|
|
# Extract styling options
|
|
settings = {
|
|
'size': data.get('size', 10),
|
|
'border': data.get('border', 4),
|
|
'foreground_color': data.get('foreground_color', '#000000'),
|
|
'background_color': data.get('background_color', '#FFFFFF'),
|
|
'style': data.get('style', 'square')
|
|
}
|
|
|
|
# Generate QR code
|
|
qr_img = qr_generator.generate_qr_code(qr_content, settings)
|
|
|
|
# Add logo if provided
|
|
logo_path = data.get('logo_path')
|
|
if logo_path and os.path.exists(logo_path):
|
|
qr_img = qr_generator.add_logo(qr_img, logo_path)
|
|
|
|
# Convert to base64
|
|
img_buffer = io.BytesIO()
|
|
qr_img.save(img_buffer, format='PNG')
|
|
img_buffer.seek(0)
|
|
img_base64 = base64.b64encode(img_buffer.getvalue()).decode()
|
|
|
|
# Save QR code record
|
|
qr_id = str(uuid.uuid4())
|
|
qr_record = {
|
|
'id': qr_id,
|
|
'type': qr_type,
|
|
'content': qr_content,
|
|
'settings': settings,
|
|
'created_at': datetime.now().isoformat(),
|
|
'image_data': img_base64
|
|
}
|
|
qr_codes_db[qr_id] = qr_record
|
|
|
|
# Save image file
|
|
img_path = os.path.join(UPLOAD_FOLDER, f'{qr_id}.png')
|
|
qr_img.save(img_path)
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'qr_id': qr_id,
|
|
'image_data': f'data:image/png;base64,{img_base64}',
|
|
'download_url': f'/api/download/{qr_id}'
|
|
})
|
|
|
|
except Exception as e:
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
|
|
@app.route('/api/download/<qr_id>')
|
|
def download_qr(qr_id):
|
|
"""Download QR code"""
|
|
try:
|
|
img_path = os.path.join(UPLOAD_FOLDER, f'{qr_id}.png')
|
|
if os.path.exists(img_path):
|
|
return send_file(img_path, as_attachment=True, download_name=f'qr_code_{qr_id}.png')
|
|
else:
|
|
return jsonify({'error': 'QR code not found'}), 404
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
@app.route('/api/qr_codes')
|
|
def list_qr_codes():
|
|
"""List all generated QR codes"""
|
|
qr_list = []
|
|
for qr_id, qr_data in qr_codes_db.items():
|
|
qr_list.append({
|
|
'id': qr_id,
|
|
'type': qr_data['type'],
|
|
'created_at': qr_data['created_at'],
|
|
'preview': f'data:image/png;base64,{qr_data["image_data"]}'
|
|
})
|
|
return jsonify(qr_list)
|
|
|
|
@app.route('/api/qr_codes/<qr_id>')
|
|
def get_qr_code(qr_id):
|
|
"""Get specific QR code details"""
|
|
if qr_id in qr_codes_db:
|
|
return jsonify(qr_codes_db[qr_id])
|
|
else:
|
|
return jsonify({'error': 'QR code not found'}), 404
|
|
|
|
@app.route('/api/qr_codes/<qr_id>', methods=['DELETE'])
|
|
def delete_qr_code(qr_id):
|
|
"""Delete QR code"""
|
|
try:
|
|
if qr_id in qr_codes_db:
|
|
# Remove from database
|
|
del qr_codes_db[qr_id]
|
|
|
|
# Remove image file
|
|
img_path = os.path.join(UPLOAD_FOLDER, f'{qr_id}.png')
|
|
if os.path.exists(img_path):
|
|
os.remove(img_path)
|
|
|
|
return jsonify({'success': True})
|
|
else:
|
|
return jsonify({'error': 'QR code not found'}), 404
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
@app.route('/api/upload_logo', methods=['POST'])
|
|
def upload_logo():
|
|
"""Upload logo for QR code"""
|
|
try:
|
|
if 'logo' not in request.files:
|
|
return jsonify({'error': 'No logo file provided'}), 400
|
|
|
|
file = request.files['logo']
|
|
if file.filename == '':
|
|
return jsonify({'error': 'No file selected'}), 400
|
|
|
|
# Save logo
|
|
logo_id = str(uuid.uuid4())
|
|
logo_extension = file.filename.rsplit('.', 1)[1].lower()
|
|
logo_filename = f'{logo_id}.{logo_extension}'
|
|
logo_path = os.path.join(LOGOS_FOLDER, logo_filename)
|
|
file.save(logo_path)
|
|
|
|
return jsonify({
|
|
'success': True,
|
|
'logo_path': logo_path,
|
|
'logo_id': logo_id
|
|
})
|
|
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
if __name__ == '__main__':
|
|
app.run(debug=True, host='0.0.0.0', port=5000)
|