import tkinter as tk
from tkinter import filedialog, messagebox
import os
import shutil
import easyocr
from docx import Document
from PIL import Image, ImageFilter
import openai
import datetime
import sys
# Set expiration date
EXPIRATION_DATE = datetime.datetime(2025, 12, 31) # YYYY, MM, DD
CURRENT_DATE = datetime.datetime.now()
# Check if the program has expired
if CURRENT_DATE >= EXPIRATION_DATE:
messagebox.showerror("Program Expired", "This program has expired and is no
longer operational.")
sys.exit() # Stop execution
def enhance_image(image_path):
""" Basic image enhancement using Pillow's SHARPEN filter. """
image = Image.open(image_path).convert("RGB")
sharpened = image.filter(ImageFilter.SHARPEN)
return sharpened
def extract_text(image_path):
""" Extract text from an image using EasyOCR. """
reader = easyocr.Reader(['en'], gpu=True, recog_network='latin_g2')
text_list = reader.readtext(image_path, detail=0)
return "\n".join(text_list)
def format_text_with_gpt(raw_text, api_key):
""" Use GPT (via OpenAI API) to format extracted text, if API key is provided.
"""
if not api_key:
return raw_text
try:
openai.api_key = api_key
system_prompt = (
"You are a helpful assistant that formats raw OCR text into a cleaner,
"
"more human-readable style. Preserve essential content, but fix
spacing, "
"punctuation, and line breaks as appropriate."
)
user_prompt = f"Please format the following text:\n\n{raw_text}\n\n"
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
temperature=0.2,
max_tokens=1000
)
formatted_text = response["choices"][0]["message"]["content"].strip()
return formatted_text
except Exception as e:
messagebox.showwarning("GPT Formatting Warning", f"GPT formatting failed:\
n{str(e)}")
return raw_text
def save_to_docx(text, output_path):
""" Save extracted text into a DOCX file using python-docx. """
doc = Document()
doc.add_paragraph(text)
doc.save(output_path)
def convert_single_image(image_path, api_key=None):
"""
Convert a single image file to .docx:
1) Enhance -> Save to "enhanced"
2) OCR
3) Optional GPT formatting
4) Save DOCX to "converted"
5) Move original image to "finished"
Returns a status string for reporting.
"""
try:
if not os.path.isfile(image_path):
return f"ERROR: File not found: {image_path}"
# Base directory of the image
directory = os.path.dirname(image_path)
# Create necessary folders
enhanced_folder = os.path.join(directory, "enhanced")
converted_folder = os.path.join(directory, "converted")
finished_folder = os.path.join(directory, "finished")
for folder in [enhanced_folder, converted_folder, finished_folder]:
if not os.path.exists(folder):
os.makedirs(folder)
# Extract base filename without extension
filename = os.path.basename(image_path)
name, ext = os.path.splitext(filename)
# 1) Enhance the image
enhanced = enhance_image(image_path)
enhanced_path = os.path.join(enhanced_folder, f"{name}_enhanced.jpg")
enhanced.save(enhanced_path)
# 2) Extract OCR text from the enhanced image
raw_text = extract_text(enhanced_path)
# 3) Format text using GPT if API key is provided
final_text = format_text_with_gpt(raw_text, api_key)
# 4) Save the formatted text to a DOCX file in the converted folder
output_path = os.path.join(converted_folder, f"{name}.docx")
save_to_docx(final_text, output_path)
# 5) Move the original image to the "finished" folder
finished_path = os.path.join(finished_folder, filename)
shutil.move(image_path, finished_path)
return f"✓ Success: {filename} -> Enhanced:
{os.path.basename(enhanced_path)}, DOCX: {os.path.basename(output_path)}, Moved to
'finished'."
except Exception as e:
return f"✗ Error with {os.path.basename(image_path)}: {str(e)}"
def convert_multiple_images(file_list, api_key=None):
""" Convert multiple selected images one by one and show results. """
results = []
for f in file_list:
status = convert_single_image(f, api_key)
results.append(status)
final_report = "\n".join(results)
messagebox.showinfo("Conversion Results", final_report)
# --------------------- GUI ----------------------
def browse_files():
""" Allow user to select multiple image files. """
filepaths = filedialog.askopenfilenames(
title="Select One or More Document Images",
filetypes=[("Image Files", "*.jpg *.jpeg *.png *.bmp *.tiff *.webp"), ("All
Files", "*.*")]
)
if filepaths:
text_files.delete("1.0", tk.END)
for fp in filepaths:
text_files.insert(tk.END, fp + "\n")
def convert_action():
""" Collect file paths and optional API key, then start conversion. """
content = text_files.get("1.0", tk.END).strip()
if not content:
messagebox.showerror("Error", "No files selected.")
return
# Split lines into a list of file paths
file_list = [line.strip() for line in content.splitlines() if line.strip()]
if not file_list:
messagebox.showerror("Error", "No valid file paths found.")
return
api_key = entry_api.get().strip()
convert_multiple_images(file_list, api_key)
# MAIN WINDOW
root = tk.Tk()
root.title("Batch Image-to-DOCX Converter")
root.geometry("700x400")
root.resizable(False, False)
# Label for instructions
lbl_info = tk.Label(root, text="Select your image files (multiple):",
font=("Arial", 12))
lbl_info.pack(pady=(10, 5))
# "Browse" button
btn_browse = tk.Button(root, text="Browse Files", command=browse_files,
font=("Arial", 10))
btn_browse.pack()
# A text widget to display chosen file paths
frame_files = tk.Frame(root)
frame_files.pack(padx=10, pady=10, fill="both", expand=True)
scroll_y = tk.Scrollbar(frame_files, orient=tk.VERTICAL)
text_files = tk.Text(frame_files, wrap="none", height=8, width=80,
yscrollcommand=scroll_y.set)
scroll_y.config(command=text_files.yview)
scroll_y.pack(side=tk.RIGHT, fill=tk.Y)
text_files.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
lbl_api = tk.Label(root, text="OpenAI API Key (optional):", font=("Arial", 12))
lbl_api.pack(pady=(10, 5))
entry_api = tk.Entry(root, width=60, font=("Arial", 10), show='*')
entry_api.pack()
btn_convert = tk.Button(root, text="Convert All to DOCX", command=convert_action,
font=("Arial", 12), bg="navy", fg="white")
btn_convert.pack(pady=20)
root.mainloop()