MuhammadAdnanMalik/PDF_to_Word_File_Converter
0
1import io2import os3import tempfile4import fitz # PyMuPDF5import pdfplumber6from docx import Document7from docx.shared import Pt, Inches8from PIL import Image9import gradio as gr10 11# Utility: clean filename12def _clean_filename(name: str) -> str:13 return "".join(c for c in name if c.isalnum() or c in (' ', '.', '_', '-')).rstrip()14 15 16def extract_with_pymupdf(pdf_bytes: bytes):17 doc = fitz.open(stream=pdf_bytes, filetype="pdf")18 pages_out = []19 for page in doc:20 page_dict = {"spans": [], "images": []}21 blocks = page.get_text("dict").get("blocks", [])22 for b in blocks:23 if b.get("type") == 0:24 for line in b.get("lines", []):25 for span in line.get("spans", []):26 page_dict["spans"].append({27 "text": span.get("text", ""),28 "font": span.get("font", ""),29 "size": span.get("size", 11),30 "bbox": span.get("bbox", None),31 })32 try:33 for imginfo in page.get_images(full=True):34 xref = imginfo[0]35 base_image = doc.extract_image(xref)36 img_bytes = base_image["image"]37 ext = base_image.get("ext", "png")38 page_dict["images"].append({"image_bytes": img_bytes, "bbox": None, "ext": ext})39 except Exception:40 pass41 pages_out.append(page_dict)42 doc.close()43 return pages_out44 45 46def extract_tables_with_pdfplumber(pdf_bytes: bytes):47 tables_by_page = {}48 with pdfplumber.open(io.BytesIO(pdf_bytes)) as pdf:49 for i, page in enumerate(pdf.pages):50 try:51 table = page.extract_table()52 if table:53 tables_by_page[i] = table54 except Exception:55 continue56 return tables_by_page57 58 59def pdf_to_docx(pdf_bytes: bytes, original_filename: str = "converted.pdf"):60 pages = extract_with_pymupdf(pdf_bytes)61 tables = extract_tables_with_pdfplumber(pdf_bytes)62 63 doc = Document()64 65 for p_idx, page in enumerate(pages):66 if p_idx > 0:67 doc.add_page_break()68 69 if p_idx in tables:70 tbl_data = tables[p_idx]71 rows = len(tbl_data)72 cols = max(len(r) for r in tbl_data)73 table = doc.add_table(rows=rows, cols=cols)74 table.style = "Table Grid"75 for r_i, row in enumerate(tbl_data):76 for c_i, cell in enumerate(row):77 table.rows[r_i].cells[c_i].text = cell if cell is not None else ""78 79 para = None80 for span in page.get("spans", []):81 txt = span.get("text", "")82 if not txt:83 continue84 if para is None:85 para = doc.add_paragraph()86 run = para.add_run(txt)87 try:88 if span.get("font"):89 run.font.name = span.get("font")90 if span.get("size"):91 run.font.size = Pt(float(span.get("size")))92 except Exception:93 pass94 95 for img in page.get("images", []):96 try:97 image_bytes = img.get("image_bytes")98 ext = img.get("ext", "png")99 with tempfile.NamedTemporaryFile(delete=False, suffix=f".{ext}") as tf:100 tf.write(image_bytes)101 tmpname = tf.name102 try:103 doc.add_picture(tmpname, width=Inches(6))104 except Exception:105 doc.add_picture(tmpname)106 finally:107 try:108 os.unlink(tmpname)109 except Exception:110 pass111 except Exception:112 continue113 114 out_stream = io.BytesIO()115 safe_name = _clean_filename(os.path.splitext(original_filename)[0])116 out_filename = f"{safe_name}.docx"117 doc.save(out_stream)118 out_stream.seek(0)119 return out_filename, out_stream120 121 122def process_pdf(uploaded_file_path):123 if uploaded_file_path is None:124 return None125 # ✅ Open the file path instead of using .read()126 with open(uploaded_file_path, "rb") as f:127 file_bytes = f.read()128 base_name = os.path.basename(uploaded_file_path)129 out_name, out_stream = pdf_to_docx(file_bytes, original_filename=base_name)130 return (out_name, out_stream.getvalue())131 132 133with gr.Blocks(title="PDF → Word Converter") as demo:134 with gr.Row():135 gr.Markdown("# PDF → Word Converter\nUpload a PDF and get an editable .docx file.")136 with gr.Tabs():137 with gr.TabItem("Converter"):138 with gr.Column():139 pdf_in = gr.File(label="Upload PDF", file_count="single", file_types=[".pdf"]) 140 convert_btn = gr.Button("Convert to Word")141 output_file = gr.File(label="Download .docx")142 status = gr.Textbox(label="Status", interactive=False)143 144 def on_convert(f):145 if f is None:146 return None, "No file uploaded"147 try:148 name, data = process_pdf(f)149 tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".docx")150 tmp.write(data)151 tmp.flush()152 tmp.close()153 return tmp.name, "Conversion successful"154 except Exception as e:155 return None, f"Conversion failed: {e}"156 157 convert_btn.click(on_convert, inputs=[pdf_in], outputs=[output_file, status])158 159 with gr.TabItem("News — Announcement"):160 try:161 with open("news1.md", "r", encoding="utf-8") as f:162 news_md = f.read()163 except Exception:164 news_md = "No news file (news1.md) found."165 gr.Markdown(news_md)166 167 with gr.TabItem("News — Release Notes"):168 try:169 with open("news2.md", "r", encoding="utf-8") as f:170 news_md2 = f.read()171 except Exception:172 news_md2 = "No news file (news2.md) found."173 gr.Markdown(news_md2)174 175 demo.launch()176 