import gradio as gr import qrcode import random import os from datetime import datetime from PIL import Image, ImageDraw from math import cos, sin, radians def create_border_decoration(qr_image, decoration_style="Flowers"): # Convert QR image to RGB mode first qr_image = qr_image.convert('RGB') # Get the size of the QR code image width, height = qr_image.size # 패딩을 더 작게 조정 padding = 20 # 패딩을 30에서 20으로 줄임 new_width = width + (padding * 2) new_height = height + (padding * 2) # Create new image with white background decorated_image = Image.new('RGB', (new_width, new_height), 'white') # Paste QR code in center decorated_image.paste(qr_image, (padding, padding)) # Get draw object draw = ImageDraw.Draw(decorated_image) # 장식 크기 설정 - 간격을 더 좁게 deco_size = 8 # 장식 크기를 12에서 8로 줄임 gap = deco_size * 1.5 # 간격을 2배에서 1.5배로 줄임 # 테두리를 따라 점들의 위치 계산 border_points = [] # 상단 테두리 for x in range(padding//2, new_width - padding//2, int(gap)): border_points.append((x, padding//2)) # 우측 테두리 for y in range(padding//2, new_height - padding//2, int(gap)): border_points.append((new_width - padding//2, y)) # 하단 테두리 for x in range(new_width - padding//2, padding//2, -int(gap)): border_points.append((x, new_height - padding//2)) # 좌측 테두리 for y in range(new_height - padding//2, padding//2, -int(gap)): border_points.append((padding//2, y)) # 각 스타일에 따른 장식 그리기 for x, y in border_points: if decoration_style == "Flowers": # 꽃 패턴 # 꽃잎 for angle in range(0, 360, 45): x1 = x + deco_size * cos(radians(angle)) y1 = y + deco_size * sin(radians(angle)) draw.ellipse([x1-4, y1-4, x1+4, y1+4], fill='pink') # 꽃 중심 draw.ellipse([x-3, y-3, x+3, y+3], fill='yellow') elif decoration_style == "Hearts": # 하트 패턴 # 작은 하트들을 연속으로 그리기 heart_size = 6 draw.ellipse([x-heart_size, y-heart_size, x, y], fill='red') draw.ellipse([x, y-heart_size, x+heart_size, y], fill='red') draw.polygon([(x-heart_size, y), (x+heart_size, y), (x, y+heart_size)], fill='red') elif decoration_style == "Waves": # 웨이브 패턴 wave_size = 10 draw.arc([x-wave_size, y-wave_size//2, x+wave_size, y+wave_size//2], 0, 180, fill='lightblue', width=2) draw.arc([x-wave_size, y, x+wave_size, y+wave_size], 0, 180, fill='blue', width=2) elif decoration_style == "Leaves": # 잎 패턴 leaf_size = 8 # 메인 잎 draw.ellipse([x-leaf_size, y-leaf_size//2, x+leaf_size, y+leaf_size//2], fill='lightgreen') # 작은 잎 draw.ellipse([x-leaf_size//2, y-leaf_size, x+leaf_size//2, y+leaf_size], fill='darkgreen') elif decoration_style == "Stars": # 별 패턴 star_size = 6 points = [] for i in range(5): angle = i * 72 # 외곽 점 x1 = x + star_size * cos(radians(angle)) y1 = y + star_size * sin(radians(angle)) points.append((x1, y1)) # 내부 점 x2 = x + (star_size/2) * cos(radians(angle + 36)) y2 = y + (star_size/2) * sin(radians(angle + 36)) points.append((x2, y2)) draw.polygon(points, fill='gold') elif decoration_style == "Chains": # 체인 패턴 chain_size = 8 draw.ellipse([x-chain_size, y-chain_size//2, x+chain_size, y+chain_size//2], outline='gray', width=2) elif decoration_style == "Bubbles": # 버블 패턴 bubble_sizes = [6, 4, 2] for size in bubble_sizes: draw.ellipse([x-size, y-size, x+size, y+size], outline='skyblue', width=1) elif decoration_style == "Vines": # 덩굴 패턴 vine_size = 10 # 메인 덩굴 draw.arc([x-vine_size, y-vine_size, x+vine_size, y+vine_size], 0, 180, fill='green', width=2) # 작은 잎 draw.ellipse([x-3, y-3, x+3, y+3], fill='lightgreen') elif decoration_style == "Diamonds": # 다이아몬드 패턴 diamond_size = 6 points = [ (x, y-diamond_size), # 상단 (x+diamond_size, y), # 우측 (x, y+diamond_size), # 하단 (x-diamond_size, y) # 좌측 ] draw.polygon(points, outline='purple', width=1) elif decoration_style == "Lace": # 레이스 패턴 lace_size = 8 # 레이스 원형 패턴 draw.arc([x-lace_size, y-lace_size, x+lace_size, y+lace_size], 0, 180, fill='gray', width=1) draw.arc([x-lace_size//2, y-lace_size//2, x+lace_size//2, y+lace_size//2], 180, 360, fill='gray', width=1) return decorated_image def rgba_to_rgb(rgba_color): """Convert RGBA color string to RGB hex color""" if rgba_color.startswith('rgba'): # Extract numbers from rgba string values = rgba_color.strip('rgba()').split(',') r = int(float(values[0])) g = int(float(values[1])) b = int(float(values[2])) return f'#{r:02x}{g:02x}{b:02x}' return rgba_color def create_qr(content, qr_type, fill_color, back_color, box_size, border_size, error_correction, border_decoration="No Decoration"): # Convert RGBA colors to RGB fill_color = rgba_to_rgb(fill_color) back_color = rgba_to_rgb(back_color) # QR 코드 데이터 포맷팅 formatted_data = format_data(content, qr_type) # 에러 수정 레벨 설정 error_levels = { "Low (7%)": qrcode.constants.ERROR_CORRECT_L, "Medium (15%)": qrcode.constants.ERROR_CORRECT_M, "Quartile (25%)": qrcode.constants.ERROR_CORRECT_Q, "High (30%)": qrcode.constants.ERROR_CORRECT_H } # QR 코드 생성 qr = qrcode.QRCode( version=1, error_correction=error_levels[error_correction], box_size=box_size, border=border_size, ) qr.add_data(formatted_data) qr.make(fit=True) # QR 이미지 생성 qr_img = qr.make_image(fill_color=fill_color, back_color=back_color) # Add border decoration if specified and not "No Decoration" if border_decoration != "No Decoration": qr_img = create_border_decoration(qr_img, border_decoration) # 파일 저장 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") random_id = random.randint(1000, 9999) filename = f"qrfile/qr_{timestamp}_{random_id}.png" # 디렉토리 확인 및 생성 os.makedirs("qrfile", exist_ok=True) # 이미지 저장 qr_img.save(filename) cleanup_old_files("qrfile/", max_files=100) return filename, formatted_data # 데이터 포맷팅 함수 def format_data(content, qr_type): if not content: return "" format_rules = { "URL": lambda x: f"https://{x}" if not x.startswith(('http://', 'https://')) else x, "Email": lambda x: f"mailto:{x}", "Phone": lambda x: f"tel:{x}", "SMS": lambda x: f"sms:{x}", "WhatsApp": lambda x: f"whatsapp://send?text={x}", "Location": lambda x: f"geo:{x}", "Wi-Fi": lambda x: f"WIFI:S:{x};;", "Text": lambda x: x, "vCard": lambda x: f"BEGIN:VCARD\nVERSION:3.0\n{x}\nEND:VCARD" } return format_rules[qr_type](content.strip()) # 파일 정리 함수 def cleanup_old_files(directory, max_files): files = [f for f in os.listdir(directory) if f.endswith('.png')] if len(files) > max_files: files.sort(key=lambda x: os.path.getctime(os.path.join(directory, x))) for f in files[:-max_files]: try: os.remove(os.path.join(directory, f)) except: continue def format_example_text(qr_type): examples = { "URL": "• Direct URL: https://example.com\n• Without https: example.com", "Email": "• Basic: example@email.com\n• With subject: example@email.com?subject=Hello", "Phone": "• International: +1234567890\n• Local: 01012345678", "SMS": "• Basic: +1234567890\n• With message: +1234567890?body=Hello", "WhatsApp": "• Message: Hello World!\n• With number: +1234567890:Hello", "Location": "• Coordinates: 37.7749,-122.4194\n• With zoom: 37.7749,-122.4194,15z", "Wi-Fi": "• Network name only: MyWiFiNetwork\n• With password: WIFI:S:MyNetwork;P:password;;", "Text": "• Simple text: Hello World!\n• Multiple lines: Line 1\\nLine 2", "vCard": "• Basic:\nFN:John Doe\nTEL:+1234567890\nEMAIL:john@example.com\n• Extended:\nFN:John Doe\nTEL:+1234567890\nEMAIL:john@example.com\nADR:;;123 Street;City;State;12345;Country" } return examples.get(qr_type, "Enter your content here...") def create_interface(): theme = gr.themes.Soft( primary_hue="blue", secondary_hue="indigo", ).set( body_background_fill="*neutral_50", block_background_fill="*neutral_100", button_primary_background_fill="*primary_500", ) with gr.Blocks(theme=theme, title="QR Canvas") as demo: gr.Markdown( """ # 🎯 QR CANVAS+ Create customized QR codes for various purposes with professional styling options. """ ) gr.HTML(""" """) with gr.Row(): with gr.Column(scale=2): qr_type = gr.Dropdown( choices=["URL", "Email", "Phone", "SMS", "WhatsApp", "Location", "Wi-Fi", "Text", "vCard"], value="URL", label="QR Code Type" ) content = gr.Textbox( label="Content", placeholder="Enter your content here...", lines=3 ) example_format = gr.Textbox( value=format_example_text("URL"), label="Format Examples", interactive=False, lines=6 ) with gr.Row(): fill_color = gr.ColorPicker( label="QR Code Color", value="#000000" ) back_color = gr.ColorPicker( label="Background Color", value="#FFFFFF" ) with gr.Row(): box_size = gr.Slider( minimum=1, maximum=30, # 최대값을 20에서 30으로 증가 value=15, # 기본값을 10에서 15로 증가 step=1, label="QR Code Size" ) border_size = gr.Slider( minimum=0, maximum=5, # 최대값을 10에서 5로 감소 value=2, # 기본값을 4에서 2로 감소 step=1, label="Border Size" ) error_correction = gr.Dropdown( choices=[ "Low (7%)", "Medium (15%)", "Quartile (25%)", "High (30%)" ], value="Medium (15%)", label="Error Correction Level" ) border_decoration = gr.Dropdown( choices=[ "No Decoration", # 명시적인 "장식 없음" 옵션 "Flowers", "Hearts", "Waves", "Leaves", "Stars", "Chains", "Bubbles", "Vines", "Diamonds", "Lace" ], value="No Decoration", # 기본값으로 "No Decoration" 설정 label="Border Decoration Style" ) generate_btn = gr.Button( "Generate QR Code", variant="primary" ) with gr.Column(scale=1): output_image = gr.Image( label="Generated QR Code", type="filepath" ) output_data = gr.Textbox( label="Formatted Data", interactive=False ) def update_example(qr_type): return format_example_text(qr_type) qr_type.change( fn=update_example, inputs=[qr_type], outputs=example_format ) generate_btn.click( fn=create_qr, inputs=[ content, qr_type, fill_color, back_color, box_size, border_size, error_correction, border_decoration ], outputs=[output_image, output_data] ) gr.Markdown( """ ### 📝 Instructions 1. Select the QR code type from the dropdown menu 2. Enter your content following the format examples shown 3. Customize the appearance using the color pickers and sliders 4. Click 'Generate QR Code' to create your custom QR code ### 💡 Tips - Use higher error correction levels for better scan reliability - Ensure sufficient contrast between QR code and background colors - Keep the content concise for better readability - Follow the format examples for best results """ ) return demo if __name__ == "__main__": try: os.makedirs("qrfile", exist_ok=True) demo = create_interface() demo.launch( server_name="0.0.0.0", server_port=7860, share=True, debug=True ) except Exception as e: print(f"Error starting the application: {e}")