Spaces:
Sleeping
Sleeping
| # Gradio ๋ฐ Pillow ์ค์น (์ด๋ฏธ ์คํํ๋ค๋ฉด ์๋ต ๊ฐ๋ฅ) | |
| # !pip install gradio pillow -q | |
| import gradio as gr | |
| from PIL import Image | |
| import os | |
| import re | |
| import io | |
| import tempfile | |
| # --- ์ด์ ์ฝ๋์์ ๊ฐ์ ธ์จ ํฌํผ ํจ์๋ค (๋ณ๊ฒฝ ์์) --- | |
| def natural_sort_key(s): | |
| name_without_extension = os.path.splitext(s)[0] | |
| return [int(text) if text.isdigit() else text.lower() | |
| for text in re.split(r'(\d+)', name_without_extension)] | |
| def resize_and_pad(img, target_size, background_color=(255, 255, 255)): | |
| original_width, original_height = img.size | |
| target_width, target_height = target_size | |
| img_copy = img.copy() | |
| try: | |
| img_copy.thumbnail((target_width, target_height), Image.Resampling.LANCZOS) | |
| except AttributeError: | |
| img_copy.thumbnail((target_width, target_height), Image.ANTIALIAS) | |
| new_img = Image.new("RGB", target_size, background_color) | |
| paste_x = (target_width - img_copy.width) // 2 | |
| paste_y = (target_height - img_copy.height) // 2 | |
| new_img.paste(img_copy, (paste_x, paste_y)) | |
| return new_img | |
| # --- Gradio ์ธํฐํ์ด์ค๋ฅผ ์ํ ํต์ฌ ํจ์ (๋ณ๊ฒฝ ์์) --- | |
| def images_to_pdf_gradio(uploaded_files, resize_option, output_filename="converted_images.pdf"): | |
| status_messages = [] | |
| if not uploaded_files: | |
| return None, "์ค๋ฅ: ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํด์ฃผ์ธ์." | |
| file_info_list = [] | |
| for file_obj in uploaded_files: | |
| temp_path = "" | |
| original_name = "" | |
| if hasattr(file_obj, 'name') and isinstance(file_obj.name, str): | |
| temp_path = file_obj.name | |
| if hasattr(file_obj, 'orig_name') and isinstance(file_obj.orig_name, str): | |
| original_name = file_obj.orig_name | |
| else: | |
| original_name = os.path.basename(temp_path) | |
| elif isinstance(file_obj, str): | |
| temp_path = file_obj | |
| original_name = os.path.basename(temp_path) | |
| else: | |
| status_messages.append(f"โ ๏ธ ๊ฒฝ๊ณ : ์ ์ ์๋ ํ์ผ ๊ฐ์ฒด ํ์ : {type(file_obj)}. ์ด ํ์ผ์ ๊ฑด๋๋๋๋ค.") | |
| continue | |
| file_info_list.append({'path': temp_path, 'orig_name': original_name}) | |
| if not file_info_list: | |
| final_status = "\n".join(status_messages) + "\n์ค๋ฅ: ์ ํจํ ํ์ผ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค." | |
| return None, final_status | |
| try: | |
| sorted_files_info = sorted(file_info_list, key=lambda f_info: natural_sort_key(f_info['orig_name'])) | |
| except Exception as e: | |
| final_status = "\n".join(status_messages) + f"\nํ์ผ ์ ๋ ฌ ์ค ์ค๋ฅ: {e}." | |
| return None, final_status | |
| pil_images = [] | |
| target_size = None | |
| processed_count = 0 | |
| if resize_option == "์ฒซ ๋ฒ์งธ ์ด๋ฏธ์ง์ ๋ง์ถค" and sorted_files_info: | |
| try: | |
| first_file_info = sorted_files_info[0] | |
| with Image.open(first_file_info['path']) as img_for_size_orig: | |
| img_for_size = img_for_size_orig.copy() | |
| if img_for_size.mode == 'RGBA' or img_for_size.mode == 'P': | |
| img_for_size = img_for_size.convert('RGB') | |
| target_size = img_for_size.size | |
| status_messages.append(f"๊ธฐ์ค ์ด๋ฏธ์ง ํฌ๊ธฐ: {target_size} (์ฒซ ๋ฒ์งธ ์ด๋ฏธ์ง '{first_file_info['orig_name']}' ๊ธฐ์ค)") | |
| except Exception as e: | |
| status_messages.append(f"โ ๏ธ ๊ฒฝ๊ณ : ์ฒซ ๋ฒ์งธ ์ด๋ฏธ์ง ํฌ๊ธฐ ์ค์ ์ค ์ค๋ฅ - {e}. ์๋ณธ ํฌ๊ธฐ๋ก ์งํํฉ๋๋ค.") | |
| resize_option = "์๋ณธ ํฌ๊ธฐ ์ ์ง" | |
| for file_info in sorted_files_info: | |
| original_filename = file_info['orig_name'] | |
| temp_filepath = file_info['path'] | |
| try: | |
| with Image.open(temp_filepath) as img_orig: | |
| img = img_orig.copy() | |
| if img.mode == 'RGBA' or img.mode == 'P': | |
| img = img.convert('RGB') | |
| if resize_option == "์ฒซ ๋ฒ์งธ ์ด๋ฏธ์ง์ ๋ง์ถค" and target_size: | |
| img = resize_and_pad(img, target_size) | |
| pil_images.append(img) | |
| status_messages.append(f"โ ์ฒ๋ฆฌ ์๋ฃ: {original_filename}") | |
| processed_count += 1 | |
| except Exception as e: | |
| status_messages.append(f"โ ์ค๋ฅ: '{original_filename}' ์ฒ๋ฆฌ ์ค ๋ฌธ์ ๋ฐ์ - {e}") | |
| if not pil_images: | |
| final_status = "\n".join(status_messages) + "\n\n์ค๋ฅ: PDF๋ก ๋ณํํ ์ ํจํ ์ด๋ฏธ์ง๊ฐ ์์ต๋๋ค." | |
| return None, final_status | |
| pdf_buffer = io.BytesIO() | |
| try: | |
| pil_images[0].save( | |
| pdf_buffer, | |
| format="PDF", | |
| save_all=True, | |
| append_images=pil_images[1:], | |
| resolution=100.0 | |
| ) | |
| pdf_buffer.seek(0) | |
| file_prefix = os.path.splitext(output_filename)[0] | |
| with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf", prefix=file_prefix + "_") as tmp_pdf: | |
| tmp_pdf.write(pdf_buffer.getvalue()) | |
| tmp_pdf_path = tmp_pdf.name | |
| final_status = "\n".join(status_messages) + f"\n\n๐ ์ฑ๊ณต: ์ด {processed_count}๊ฐ์ ์ด๋ฏธ์ง๋ก PDF ์์ฑ ์๋ฃ! ์๋ ๋ฒํผ์ผ๋ก ๋ค์ด๋ก๋ํ์ธ์." | |
| return tmp_pdf_path, final_status | |
| except Exception as e: | |
| final_status = "\n".join(status_messages) + f"\n\nโ ์ค๋ฅ: PDF ํ์ผ ์ ์ฅ ์ค ๋ฌธ์ ๋ฐ์ - {e}" | |
| return None, final_status | |
| # --- Gradio ์ธํฐํ์ด์ค ์ ์ (DownloadButton ์ด๊ธฐ ์ํ ๋ณ๊ฒฝ) --- | |
| with gr.Blocks(theme=gr.themes.Default()) as demo: | |
| gr.Markdown( | |
| """ | |
| # ๐ผ๏ธ ์ด๋ฏธ์ง(PNG, JPG)๋ฅผ PDF๋ก ๋ณํ ๐ | |
| ์ฌ๋ฌ ์ด๋ฏธ์ง ํ์ผ์ ์ ๋ก๋ํ๊ณ , ํ์์ ๋ฐ๋ผ ํฌ๊ธฐ ์กฐ์ ์ต์ ์ ์ ํํ ํ 'PDF ์์ฑ' ๋ฒํผ์ ๋๋ฅด์ธ์. | |
| ํ์ผ์ ์ด๋ฆ์ (์: 1.jpg, 2.png, 10.jpg)์ผ๋ก ์ ๋ ฌ๋ฉ๋๋ค. | |
| """ | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| image_files_input = gr.File( | |
| label="1. ์ด๋ฏธ์ง ํ์ผ ์ ๋ก๋ (๋ค์ค ์ ํ ๊ฐ๋ฅ)", | |
| file_count="multiple", | |
| file_types=["image"], | |
| ) | |
| resize_option_input = gr.Radio( | |
| label="2. ์ด๋ฏธ์ง ํฌ๊ธฐ ์กฐ์ ์ต์ ", | |
| choices=["์ฒซ ๋ฒ์งธ ์ด๋ฏธ์ง์ ๋ง์ถค", "์๋ณธ ํฌ๊ธฐ ์ ์ง"], | |
| value="์ฒซ ๋ฒ์งธ ์ด๋ฏธ์ง์ ๋ง์ถค", | |
| ) | |
| output_pdf_name_input = gr.Textbox( | |
| label="3. ์์ฑ๋ PDF ํ์ผ ์ด๋ฆ (ํ์ฅ์ ํฌํจ)", | |
| value="converted_images.pdf", | |
| placeholder="์: my_album.pdf" | |
| ) | |
| submit_button = gr.Button("๐ PDF ์์ฑ ์คํ", variant="primary", elem_id="generate-button") | |
| pdf_download_button = gr.DownloadButton( | |
| "โฌ๏ธ ๋ณํฉ๋ PDF ๋ค์ด๋ก๋ (์์ฑ ํ ํ์ฑํ)", # ์ด๊ธฐ ๋ ์ด๋ธ ๋ณ๊ฒฝ | |
| value=None, | |
| variant="secondary", | |
| visible=True, # ์ฒ์๋ถํฐ ๋ณด์ด๋๋ก ๋ณ๊ฒฝ | |
| interactive=False # ์ฒ์์๋ ๋นํ์ฑํ | |
| # elem_id="download-button-custom-style" | |
| ) | |
| with gr.Column(scale=3): | |
| status_message_output = gr.Textbox( | |
| label="๐ ์ฒ๋ฆฌ ์ํ ๋ฐ ๊ฒฐ๊ณผ ๋ฉ์์ง", | |
| lines=10, | |
| interactive=False, | |
| show_copy_button=True | |
| ) | |
| def process_and_update_ui(uploaded_files, resize_option, output_filename): | |
| actual_pdf_file_path, status_msg = images_to_pdf_gradio(uploaded_files, resize_option, output_filename) | |
| download_button_update_dict = {} | |
| if actual_pdf_file_path: | |
| download_button_update_dict = { | |
| "value": actual_pdf_file_path, | |
| "label": "โฌ๏ธ ๋ณํฉ๋ PDF ๋ค์ด๋ก๋", | |
| "interactive": True # ํ์ฑํ | |
| } | |
| else: | |
| download_button_update_dict = { | |
| "value": None, | |
| "label": "โฌ๏ธ PDF ๋ค์ด๋ก๋ (์์ฑ ์คํจ ๋๋ ํ์ผ ์์)", | |
| "interactive": False # ๋นํ์ฑํ ์ ์ง | |
| } | |
| return status_msg, gr.update(**download_button_update_dict) | |
| submit_button.click( | |
| fn=process_and_update_ui, | |
| inputs=[image_files_input, resize_option_input, output_pdf_name_input], | |
| outputs=[status_message_output, pdf_download_button] | |
| ) | |
| # CSS ์ฃผ์์ ์ด์ ๊ณผ ๋์ผํ๊ฒ ์ ์ง (์ ํ ์ฌํญ) | |
| # demo.css = """ ... """ | |
| gr.Markdown( | |
| """ | |
| --- | |
| **๐ก ์ฌ์ฉ ํ:** | |
| - ํ์ผ ์ด๋ฆ์ ์ซ์๊ฐ ํฌํจ๋ ๊ฒฝ์ฐ ์์ฐ ์ ๋ ฌ๋ฉ๋๋ค (์: `image1.jpg`, `image2.jpg`, `image10.jpg`). | |
| - '์ฒซ ๋ฒ์งธ ์ด๋ฏธ์ง์ ๋ง์ถค' ์ต์ ์ ํ ์, ๋ชจ๋ ์ด๋ฏธ์ง๊ฐ ์ฒซ ๋ฒ์งธ ์ด๋ฏธ์ง์ ๊ฐ๋ก/์ธ๋ก ํฌ๊ธฐ์ ๋ง์ถฐ์ง๋๋ค. ๋น์จ์ ์ ์ง๋๋ฉฐ, ๋จ๋ ๊ณต๊ฐ์ ํฐ์์ผ๋ก ์ฑ์์ง๋๋ค. | |
| - ๋์ฉ๋ ํ์ผ์ด๋ ๋ง์ ์์ ํ์ผ์ ํ ๋ฒ์ ์ฒ๋ฆฌํ๋ฉด ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆด ์ ์์ต๋๋ค. | |
| """ | |
| ) | |
| demo.launch(debug=True) |