Upload 4 files
Browse files- .gitattributes +1 -0
- Vazirmatn-Regular.ttf +3 -0
- app.py +147 -0
- font_converter.py +19 -0
- requirements.txt +7 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
Vazirmatn-Regular.ttf filter=lfs diff=lfs merge=lfs -text
|
Vazirmatn-Regular.ttf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4ff2f8a98d8a4311900cda6dfb9381b5e65cc9d33725301a89060edc87884b67
|
| 3 |
+
size 126728
|
app.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import io
|
| 3 |
+
import traceback
|
| 4 |
+
import re
|
| 5 |
+
from flask import Flask, request, send_file, render_template
|
| 6 |
+
from flask_cors import CORS # --- این خط اضافه شده است ---
|
| 7 |
+
|
| 8 |
+
# --- کتابخانههای اصلی ---
|
| 9 |
+
from docx import Document
|
| 10 |
+
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
| 11 |
+
from weasyprint import HTML, CSS
|
| 12 |
+
import arabic_reshaper
|
| 13 |
+
|
| 14 |
+
# --- کتابخانه جدید و کلیدی برای تبدیل HTML به DOCX ---
|
| 15 |
+
from htmldocx import HtmlToDocx
|
| 16 |
+
|
| 17 |
+
app = Flask(__name__)
|
| 18 |
+
CORS(app) # --- این خط اضافه شده است تا به همه دامنهها اجازه دسترسی بدهد ---
|
| 19 |
+
|
| 20 |
+
# --- ثابتها ---
|
| 21 |
+
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
| 22 |
+
FONT_FILE_NAME = "Vazirmatn-Regular.ttf"
|
| 23 |
+
FOOTER_TEXT = "هوش مصنوعی آلفا دانلود از گوگل پلی"
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
# --- توابع کمکی ---
|
| 27 |
+
def get_line_direction(line):
|
| 28 |
+
if not line or line.isspace(): return 'ltr'
|
| 29 |
+
rtl_pattern = re.compile(r'[\u0600-\u06FF\u0750-\u077F]')
|
| 30 |
+
return 'rtl' if rtl_pattern.search(line) else 'ltr'
|
| 31 |
+
|
| 32 |
+
def reshape_rtl_text(line):
|
| 33 |
+
return arabic_reshaper.reshape(line)
|
| 34 |
+
|
| 35 |
+
# --- توابع ساخت فایل ---
|
| 36 |
+
|
| 37 |
+
def get_base_html_for_conversion(text_content):
|
| 38 |
+
"""
|
| 39 |
+
یک رشته HTML پایه تولید میکند که هم برای PDF و هم برای DOCX قابل استفاده است.
|
| 40 |
+
"""
|
| 41 |
+
content_html_parts = []
|
| 42 |
+
for line in text_content.split('\n'):
|
| 43 |
+
if not line.strip():
|
| 44 |
+
content_html_parts.append('<p> </p>')
|
| 45 |
+
continue
|
| 46 |
+
direction = get_line_direction(line)
|
| 47 |
+
if direction == 'rtl':
|
| 48 |
+
reshaped_line = reshape_rtl_text(line)
|
| 49 |
+
content_html_parts.append(f'<p style="text-align: right; direction: rtl;">{reshaped_line}</p>')
|
| 50 |
+
else:
|
| 51 |
+
content_html_parts.append(f'<p style="text-align: left; direction: ltr;">{line}</p>')
|
| 52 |
+
return "\n".join(content_html_parts)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def create_docx(text_content):
|
| 56 |
+
"""
|
| 57 |
+
DOCX را با تبدیل مستقیم از HTML ایجاد میکند.
|
| 58 |
+
"""
|
| 59 |
+
print("--- DOCX Creation: Using HTML to DOCX conversion method ---")
|
| 60 |
+
document = Document()
|
| 61 |
+
parser = HtmlToDocx()
|
| 62 |
+
html_content = get_base_html_for_conversion(text_content)
|
| 63 |
+
parser.add_html_to_document(html_content, document)
|
| 64 |
+
footer = document.sections[0].footer
|
| 65 |
+
footer_p = footer.paragraphs[0] if footer.paragraphs else footer.add_paragraph()
|
| 66 |
+
footer_p.text = reshape_rtl_text(FOOTER_TEXT)
|
| 67 |
+
footer_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
| 68 |
+
buffer = io.BytesIO()
|
| 69 |
+
document.save(buffer)
|
| 70 |
+
buffer.seek(0)
|
| 71 |
+
return buffer
|
| 72 |
+
|
| 73 |
+
def create_pdf_with_weasyprint(text_content):
|
| 74 |
+
"""
|
| 75 |
+
PDF را از روی یک قالب کامل HTML با فونت سفارشی ایجاد میکند.
|
| 76 |
+
"""
|
| 77 |
+
html_body = get_base_html_for_conversion(text_content)
|
| 78 |
+
reshaped_footer = reshape_rtl_text(FOOTER_TEXT)
|
| 79 |
+
full_html = f"""
|
| 80 |
+
<!DOCTYPE html><html lang="fa"><head><meta charset="UTF-8"><title>Exported PDF</title>
|
| 81 |
+
<style>
|
| 82 |
+
@font-face {{ font-family: 'Vazir'; src: url('{FONT_FILE_NAME}'); }}
|
| 83 |
+
body {{ font-family: 'Vazir', sans-serif; font-size: 12pt; line-height: 1.8; }}
|
| 84 |
+
p {{ margin: 0; padding: 0; }}
|
| 85 |
+
.footer {{ position: fixed; bottom: 10px; left: 0; right: 0; text-align: center; color: #007bff; font-size: 10pt; font-family: 'Vazir', sans-serif; }}
|
| 86 |
+
</style></head><body>{html_body}
|
| 87 |
+
<div class="footer">{reshaped_footer}</div></body></html>
|
| 88 |
+
"""
|
| 89 |
+
try:
|
| 90 |
+
html = HTML(string=full_html, base_url=BASE_DIR)
|
| 91 |
+
return io.BytesIO(html.write_pdf())
|
| 92 |
+
except Exception:
|
| 93 |
+
print(f"🔥🔥🔥 WEASYPRINT FAILED! 🔥🔥🔥\n{traceback.format_exc()}")
|
| 94 |
+
return io.BytesIO(b"Error generating PDF.")
|
| 95 |
+
|
| 96 |
+
def create_txt(text_content):
|
| 97 |
+
full_content = f"{text_content}\n\n\n---\n{FOOTER_TEXT}"
|
| 98 |
+
return io.BytesIO(full_content.encode('utf-8'))
|
| 99 |
+
|
| 100 |
+
def create_html(text_content):
|
| 101 |
+
"""یک فایل HTML قابل نمایش در مرورگر با استایلهای کامل ایجاد میکند."""
|
| 102 |
+
html_body = get_base_html_for_conversion(text_content)
|
| 103 |
+
reshaped_footer = reshape_rtl_text(FOOTER_TEXT)
|
| 104 |
+
full_html = f"""
|
| 105 |
+
<!DOCTYPE html><html lang="fa"><head><meta charset="UTF-8"><title>Exported File</title>
|
| 106 |
+
<style>
|
| 107 |
+
body {{ font-size: 12pt; line-height: 1.8; max-width: 800px; margin: 2rem auto; padding: 2rem; border: 1px solid #ddd; font-family: sans-serif; }}
|
| 108 |
+
p {{ margin: 0; padding: 0 0 0.5em 0; }}
|
| 109 |
+
.footer {{ margin-top: 2rem; padding-top: 1rem; border-top: 1px solid #eee; text-align: center; color: #007bff; font-size: 10pt; }}
|
| 110 |
+
</style></head><body>{html_body}
|
| 111 |
+
<div class="footer">{reshaped_footer}</div></body></html>
|
| 112 |
+
"""
|
| 113 |
+
return io.BytesIO(full_html.encode('utf-8'))
|
| 114 |
+
|
| 115 |
+
# --- تابع پردازشگر اصلی درخواست ---
|
| 116 |
+
def process_request(content, file_format):
|
| 117 |
+
actions = {'pdf': create_pdf_with_weasyprint, 'docx': create_docx, 'html': create_html, 'txt': create_txt}
|
| 118 |
+
buffer_func = actions.get(file_format, create_txt)
|
| 119 |
+
buffer = buffer_func(content)
|
| 120 |
+
mimetypes = {'pdf': 'application/pdf', 'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'html': 'text/html', 'txt': 'text/plain'}
|
| 121 |
+
mimetype = mimetypes.get(file_format, 'text/plain')
|
| 122 |
+
filename = f'export.{file_format}'
|
| 123 |
+
return send_file(buffer, as_attachment=True, download_name=filename, mimetype=mimetype)
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
# --- ***** تغییر اصلی برای پشتیبانی از Render در اینجا اعمال شده است ***** ---
|
| 127 |
+
@app.route('/', methods=['GET', 'POST', 'HEAD'])
|
| 128 |
+
def index():
|
| 129 |
+
# اگر درخواست از نوع HEAD بود (برای Health Check سرویس Render)
|
| 130 |
+
# یک پاسخ موفقیتآمیز و خالی برگردان
|
| 131 |
+
if request.method == 'HEAD':
|
| 132 |
+
return '', 200
|
| 133 |
+
|
| 134 |
+
# اگر درخواست از نوع POST بود (کاربر دکمه ساخت فایل را زده)
|
| 135 |
+
if request.method == 'POST':
|
| 136 |
+
content = request.form.get('content')
|
| 137 |
+
file_format = request.form.get('format', 'txt').lower()
|
| 138 |
+
if not content:
|
| 139 |
+
return "لطفا متنی برای تبدیل وارد کنید.", 400
|
| 140 |
+
return process_request(content, file_format)
|
| 141 |
+
|
| 142 |
+
# در غیر این صورت، درخواست GET است و باید صفحه اصلی نمایش داده شود
|
| 143 |
+
return render_template('index.html')
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
if __name__ == '__main__':
|
| 147 |
+
app.run(debug=True)
|
font_converter.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# font_converter.py
|
| 2 |
+
import base64
|
| 3 |
+
|
| 4 |
+
# مطمئن شوید نام فایل فونت دقیق است
|
| 5 |
+
font_filename = 'Vazirmatn-Regular.ttf'
|
| 6 |
+
output_filename = 'font_base64.txt'
|
| 7 |
+
|
| 8 |
+
try:
|
| 9 |
+
with open(font_filename, 'rb') as font_file:
|
| 10 |
+
encoded_string = base64.b64encode(font_file.read()).decode('utf-8')
|
| 11 |
+
|
| 12 |
+
with open(output_filename, 'w') as text_file:
|
| 13 |
+
text_file.write(encoded_string)
|
| 14 |
+
|
| 15 |
+
print(f"فونت با موفقیت به Base64 تبدیل شد و در فایل '{output_filename}' ذخیره شد.")
|
| 16 |
+
print("محتوای این فایل را کپی کرده و در فایل app.py خود جایگذاری کنید.")
|
| 17 |
+
|
| 18 |
+
except FileNotFoundError:
|
| 19 |
+
print(f"خطا: فایل فونت '{font_filename}' پیدا نشد. لطفاً آن را در همین پوشه قرار دهید.")
|
requirements.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Flask
|
| 2 |
+
gunicorn
|
| 3 |
+
python-docx
|
| 4 |
+
weasyprint
|
| 5 |
+
arabic-reshaper
|
| 6 |
+
htmldocx
|
| 7 |
+
Flask-Cors
|