Opera8 commited on
Commit
419f149
·
verified ·
1 Parent(s): f2c4a09

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -116
app.py CHANGED
@@ -3,98 +3,92 @@ 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
- import markdown # --- کتابخانه پردازش مارک‌داون برای تشخیص تیترها و بولدها ---
14
-
15
- # --- کتابخانه کلیدی برای تبدیل HTML به DOCX ---
16
  from htmldocx import HtmlToDocx
17
 
18
  app = Flask(__name__)
19
- CORS(app) # --- اجازه دسترسی به همه دامنه‌ها ---
20
 
21
- # --- ثابت‌ها ---
22
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
23
  FONT_FILE_NAME = "Vazirmatn-Regular.ttf"
24
  FOOTER_TEXT = "هوش مصنوعی آلفا دانلود از گوگل پلی"
25
 
26
-
27
- # --- توابع کمکی ---
28
  def get_line_direction(line):
29
  if not line or line.isspace(): return 'ltr'
30
  rtl_pattern = re.compile(r'[\u0600-\u06FF\u0750-\u077F]')
31
  return 'rtl' if rtl_pattern.search(line) else 'ltr'
32
 
33
  def reshape_rtl_text(text):
34
- # اعمال Reshape برای رفع مشکل جدا نویسی حروف فارسی
35
- return arabic_reshaper.reshape(text)
36
-
37
- # --- توابع ساخت فایل ---
38
 
39
  def get_base_html_for_conversion(text_content):
40
- """
41
- یک رشته HTML پایه تولید می‌کند که عناوین، لیست‌ها و متون بولد مارک‌داون
42
- را به دقت به تگ‌های HTML تبدیل می‌کند و سپس راست‌چین می‌سازد.
43
- """
44
- # ۱. تبدیل هوشمند متن هوش مصنوعی (مارک‌داون) به HTML خام
45
  raw_html = markdown.markdown(
46
  text_content,
47
  extensions=['extra', 'tables', 'nl2br', 'sane_lists']
48
  )
 
49
 
50
- # ۲. اعمال Reshape روی حروف فارسی (تگ‌های HTML انگلیسی دست‌نخورده می‌مانند)
51
- reshaped_html = reshape_rtl_text(raw_html)
52
-
53
- # ۳. تزریق استایل راست‌چین و فونت استاندارد به تمام تگ‌های متنی (هدرها، پاراگراف‌ها، جداول)
54
  tags_to_rtl =['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'table', 'th', 'td']
55
  for tag in tags_to_rtl:
56
  pattern = re.compile(rf'<{tag}(?P<attrs>\s[^>]*)?>', re.IGNORECASE)
57
-
58
  def replacer(match):
59
  attrs = match.group('attrs') or ''
60
- # اگر تگ از پیش برای کدهای برنامه‌نویسی و فرمول‌ها چپ‌چین تنظیم شده بود به آن دست نمی‌زنیم
61
  if 'dir="ltr"' in attrs.lower() or 'style="text-align: left' in attrs.lower():
62
  return match.group(0)
63
  return f'<{tag}{attrs} style="text-align: right; direction: rtl; font-family: Vazir, sans-serif;">'
64
-
65
  reshaped_html = pattern.sub(replacer, reshaped_html)
66
 
67
  return reshaped_html
68
 
69
-
70
  def create_docx(text_content):
71
- """
72
- DOCX را با تبدیل مستقیم از HTML (شامل تیترهای بزرگ و متن‌های توپر) ایجاد می‌کند.
73
- """
74
- print("--- DOCX Creation: Using HTML to DOCX conversion method ---")
75
- document = Document()
76
- parser = HtmlToDocx()
77
- html_content = get_base_html_for_conversion(text_content)
78
- parser.add_html_to_document(html_content, document)
79
-
80
- # قرار دادن متن تبلیغاتی فقط در انتهای فایل (صفحه آخر) به جای پانویس تکراری
81
- document.add_paragraph("") # ایجاد یک خط فاصله
82
- footer_p = document.add_paragraph(reshape_rtl_text(FOOTER_TEXT))
83
- footer_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
84
- # بولد کردن متن تبلیغاتی
85
- for run in footer_p.runs:
86
- run.bold = True
87
-
88
- buffer = io.BytesIO()
89
- document.save(buffer)
90
- buffer.seek(0)
91
- return buffer
92
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
  def create_pdf_with_weasyprint(text_content):
95
- """
96
- PDF را از روی یک قالب کامل HTML با استایل‌های حرفه‌ای (تیتر، کد، فرمول، بولد) ایجاد می‌کند.
97
- """
98
  html_body = get_base_html_for_conversion(text_content)
99
  reshaped_footer = reshape_rtl_text(FOOTER_TEXT)
100
 
@@ -102,84 +96,38 @@ def create_pdf_with_weasyprint(text_content):
102
  <!DOCTYPE html><html lang="fa"><head><meta charset="UTF-8"><title>Exported PDF</title>
103
  <style>
104
  @font-face {{ font-family: 'Vazir'; src: url('{FONT_FILE_NAME}'); }}
105
- body {{
106
- font-family: 'Vazir', sans-serif;
107
- font-size: 12pt;
108
- line-height: 1.8;
109
- color: #111;
110
- }}
111
- /* تنظیمات سایز و استایل عناوین مقالات */
112
  h1 {{ font-size: 24pt; color: #0d5c75; border-bottom: 2px solid #0d5c75; padding-bottom: 5px; margin-top: 30px; margin-bottom: 15px; }}
113
  h2 {{ font-size: 20pt; color: #1a7b9c; margin-top: 25px; margin-bottom: 10px; }}
114
  h3 {{ font-size: 16pt; color: #222; margin-top: 20px; }}
115
  h4, h5, h6 {{ font-size: 14pt; color: #333; font-weight: bold; }}
116
- /* تنظیمات متون مهم */
117
  strong, b {{ font-weight: bold; color: #000; }}
118
  em, i {{ font-style: italic; color: #444; }}
119
- /* استایل نقل قول‌ها */
120
- blockquote {{
121
- border-right: 4px solid #1095c1;
122
- margin: 15px 0;
123
- padding: 10px 15px 10px 0;
124
- color: #555;
125
- background-color: #f7fbff;
126
- border-radius: 4px;
127
- }}
128
- /* استایل فرمول‌های ریاضی و کدها */
129
- code {{
130
- font-family: monospace;
131
- background-color: #f1f1f1;
132
- padding: 2px 6px;
133
- border-radius: 4px;
134
- font-size: 11pt;
135
- direction: ltr;
136
- display: inline-block;
137
- color: #c7254e;
138
- }}
139
- pre {{
140
- background-color: #f8f9fa;
141
- padding: 15px;
142
- border-radius: 6px;
143
- border: 1px solid #e1e1e8;
144
- direction: ltr;
145
- text-align: left;
146
- overflow-x: auto;
147
- }}
148
  pre code {{ background-color: transparent; padding: 0; color: #333; }}
149
- /* استایل جداول */
150
  table {{ border-collapse: collapse; width: 100%; margin-top: 20px; margin-bottom: 20px; }}
151
  th, td {{ border: 1px solid #ccc; padding: 10px; text-align: right; }}
152
  th {{ background-color: #e9ecef; font-weight: bold; }}
153
  p {{ margin: 0 0 12px 0; padding: 0; text-align: justify; }}
154
-
155
- /* استایل فوتر تغییر یافته: این بخش فقط در انتهای آخرین صفحه و بعد از مقاله قرار می‌گیرد */
156
- .footer {{
157
- margin-top: 50px;
158
- padding-top: 15px;
159
- border-top: 2px solid #1095c1;
160
- text-align: center;
161
- color: #1095c1;
162
- font-size: 11pt;
163
- font-weight: bold;
164
- font-family: 'Vazir', sans-serif;
165
- page-break-inside: avoid;
166
- }}
167
  </style></head><body>{html_body}
168
  <div class="footer">{reshaped_footer}</div></body></html>
169
  """
170
  try:
171
  html = HTML(string=full_html, base_url=BASE_DIR)
172
  return io.BytesIO(html.write_pdf())
173
- except Exception:
174
- print(f"🔥🔥🔥 WEASYPRINT FAILED! 🔥🔥🔥\n{traceback.format_exc()}")
175
- return io.BytesIO(b"Error generating PDF.")
 
176
 
177
  def create_txt(text_content):
178
  full_content = f"{text_content}\n\n\n---\n{FOOTER_TEXT}"
179
  return io.BytesIO(full_content.encode('utf-8'))
180
 
181
  def create_html(text_content):
182
- """یک فایل HTML قابل نمایش در مرورگر با استایل‌های کامل ایجاد می‌کند."""
183
  html_body = get_base_html_for_conversion(text_content)
184
  reshaped_footer = reshape_rtl_text(FOOTER_TEXT)
185
  full_html = f"""
@@ -189,8 +137,8 @@ def create_html(text_content):
189
  h1 {{ color: #0d5c75; border-bottom: 2px solid #ddd; padding-bottom: 5px; }}
190
  h2 {{ color: #1a7b9c; }}
191
  blockquote {{ border-right: 4px solid #1095c1; margin: 0; padding-right: 15px; background: #f9f9f9; padding: 10px; }}
192
- code {{ font-family: monospace; background-color: #f4f4f4; padding: 2px 5px; border-radius: 4px; direction: ltr; display: inline-block; color: #c7254e; }}
193
- pre {{ background-color: #f4f4f4; padding: 15px; border-radius: 5px; direction: ltr; text-align: left; overflow-x: auto; }}
194
  table {{ border-collapse: collapse; width: 100%; }}
195
  th, td {{ border: 1px solid #ddd; padding: 8px; }}
196
  th {{ background-color: #f2f2f2; }}
@@ -201,7 +149,6 @@ def create_html(text_content):
201
  """
202
  return io.BytesIO(full_html.encode('utf-8'))
203
 
204
- # --- تابع پردازشگر اصلی درخواست ---
205
  def process_request(content, file_format):
206
  actions = {'pdf': create_pdf_with_weasyprint, 'docx': create_docx, 'html': create_html, 'txt': create_txt}
207
  buffer_func = actions.get(file_format, create_txt)
@@ -211,15 +158,11 @@ def process_request(content, file_format):
211
  filename = f'export.{file_format}'
212
  return send_file(buffer, as_attachment=True, download_name=filename, mimetype=mimetype)
213
 
214
-
215
- # --- بخش مدیریت روت‌های وب ---
216
  @app.route('/', methods=['GET', 'POST', 'HEAD'])
217
  def index():
218
- # اگر درخواست از نوع HEAD بود (برای Health Check سرویس) یک پاسخ موفقیت‌آمیز و خالی برگردان
219
  if request.method == 'HEAD':
220
  return '', 200
221
 
222
- # اگر درخواست از نوع POST بود (کاربر دکمه ساخت فایل را زده یا ربات درخواست داده است)
223
  if request.method == 'POST':
224
  content = request.form.get('content')
225
  file_format = request.form.get('format', 'txt').lower()
@@ -227,9 +170,7 @@ def index():
227
  return "لطفا متنی برای تبدیل وارد کنید.", 400
228
  return process_request(content, file_format)
229
 
230
- # در غیر این صورت، درخواست GET است و باید صفحه اصلی نمایش داده شود
231
  return render_template('index.html')
232
 
233
-
234
  if __name__ == '__main__':
235
- app.run(debug=True)
 
3
  import traceback
4
  import re
5
  from flask import Flask, request, send_file, render_template
6
+ from flask_cors import CORS
7
 
 
8
  from docx import Document
9
  from docx.enum.text import WD_ALIGN_PARAGRAPH
10
+ from docx.shared import Pt
11
  from weasyprint import HTML, CSS
12
  import arabic_reshaper
13
+ from bidi.algorithm import get_display # برای راست‌چین و ترکیب حروف فارسی
14
+ import markdown
 
15
  from htmldocx import HtmlToDocx
16
 
17
  app = Flask(__name__)
18
+ CORS(app)
19
 
 
20
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
21
  FONT_FILE_NAME = "Vazirmatn-Regular.ttf"
22
  FOOTER_TEXT = "هوش مصنوعی آلفا دانلود از گوگل پلی"
23
 
 
 
24
  def get_line_direction(line):
25
  if not line or line.isspace(): return 'ltr'
26
  rtl_pattern = re.compile(r'[\u0600-\u06FF\u0750-\u077F]')
27
  return 'rtl' if rtl_pattern.search(line) else 'ltr'
28
 
29
  def reshape_rtl_text(text):
30
+ # ترکیب reshaper و bidi برای خروجی بی‌نقص در فایل‌ها
31
+ reshaped_text = arabic_reshaper.reshape(text)
32
+ return get_display(reshaped_text)
 
33
 
34
  def get_base_html_for_conversion(text_content):
 
 
 
 
 
35
  raw_html = markdown.markdown(
36
  text_content,
37
  extensions=['extra', 'tables', 'nl2br', 'sane_lists']
38
  )
39
+ reshaped_html = arabic_reshaper.reshape(raw_html)
40
 
 
 
 
 
41
  tags_to_rtl =['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'table', 'th', 'td']
42
  for tag in tags_to_rtl:
43
  pattern = re.compile(rf'<{tag}(?P<attrs>\s[^>]*)?>', re.IGNORECASE)
 
44
  def replacer(match):
45
  attrs = match.group('attrs') or ''
 
46
  if 'dir="ltr"' in attrs.lower() or 'style="text-align: left' in attrs.lower():
47
  return match.group(0)
48
  return f'<{tag}{attrs} style="text-align: right; direction: rtl; font-family: Vazir, sans-serif;">'
 
49
  reshaped_html = pattern.sub(replacer, reshaped_html)
50
 
51
  return reshaped_html
52
 
 
53
  def create_docx(text_content):
54
+ try:
55
+ document = Document()
56
+ parser = HtmlToDocx()
57
+ html_content = get_base_html_for_conversion(text_content)
58
+ parser.add_html_to_document(html_content, document)
59
+
60
+ document.add_paragraph("")
61
+ footer_p = document.add_paragraph(reshape_rtl_text(FOOTER_TEXT))
62
+ footer_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
63
+ for run in footer_p.runs:
64
+ run.bold = True
65
+
66
+ buffer = io.BytesIO()
67
+ document.save(buffer)
68
+ buffer.seek(0)
69
+ return buffer
70
+ except Exception as e:
71
+ print(f"⚠️ HTML to DOCX Failed: {e}")
72
+ # سیستم Fallback: اگر مارک‌داون هوش مصنوعی خراب بود، فایل Word ساده می‌سازد
73
+ document = Document()
74
+ document.styles['Normal'].font.name = 'Arial'
75
+
76
+ for line in text_content.split('\n'):
77
+ if line.strip():
78
+ p = document.add_paragraph(reshape_rtl_text(line))
79
+ p.alignment = WD_ALIGN_PARAGRAPH.RIGHT
80
+
81
+ document.add_paragraph("\n")
82
+ footer_p = document.add_paragraph(reshape_rtl_text(FOOTER_TEXT))
83
+ footer_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
84
+ for run in footer_p.runs: run.bold = True
85
+
86
+ buffer = io.BytesIO()
87
+ document.save(buffer)
88
+ buffer.seek(0)
89
+ return buffer
90
 
91
  def create_pdf_with_weasyprint(text_content):
 
 
 
92
  html_body = get_base_html_for_conversion(text_content)
93
  reshaped_footer = reshape_rtl_text(FOOTER_TEXT)
94
 
 
96
  <!DOCTYPE html><html lang="fa"><head><meta charset="UTF-8"><title>Exported PDF</title>
97
  <style>
98
  @font-face {{ font-family: 'Vazir'; src: url('{FONT_FILE_NAME}'); }}
99
+ body {{ font-family: 'Vazir', sans-serif; font-size: 12pt; line-height: 1.8; color: #111; }}
 
 
 
 
 
 
100
  h1 {{ font-size: 24pt; color: #0d5c75; border-bottom: 2px solid #0d5c75; padding-bottom: 5px; margin-top: 30px; margin-bottom: 15px; }}
101
  h2 {{ font-size: 20pt; color: #1a7b9c; margin-top: 25px; margin-bottom: 10px; }}
102
  h3 {{ font-size: 16pt; color: #222; margin-top: 20px; }}
103
  h4, h5, h6 {{ font-size: 14pt; color: #333; font-weight: bold; }}
 
104
  strong, b {{ font-weight: bold; color: #000; }}
105
  em, i {{ font-style: italic; color: #444; }}
106
+ blockquote {{ border-right: 4px solid #1095c1; margin: 15px 0; padding: 10px 15px 10px 0; color: #555; background-color: #f7fbff; border-radius: 4px; }}
107
+ code {{ font-family: monospace; background-color: #f1f1f1; padding: 2px 6px; border-radius: 4px; font-size: 11pt; direction: ltr; display: inline-block; color: #c7254e; word-wrap: break-word; }}
108
+ pre {{ background-color: #f8f9fa; padding: 15px; border-radius: 6px; border: 1px solid #e1e1e8; direction: ltr; text-align: left; overflow-x: auto; white-space: pre-wrap; }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  pre code {{ background-color: transparent; padding: 0; color: #333; }}
 
110
  table {{ border-collapse: collapse; width: 100%; margin-top: 20px; margin-bottom: 20px; }}
111
  th, td {{ border: 1px solid #ccc; padding: 10px; text-align: right; }}
112
  th {{ background-color: #e9ecef; font-weight: bold; }}
113
  p {{ margin: 0 0 12px 0; padding: 0; text-align: justify; }}
114
+ .footer {{ margin-top: 50px; padding-top: 15px; border-top: 2px solid #1095c1; text-align: center; color: #1095c1; font-size: 11pt; font-weight: bold; font-family: 'Vazir', sans-serif; page-break-inside: avoid; }}
 
 
 
 
 
 
 
 
 
 
 
 
115
  </style></head><body>{html_body}
116
  <div class="footer">{reshaped_footer}</div></body></html>
117
  """
118
  try:
119
  html = HTML(string=full_html, base_url=BASE_DIR)
120
  return io.BytesIO(html.write_pdf())
121
+ except Exception as e:
122
+ print(f"🔥🔥🔥 WEASYPRINT FAILED! 🔥🔥🔥\n{e}")
123
+ # در صورت شکست، به جای ارور دادن، یک خروجی ساده برمی‌گرداند
124
+ return create_txt("خطا در ساخت PDF به دلیل پیچیدگی متن. لطفا از فایل DOCX استفاده کنید.\n\n" + text_content)
125
 
126
  def create_txt(text_content):
127
  full_content = f"{text_content}\n\n\n---\n{FOOTER_TEXT}"
128
  return io.BytesIO(full_content.encode('utf-8'))
129
 
130
  def create_html(text_content):
 
131
  html_body = get_base_html_for_conversion(text_content)
132
  reshaped_footer = reshape_rtl_text(FOOTER_TEXT)
133
  full_html = f"""
 
137
  h1 {{ color: #0d5c75; border-bottom: 2px solid #ddd; padding-bottom: 5px; }}
138
  h2 {{ color: #1a7b9c; }}
139
  blockquote {{ border-right: 4px solid #1095c1; margin: 0; padding-right: 15px; background: #f9f9f9; padding: 10px; }}
140
+ code {{ font-family: monospace; background-color: #f4f4f4; padding: 2px 5px; border-radius: 4px; direction: ltr; display: inline-block; color: #c7254e; word-wrap: break-word; }}
141
+ pre {{ background-color: #f4f4f4; padding: 15px; border-radius: 5px; direction: ltr; text-align: left; overflow-x: auto; white-space: pre-wrap; }}
142
  table {{ border-collapse: collapse; width: 100%; }}
143
  th, td {{ border: 1px solid #ddd; padding: 8px; }}
144
  th {{ background-color: #f2f2f2; }}
 
149
  """
150
  return io.BytesIO(full_html.encode('utf-8'))
151
 
 
152
  def process_request(content, file_format):
153
  actions = {'pdf': create_pdf_with_weasyprint, 'docx': create_docx, 'html': create_html, 'txt': create_txt}
154
  buffer_func = actions.get(file_format, create_txt)
 
158
  filename = f'export.{file_format}'
159
  return send_file(buffer, as_attachment=True, download_name=filename, mimetype=mimetype)
160
 
 
 
161
  @app.route('/', methods=['GET', 'POST', 'HEAD'])
162
  def index():
 
163
  if request.method == 'HEAD':
164
  return '', 200
165
 
 
166
  if request.method == 'POST':
167
  content = request.form.get('content')
168
  file_format = request.form.get('format', 'txt').lower()
 
170
  return "لطفا متنی برای تبدیل وارد کنید.", 400
171
  return process_request(content, file_format)
172
 
 
173
  return render_template('index.html')
174
 
 
175
  if __name__ == '__main__':
176
+ app.run(debug=False, host='0.0.0.0', port=7860)