| import gradio as gr |
| import unicodeconverter as uc |
| from weasyprint import HTML |
| import os |
| import tempfile |
| import shutil |
| import re |
| from groq import Groq |
| from docx import Document |
| from docx.shared import Pt, RGBColor |
| from docx.enum.text import WD_ALIGN_PARAGRAPH |
|
|
| |
| DEFAULT_FONT_PATH = os.path.abspath("SolaimanLipi_22-02-2012.ttf") |
| HAS_DEFAULT_FONT = os.path.exists(DEFAULT_FONT_PATH) |
|
|
|
|
| def detect_and_preserve_english(text, groq_api_key): |
| """ |
| Use Groq AI to identify English segments in mixed Bijoy-English text |
| Returns a list of tuples: (text_segment, is_english) |
| """ |
| try: |
| client = Groq(api_key=groq_api_key) |
| |
| prompt = f"""You are a text analyzer. Analyze this text and identify which parts are English and which are Bijoy-encoded Bengali. |
| Text: {text} |
| Return a JSON array where each element is an object with: |
| - "text": the text segment |
| - "is_english": true if English, false if Bijoy Bengali |
| Rules: |
| 1. English segments include: letters (a-z, A-Z), common punctuation, numbers in English context |
| 2. Bijoy segments include: Bengali-encoded characters with special symbols |
| 3. Preserve exact spacing and punctuation |
| 4. Split at language boundaries |
| Example output format: |
| [ |
| {{"text": "bijoy text here", "is_english": false}}, |
| {{"text": " This is English.", "is_english": true}}, |
| {{"text": " more bijoy", "is_english": false}} |
| ] |
| Return ONLY the JSON array, nothing else.""" |
|
|
| response = client.chat.completions.create( |
| messages=[{"role": "user", "content": prompt}], |
| model="llama-3.3-70b-versatile", |
| temperature=0.1, |
| max_tokens=2000 |
| ) |
| |
| result_text = response.choices[0].message.content.strip() |
| |
| |
| json_match = re.search(r'\[.*\]', result_text, re.DOTALL) |
| if json_match: |
| import json |
| segments = json.loads(json_match.group()) |
| return segments |
| else: |
| return [{"text": text, "is_english": False}] |
| |
| except Exception as e: |
| print(f"Groq API Error: {e}") |
| return [{"text": text, "is_english": False}] |
|
|
|
|
| def create_txt_file(unicode_text): |
| """Create a TXT file with the converted Unicode text""" |
| temp_txt = tempfile.NamedTemporaryFile(delete=False, suffix=".txt", mode='w', encoding='utf-8') |
| temp_txt.write(unicode_text) |
| temp_txt.close() |
| return temp_txt.name |
|
|
|
|
| def create_docx_file(unicode_text): |
| """Create a DOCX file with the converted Unicode text""" |
| doc = Document() |
| |
| |
| paragraph = doc.add_paragraph(unicode_text) |
| |
| |
| paragraph_format = paragraph.paragraph_format |
| paragraph_format.line_spacing = 1.5 |
| paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT |
| |
| |
| for run in paragraph.runs: |
| run.font.size = Pt(12) |
| run.font.name = 'Noto Sans Bengali' |
| run.font.color.rgb = RGBColor(0, 0, 0) |
| |
| |
| temp_docx = tempfile.NamedTemporaryFile(delete=False, suffix=".docx") |
| temp_docx.close() |
| doc.save(temp_docx.name) |
| |
| return temp_docx.name |
|
|
|
|
| def bijoy_to_all_formats(bijoy_text, uploaded_font, groq_api_key): |
| try: |
| if not groq_api_key or groq_api_key.strip() == "": |
| return "Error: Please enter your Groq API key.", None, None, None |
| |
| |
| segments = detect_and_preserve_english(bijoy_text, groq_api_key) |
| |
| |
| converted_segments = [] |
| for segment in segments: |
| if segment["is_english"]: |
| converted_segments.append(segment["text"]) |
| else: |
| try: |
| unicode_segment = uc.convert_bijoy_to_unicode(segment["text"]) |
| converted_segments.append(unicode_segment) |
| except: |
| converted_segments.append(segment["text"]) |
| |
| unicode_text = "".join(converted_segments) |
|
|
| |
| txt_path = create_txt_file(unicode_text) |
| |
| |
| docx_path = create_docx_file(unicode_text) |
|
|
| |
| if uploaded_font is not None: |
| temp_font_dir = tempfile.mkdtemp() |
| font_path = os.path.join(temp_font_dir, os.path.basename(uploaded_font.name)) |
| shutil.copy(uploaded_font.name, font_path) |
| else: |
| if not HAS_DEFAULT_FONT: |
| raise FileNotFoundError( |
| "No font uploaded and default SolaimanLipi font not found." |
| ) |
| font_path = DEFAULT_FONT_PATH |
|
|
| |
| html_content = f""" |
| <!DOCTYPE html> |
| <html lang="bn"> |
| <head> |
| <meta charset="UTF-8"> |
| <style> |
| @page {{ |
| size: A4; |
| margin: 1in; |
| }} |
| @font-face {{ |
| font-family: 'UserBengaliFont'; |
| src: url('file://{font_path}'); |
| }} |
| body {{ |
| font-family: 'UserBengaliFont', 'Noto Sans Bengali', Arial, sans-serif; |
| font-size: 12pt; |
| line-height: 18pt; |
| color: #000; |
| }} |
| p {{ |
| margin-bottom: 12pt; |
| text-align: left; |
| }} |
| </style> |
| </head> |
| <body> |
| <p>{unicode_text}</p> |
| </body> |
| </html> |
| """ |
|
|
| |
| temp_pdf = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") |
| temp_pdf.close() |
|
|
| HTML(string=html_content).write_pdf( |
| temp_pdf.name, |
| presentational_hints=True |
| ) |
|
|
| return unicode_text, temp_pdf.name, docx_path, txt_path |
|
|
| except Exception as e: |
| return f"Error: {str(e)}", None, None, None |
|
|
|
|
| |
| |
| |
|
|
| bijoy_examples = [ |
| """‡nj‡g‡Ui wb‡P mvbMvm| mKvj‡eLv‡b cÖwZw`b gvbyl ‡Kvyvi g‡Zv RvqMv _v‡K bv| ‡divi c‡_ Zviv Avcbvi mgq, kÖg Ges AwfÁZv‡K GKmv‡_ wb‡q Rxeb cÖwZ‡e`b K‡i|""", |
|
|
| """evsjv‡`‡ki cÖK…wZ gvby‡li g‡b wewfbœ Av‡eM m„wó K‡i| b`x, cvnv‡o Ges meyR gvwUi mv‡_ gvby‡li AvZœxq m¤úK© AvMvb _v‡K| G mgqB Zviv cÖK…wZi m~ÿ wQšÍv K‡i|""", |
|
|
| """wkÿv gvb‡e Rxeb‡K cÖKvwkZ K‡i Ges gvbwmK w`K _v‡K m„wó K‡i| wkÿv Qvov gvbyl AwbðqZvi g‡a¨ _v‡K Ges mvgvwRK weKv‡k cÖwZKzjZvi m¤§yLxb nq|""", |
|
|
| """AvaywbK cÖhyw³ gvby‡li `xN©Rxeb‡K m¤ú~Y©iƒ‡c cwieZ©b K‡i‡Q| cÖhyw³i e¨envi Avgv‡`i KvR‡K mnR K‡i Ges mgq euvPvq, Z‡e myweav m¤ú‡K© mZK© _vKvI cÖ‡qvRb|""", |
|
|
| """mvgvwRK g~j¨‡eva gvby‡li AvPiY Ges AvPib‡K wbqš¿Y K‡i| mZ¨Zv, b¨vq Ges m¤§vb gvb‡ei g‡a¨ GK‡gvmZ cÖKvk K‡i Ges mvgvwRK m¤úªxwZi Dci AwZ¸i"Z¡c~Y©|""" |
| ] |
|
|
| |
| |
| |
|
|
| with gr.Blocks(title="Bijoy to Unicode Bengali Converter", theme=gr.themes.Ocean()) as app: |
| gr.Markdown("## 📝 Bijoy → Unicode Bengali Converter (PDF + DOCX + TXT)") |
| gr.Markdown( |
| "**Features:**\n" |
| "- Convert Bijoy Bangla to Unicode using AI\n" |
| "- Automatically detects and preserves English text\n" |
| "- Generate PDF, DOCX, and TXT files\n" |
| "- Upload your preferred Bengali font (.ttf)\n\n" |
| "**Note:** You need a free Groq API key. Get one at [https://console.groq.com](https://console.groq.com)" |
| ) |
|
|
| groq_key_input = gr.Textbox( |
| label="Groq API Key", |
| placeholder="Enter your Groq API key here (e.g., gsk_...)", |
| type="password" |
| ) |
|
|
| bijoy_input = gr.Textbox( |
| label="Bijoy Text (Mixed Bengali & English)", |
| placeholder="Paste Bijoy-encoded Bangla text with English here...", |
| lines=10 |
| ) |
|
|
| font_input = gr.File( |
| label="Optional Bengali Font (.ttf)", |
| file_types=[".ttf"] |
| ) |
|
|
| convert_btn = gr.Button("Convert & Generate All Files", variant="primary") |
|
|
| unicode_output = gr.Textbox( |
| label="Converted Unicode Text", |
| lines=10, |
| show_copy_button=True |
| ) |
|
|
| with gr.Row(): |
| pdf_output = gr.File(label="📄 Download PDF") |
| docx_output = gr.File(label="📝 Download DOCX") |
| txt_output = gr.File(label="📃 Download TXT") |
|
|
| gr.Examples( |
| examples=bijoy_examples, |
| inputs=bijoy_input, |
| label="📌 Example Bijoy Texts" |
| ) |
|
|
| convert_btn.click( |
| fn=bijoy_to_all_formats, |
| inputs=[bijoy_input, font_input, groq_key_input], |
| outputs=[unicode_output, pdf_output, docx_output, txt_output] |
| ) |
|
|
| gr.Markdown( |
| "---\n" |
| "### How to get Groq API Key:\n" |
| "1. Visit [console.groq.com](https://console.groq.com)\n" |
| "2. Sign up for free\n" |
| "3. Create an API key\n" |
| "4. Paste it above\n\n" |
| "### Output Files:\n" |
| "- **PDF**: Print-ready with custom font support\n" |
| "- **DOCX**: Editable Word document\n" |
| "- **TXT**: Plain text Unicode file\n\n" |
| "The app uses the free **llama-3.3-70b-versatile** model to detect English text." |
| ) |
|
|
| app.launch() |