pdf_to_ppts / app.py
Turbiling's picture
Update app.py
dd990f8 verified
Raw
History Blame Contribute Delete
5.4 kB
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)