Update app.py
Browse files
app.py
CHANGED
|
@@ -2,6 +2,7 @@ import gradio as gr
|
|
| 2 |
import fitz # PyMuPDF kütüphanesi
|
| 3 |
from PIL import Image
|
| 4 |
import os
|
|
|
|
| 5 |
|
| 6 |
# Geçici dosyaları saklamak için bir dizin oluşturalım.
|
| 7 |
# Hugging Face Spaces ortamında bu gereklidir.
|
|
@@ -9,26 +10,25 @@ TEMP_DIR = "temp_files"
|
|
| 9 |
if not os.path.exists(TEMP_DIR):
|
| 10 |
os.makedirs(TEMP_DIR)
|
| 11 |
|
| 12 |
-
def create_preview_image(pdf_path):
|
| 13 |
"""
|
| 14 |
Yüklenen PDF'in ilk 10 sayfasını şeffaf bir şekilde üst üste bindirerek
|
| 15 |
tek bir önizleme görüntüsü oluşturur. Bu, Briss'teki gibi ortak bir
|
| 16 |
kırpma alanı bulmayı kolaylaştırır.
|
| 17 |
"""
|
| 18 |
if not pdf_path:
|
| 19 |
-
return None, None,
|
| 20 |
|
| 21 |
try:
|
| 22 |
doc = fitz.open(pdf_path)
|
| 23 |
if len(doc) == 0:
|
| 24 |
-
|
| 25 |
|
| 26 |
# İlk sayfanın boyutlarını temel alalım.
|
| 27 |
base_page = doc.load_page(0)
|
| 28 |
width, height = int(base_page.rect.width), int(base_page.rect.height)
|
| 29 |
|
| 30 |
# Üst üste bindirilecek sayfalar için boş bir tuval (RGBA formatında).
|
| 31 |
-
# RGBA, şeffaflığı (alpha kanalı) destekler.
|
| 32 |
composite_image = Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
| 33 |
|
| 34 |
# Performans için en fazla ilk 10 sayfayı işleyelim.
|
|
@@ -36,67 +36,49 @@ def create_preview_image(pdf_path):
|
|
| 36 |
|
| 37 |
for i in range(page_count_to_process):
|
| 38 |
page = doc.load_page(i)
|
| 39 |
-
# Sayfayı şeffaf arka planlı bir resme dönüştür.
|
| 40 |
pix = page.get_pixmap(alpha=True)
|
| 41 |
page_image = Image.frombytes("RGBA", [pix.width, pix.height], pix.samples)
|
| 42 |
|
| 43 |
-
# Gerekirse resmi ana tuval boyutuna yeniden boyutlandır.
|
| 44 |
if page_image.size != (width, height):
|
| 45 |
page_image = page_image.resize((width, height), Image.LANCZOS)
|
| 46 |
|
| 47 |
-
|
| 48 |
-
# Her sayfanın altındaki sayfaları da gösterebilmek için bir maske kullanıyoruz.
|
| 49 |
-
alpha_value = int(255 / (i * 0.5 + 2)) # Giderek soluklaşan bir etki için
|
| 50 |
mask = Image.new("L", page_image.size, alpha_value)
|
| 51 |
composite_image.paste(page_image, (0, 0), mask)
|
| 52 |
|
| 53 |
preview_image_path = os.path.join(TEMP_DIR, "preview.png")
|
| 54 |
composite_image.save(preview_image_path)
|
| 55 |
|
| 56 |
-
# Orijinal PDF yolu ve önizleme boyutlarını bir sonraki adım için sakla.
|
| 57 |
-
# Ayrıca kullanıcıya bilgi mesajı göster.
|
| 58 |
return pdf_path, (width, height), preview_image_path
|
| 59 |
except Exception as e:
|
| 60 |
-
|
| 61 |
|
| 62 |
-
def crop_pdf(original_pdf_path, preview_dims, crop_box_coords):
|
| 63 |
"""
|
| 64 |
Kullanıcının seçtiği alana göre PDF'in tüm sayfalarını kırpar ve
|
| 65 |
sonucu yeni bir PDF dosyası olarak döndürür.
|
| 66 |
"""
|
| 67 |
-
if not
|
| 68 |
-
raise gr.Error("
|
|
|
|
|
|
|
| 69 |
|
| 70 |
try:
|
| 71 |
doc = fitz.open(original_pdf_path)
|
| 72 |
-
if len(doc) == 0:
|
| 73 |
-
raise gr.Error("PDF dosyası işlenemiyor.")
|
| 74 |
-
|
| 75 |
-
# Gradio'dan gelen seçili alanın piksel koordinatları.
|
| 76 |
x1, y1, x2, y2 = crop_box_coords
|
| 77 |
-
|
| 78 |
-
# Önizleme resmi ve orijinal PDF sayfası boyutları.
|
| 79 |
preview_width, preview_height = preview_dims
|
| 80 |
pdf_page_rect = doc.load_page(0).rect
|
| 81 |
pdf_width, pdf_height = pdf_page_rect.width, pdf_page_rect.height
|
| 82 |
|
| 83 |
-
# Piksel koordinatlarını PDF'in 'point' birimine ölçekle.
|
| 84 |
scale_x = pdf_width / preview_width
|
| 85 |
scale_y = pdf_height / preview_height
|
| 86 |
|
| 87 |
-
|
| 88 |
-
crop_rect = fitz.Rect(
|
| 89 |
-
x1 * scale_x,
|
| 90 |
-
y1 * scale_y,
|
| 91 |
-
x2 * scale_x,
|
| 92 |
-
y2 * scale_y
|
| 93 |
-
)
|
| 94 |
|
| 95 |
-
# PDF'in her sayfası için kırpma kutusunu ayarla.
|
| 96 |
for page in doc:
|
| 97 |
page.set_cropbox(crop_rect)
|
| 98 |
|
| 99 |
-
# Kırpılmış dosyayı geçici dizine kaydet.
|
| 100 |
output_pdf_path = os.path.join(TEMP_DIR, "cropped_output.pdf")
|
| 101 |
doc.save(output_pdf_path, garbage=4, deflate=True, clean=True)
|
| 102 |
doc.close()
|
|
@@ -113,43 +95,56 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="teal", secondary_hue="orange"))
|
|
| 113 |
**Bu araç, bir PDF'in tüm sayfalarını aynı anda, tek bir seçimle kırpmanızı sağlar.**
|
| 114 |
1. **PDF Yükle:** Kırpmak istediğiniz PDF dosyasını yükleyin. İlk 10 sayfanın üst üste bindirilmiş bir önizlemesi oluşturulacaktır.
|
| 115 |
2. **Alan Seç:** Fare ile sürükleyerek resim üzerinde kalmasını istediğiniz alanı seçin.
|
| 116 |
-
3. **Kırp & İndir:** "PDF'i Kırp ve İndir" düğmesine tıklayın.
|
| 117 |
"""
|
| 118 |
)
|
| 119 |
|
| 120 |
# Arka planda veri tutmak için State'ler (durumlar)
|
| 121 |
original_pdf_path_state = gr.State()
|
| 122 |
preview_dims_state = gr.State()
|
| 123 |
-
|
|
|
|
| 124 |
with gr.Row():
|
| 125 |
with gr.Column(scale=1):
|
| 126 |
pdf_upload_input = gr.File(label="1. PDF Dosyasını Yükle", file_types=[".pdf"])
|
| 127 |
-
crop_button = gr.Button("✂️ PDF'i Kırp ve İndir", variant="primary"
|
| 128 |
pdf_download_output = gr.File(label="Sonuç (İndirmeye Hazır)", interactive=False)
|
| 129 |
|
| 130 |
with gr.Column(scale=3):
|
| 131 |
-
# 'select' aracı ile kullanıcı bir kutu çizebilir.
|
| 132 |
image_preview_output = gr.Image(
|
| 133 |
label="2. Kırpma Alanını Seçin",
|
| 134 |
-
tool="select",
|
| 135 |
interactive=True
|
| 136 |
)
|
| 137 |
-
|
| 138 |
# Event Listeners (Olay Zinciri)
|
| 139 |
|
| 140 |
-
# 1. PDF yüklendiğinde -> create_preview_image fonksiyonunu çalıştır
|
| 141 |
pdf_upload_input.upload(
|
| 142 |
fn=create_preview_image,
|
| 143 |
inputs=[pdf_upload_input],
|
| 144 |
-
outputs=[original_pdf_path_state, preview_dims_state, image_preview_output]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
)
|
| 146 |
|
| 147 |
-
#
|
| 148 |
-
# image_preview_output.select event'inden gelen koordinatları doğrudan kullan.
|
| 149 |
crop_button.click(
|
| 150 |
fn=crop_pdf,
|
| 151 |
-
inputs=[original_pdf_path_state, preview_dims_state,
|
| 152 |
outputs=[pdf_download_output]
|
| 153 |
)
|
| 154 |
|
| 155 |
-
app.launch(
|
|
|
|
| 2 |
import fitz # PyMuPDF kütüphanesi
|
| 3 |
from PIL import Image
|
| 4 |
import os
|
| 5 |
+
import typing
|
| 6 |
|
| 7 |
# Geçici dosyaları saklamak için bir dizin oluşturalım.
|
| 8 |
# Hugging Face Spaces ortamında bu gereklidir.
|
|
|
|
| 10 |
if not os.path.exists(TEMP_DIR):
|
| 11 |
os.makedirs(TEMP_DIR)
|
| 12 |
|
| 13 |
+
def create_preview_image(pdf_path: str) -> tuple[typing.Optional[str], typing.Optional[tuple], typing.Optional[str]]:
|
| 14 |
"""
|
| 15 |
Yüklenen PDF'in ilk 10 sayfasını şeffaf bir şekilde üst üste bindirerek
|
| 16 |
tek bir önizleme görüntüsü oluşturur. Bu, Briss'teki gibi ortak bir
|
| 17 |
kırpma alanı bulmayı kolaylaştırır.
|
| 18 |
"""
|
| 19 |
if not pdf_path:
|
| 20 |
+
return None, None, None
|
| 21 |
|
| 22 |
try:
|
| 23 |
doc = fitz.open(pdf_path)
|
| 24 |
if len(doc) == 0:
|
| 25 |
+
raise gr.Error("Yüklenen PDF dosyası boş veya geçersiz.")
|
| 26 |
|
| 27 |
# İlk sayfanın boyutlarını temel alalım.
|
| 28 |
base_page = doc.load_page(0)
|
| 29 |
width, height = int(base_page.rect.width), int(base_page.rect.height)
|
| 30 |
|
| 31 |
# Üst üste bindirilecek sayfalar için boş bir tuval (RGBA formatında).
|
|
|
|
| 32 |
composite_image = Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
| 33 |
|
| 34 |
# Performans için en fazla ilk 10 sayfayı işleyelim.
|
|
|
|
| 36 |
|
| 37 |
for i in range(page_count_to_process):
|
| 38 |
page = doc.load_page(i)
|
|
|
|
| 39 |
pix = page.get_pixmap(alpha=True)
|
| 40 |
page_image = Image.frombytes("RGBA", [pix.width, pix.height], pix.samples)
|
| 41 |
|
|
|
|
| 42 |
if page_image.size != (width, height):
|
| 43 |
page_image = page_image.resize((width, height), Image.LANCZOS)
|
| 44 |
|
| 45 |
+
alpha_value = int(255 / (i * 0.5 + 2))
|
|
|
|
|
|
|
| 46 |
mask = Image.new("L", page_image.size, alpha_value)
|
| 47 |
composite_image.paste(page_image, (0, 0), mask)
|
| 48 |
|
| 49 |
preview_image_path = os.path.join(TEMP_DIR, "preview.png")
|
| 50 |
composite_image.save(preview_image_path)
|
| 51 |
|
|
|
|
|
|
|
| 52 |
return pdf_path, (width, height), preview_image_path
|
| 53 |
except Exception as e:
|
| 54 |
+
raise gr.Error(f"Önizleme oluşturulurken bir hata oluştu: {str(e)}")
|
| 55 |
|
| 56 |
+
def crop_pdf(original_pdf_path: str, preview_dims: tuple, crop_box_coords: list) -> typing.Optional[str]:
|
| 57 |
"""
|
| 58 |
Kullanıcının seçtiği alana göre PDF'in tüm sayfalarını kırpar ve
|
| 59 |
sonucu yeni bir PDF dosyası olarak döndürür.
|
| 60 |
"""
|
| 61 |
+
if not original_pdf_path:
|
| 62 |
+
raise gr.Error("Lütfen önce bir PDF dosyası yükleyin.")
|
| 63 |
+
if not crop_box_coords:
|
| 64 |
+
raise gr.Error("Lütfen resim üzerinde bir kırpma alanı seçin.")
|
| 65 |
|
| 66 |
try:
|
| 67 |
doc = fitz.open(original_pdf_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
x1, y1, x2, y2 = crop_box_coords
|
| 69 |
+
|
|
|
|
| 70 |
preview_width, preview_height = preview_dims
|
| 71 |
pdf_page_rect = doc.load_page(0).rect
|
| 72 |
pdf_width, pdf_height = pdf_page_rect.width, pdf_page_rect.height
|
| 73 |
|
|
|
|
| 74 |
scale_x = pdf_width / preview_width
|
| 75 |
scale_y = pdf_height / preview_height
|
| 76 |
|
| 77 |
+
crop_rect = fitz.Rect(x1 * scale_x, y1 * scale_y, x2 * scale_x, y2 * scale_y)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
|
|
|
|
| 79 |
for page in doc:
|
| 80 |
page.set_cropbox(crop_rect)
|
| 81 |
|
|
|
|
| 82 |
output_pdf_path = os.path.join(TEMP_DIR, "cropped_output.pdf")
|
| 83 |
doc.save(output_pdf_path, garbage=4, deflate=True, clean=True)
|
| 84 |
doc.close()
|
|
|
|
| 95 |
**Bu araç, bir PDF'in tüm sayfalarını aynı anda, tek bir seçimle kırpmanızı sağlar.**
|
| 96 |
1. **PDF Yükle:** Kırpmak istediğiniz PDF dosyasını yükleyin. İlk 10 sayfanın üst üste bindirilmiş bir önizlemesi oluşturulacaktır.
|
| 97 |
2. **Alan Seç:** Fare ile sürükleyerek resim üzerinde kalmasını istediğiniz alanı seçin.
|
| 98 |
+
3. **Kırp & İndir:** "PDF'i Kırp ve İndir" düğmesine tıklayın.
|
| 99 |
"""
|
| 100 |
)
|
| 101 |
|
| 102 |
# Arka planda veri tutmak için State'ler (durumlar)
|
| 103 |
original_pdf_path_state = gr.State()
|
| 104 |
preview_dims_state = gr.State()
|
| 105 |
+
crop_coords_state = gr.State() # Seçim koordinatlarını tutmak için yeni state
|
| 106 |
+
|
| 107 |
with gr.Row():
|
| 108 |
with gr.Column(scale=1):
|
| 109 |
pdf_upload_input = gr.File(label="1. PDF Dosyasını Yükle", file_types=[".pdf"])
|
| 110 |
+
crop_button = gr.Button("✂️ PDF'i Kırp ve İndir", variant="primary")
|
| 111 |
pdf_download_output = gr.File(label="Sonuç (İndirmeye Hazır)", interactive=False)
|
| 112 |
|
| 113 |
with gr.Column(scale=3):
|
|
|
|
| 114 |
image_preview_output = gr.Image(
|
| 115 |
label="2. Kırpma Alanını Seçin",
|
| 116 |
+
tool="select", # Güncel Gradio sürümü ile bu parametre çalışacaktır
|
| 117 |
interactive=True
|
| 118 |
)
|
| 119 |
+
|
| 120 |
# Event Listeners (Olay Zinciri)
|
| 121 |
|
| 122 |
+
# 1. PDF yüklendiğinde -> create_preview_image fonksiyonunu çalıştır
|
| 123 |
pdf_upload_input.upload(
|
| 124 |
fn=create_preview_image,
|
| 125 |
inputs=[pdf_upload_input],
|
| 126 |
+
outputs=[original_pdf_path_state, preview_dims_state, image_preview_output],
|
| 127 |
+
# Önceki seçimleri ve sonuçları temizle
|
| 128 |
+
trigger_mode="once"
|
| 129 |
+
).then(
|
| 130 |
+
lambda: (None, None),
|
| 131 |
+
outputs=[crop_coords_state, pdf_download_output]
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
# 2. Resim üzerinde bir alan seçildiğinde -> koordinatları crop_coords_state'e kaydet
|
| 135 |
+
def store_coords(evt: gr.SelectData):
|
| 136 |
+
return evt.index
|
| 137 |
+
|
| 138 |
+
image_preview_output.select(
|
| 139 |
+
fn=store_coords,
|
| 140 |
+
outputs=[crop_coords_state]
|
| 141 |
)
|
| 142 |
|
| 143 |
+
# 3. Kırpma butonuna tıklandığında -> crop_pdf fonksiyonunu çalıştır
|
|
|
|
| 144 |
crop_button.click(
|
| 145 |
fn=crop_pdf,
|
| 146 |
+
inputs=[original_pdf_path_state, preview_dims_state, crop_coords_state],
|
| 147 |
outputs=[pdf_download_output]
|
| 148 |
)
|
| 149 |
|
| 150 |
+
app.launch()
|