Spaces:
Sleeping
Sleeping
File size: 5,398 Bytes
f4395fd e1882fa dd990f8 e1882fa dd990f8 f4395fd dd990f8 f4395fd dd990f8 f4395fd dd990f8 f4395fd dd990f8 e1882fa dd990f8 e1882fa dd990f8 e1882fa dd990f8 e1882fa dd990f8 e1882fa dd990f8 e1882fa dd990f8 f4395fd dd990f8 f4395fd dd990f8 c363a81 dd990f8 e1882fa dd990f8 e1882fa f4395fd dd990f8 e1882fa f4395fd dd990f8 f4395fd dd990f8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | import os
import uuid
import io
import shutil
from PIL import Image
import pytesseract
import fitz # PyMuPDF
import gradio as gr
from pptx import Presentation
from pptx.util import Inches, Pt
from docx import Document
from docx.shared import Cm
# -------------------------
# Configuration
# -------------------------
UPLOAD_DIR = "uploads"
os.makedirs(UPLOAD_DIR, exist_ok=True)
MAX_FILE_SIZE_MB = 500
# Explicit path to tesseract (HF Spaces Linux default)
pytesseract.pytesseract.tesseract_cmd = "/usr/bin/tesseract"
# -------------------------
# OCR Extraction
# -------------------------
def ocr_page_image(image: Image.Image):
"""
Perform OCR on a PIL image and return list of text blocks.
"""
data = pytesseract.image_to_data(image, output_type=pytesseract.Output.DICT)
text_blocks = []
n_boxes = len(data['level'])
for i in range(n_boxes):
text = data['text'][i].strip()
if text:
# Capture bounding box
block = {
'text': text,
'left': data['left'][i],
'top': data['top'][i],
'width': data['width'][i],
'height': data['height'][i]
}
text_blocks.append(block)
return text_blocks
# -------------------------
# PDF โ Word
# -------------------------
def pdf_to_word_ocr(pdf_doc, output_path):
word_doc = Document()
for page in pdf_doc:
pix = page.get_pixmap()
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
blocks = ocr_page_image(img)
page_text = "\n".join([b['text'] for b in blocks])
word_doc.add_paragraph(page_text)
word_doc.add_page_break()
word_doc.save(output_path)
# -------------------------
# PDF โ PowerPoint
# -------------------------
def pdf_to_ppt_ocr(pdf_doc, output_path):
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
for page in pdf_doc:
pix = page.get_pixmap()
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
blocks = ocr_page_image(img)
slide = prs.slides.add_slide(prs.slide_layouts[6])
# Place text boxes for each OCR block
for block in blocks:
left = Inches(block['left'] / 100) # scale roughly
top = Inches(block['top'] / 100)
width = Inches(block['width'] / 100)
height = Inches(block['height'] / 100)
textbox = slide.shapes.add_textbox(left, top, width, height)
tf = textbox.text_frame
p = tf.add_paragraph()
p.text = block['text']
p.font.size = Pt(14)
# Optional: insert full page image as reference below text
img_stream = io.BytesIO()
img.save(img_stream, format="PNG")
img_stream.seek(0)
slide.shapes.add_picture(img_stream, 0, 0, width=prs.slide_width, height=prs.slide_height)
img_stream.close()
prs.save(output_path)
# -------------------------
# Main Conversion
# -------------------------
def convert_pdf(file_path, target_format):
if file_path is None:
raise gr.Error("Please upload a PDF file.")
if not file_path.lower().endswith(".pdf"):
raise gr.Error("Only PDF files are supported.")
unique_id = str(uuid.uuid4())
pdf_path = os.path.join(UPLOAD_DIR, f"{unique_id}.pdf")
shutil.copy(file_path, pdf_path)
pdf_doc = fitz.open(pdf_path)
if len(pdf_doc) == 0:
pdf_doc.close()
os.remove(pdf_path)
raise gr.Error("The uploaded PDF is empty.")
output_file = None
try:
if target_format == "Word Document (.docx)":
output_file = os.path.join(UPLOAD_DIR, f"{unique_id}.docx")
pdf_to_word_ocr(pdf_doc, output_file)
else:
output_file = os.path.join(UPLOAD_DIR, f"{unique_id}.pptx")
pdf_to_ppt_ocr(pdf_doc, output_file)
finally:
pdf_doc.close()
os.remove(pdf_path)
return output_file
# -------------------------
# Clear Uploads
# -------------------------
def clear_files():
for f in os.listdir(UPLOAD_DIR):
try:
os.remove(os.path.join(UPLOAD_DIR, f))
except:
pass
return None
# -------------------------
# Gradio UI
# -------------------------
with gr.Blocks(title="PDF Converter | OCR Editable") as demo:
gr.Markdown("""
# ๐ PDF Converter (OCR for Image-Based PDFs)
Convert scanned or image-based PDFs into **editable Word or PowerPoint** files.
""")
with gr.Row():
pdf_input = gr.File(
label="Upload PDF File",
file_types=[".pdf"]
)
format_choice = gr.Radio(
choices=["Word Document (.docx)", "PowerPoint (.pptx)"],
value="Word Document (.docx)",
label="Select Output Format"
)
with gr.Row():
convert_btn = gr.Button("Convert")
clear_btn = gr.Button("Clear Uploaded Files")
output_file = gr.File(label="Download Converted File")
convert_btn.click(
fn=convert_pdf,
inputs=[pdf_input, format_choice],
outputs=output_file
)
clear_btn.click(
fn=clear_files,
outputs=output_file
)
# -------------------------
# Launch
# -------------------------
demo.launch(server_name="0.0.0.0", server_port=7860) |