shuvo108 commited on
Commit
71ad0d9
·
verified ·
1 Parent(s): 9ca2a86

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +366 -196
app.py CHANGED
@@ -2,248 +2,418 @@ import os
2
  import subprocess
3
  import tempfile
4
  import uuid
5
- from flask import Flask, request, send_file, jsonify
6
  from werkzeug.utils import secure_filename
7
  from flask_cors import CORS
8
 
9
  app = Flask(__name__)
10
- CORS(app) # Frontend theke request accept korar jonno
11
 
12
- # Supported file extensions
13
- ALLOWED_EXTENSIONS = {'doc', 'docx', 'txt', 'odt'}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
- def allowed_file(filename):
16
- return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  @app.route('/')
19
  def home():
 
 
 
 
20
  return jsonify({
21
- "message": "Word to PDF Converter API",
22
- "status": "running",
23
  "endpoints": {
24
- "convert": "/convert (POST)",
25
- "health": "/health (GET)"
 
 
26
  },
27
- "instructions": "Send a POST request with 'file' field containing Word document"
 
 
28
  })
29
 
30
- @app.route('/health', methods=['GET'])
31
  def health_check():
32
  return jsonify({
33
  "status": "healthy",
34
- "service": "word-to-pdf-converter"
 
35
  })
36
 
37
  @app.route('/convert', methods=['POST'])
38
  def convert_word_to_pdf():
39
- """
40
- Convert Word document to PDF
41
- Accepts: .doc, .docx, .txt, .odt files
42
- Returns: PDF file
43
- """
44
-
45
- # Check if file was uploaded
46
  if 'file' not in request.files:
47
- return jsonify({"error": "No file uploaded"}), 400
48
 
49
  file = request.files['file']
50
-
51
- # Check if file has a name
52
  if file.filename == '':
53
- return jsonify({"error": "No file selected"}), 400
54
 
55
  # Check file extension
56
- if not allowed_file(file.filename):
57
- return jsonify({
58
- "error": "File type not allowed. Supported types: .doc, .docx, .txt, .odt"
59
- }), 400
60
 
61
- # Create a unique ID for this conversion
62
- conversion_id = str(uuid.uuid4())[:8]
 
 
 
 
 
63
 
64
- # Use temporary directory
65
  with tempfile.TemporaryDirectory() as tmpdir:
 
 
 
 
 
 
 
 
 
66
  try:
67
- # Save uploaded file
68
- original_filename = secure_filename(file.filename)
69
- input_path = os.path.join(tmpdir, original_filename)
70
- file.save(input_path)
71
-
72
- # Generate output filename
73
- base_name = os.path.splitext(original_filename)[0]
74
- pdf_filename = f"{base_name}_{conversion_id}.pdf"
75
- output_path = os.path.join(tmpdir, pdf_filename)
76
 
77
- # Method 1: Try LibreOffice first (most reliable)
78
- try:
79
- # Check if LibreOffice is available
80
- libreoffice_paths = [
81
- "/usr/bin/libreoffice",
82
- "/usr/local/bin/libreoffice",
83
- "/bin/libreoffice"
84
- ]
85
-
86
- libreoffice_found = None
87
- for path in libreoffice_paths:
88
- if os.path.exists(path):
89
- libreoffice_found = path
90
- break
91
-
92
- if libreoffice_found:
93
  cmd = [
94
- libreoffice_found,
95
  '--headless',
96
- '--convert-to', 'pdf:writer_pdf_Export',
97
  '--outdir', tmpdir,
98
  input_path
99
  ]
100
 
101
- # Run conversion
102
- result = subprocess.run(
103
- cmd,
104
- capture_output=True,
105
- text=True,
106
- timeout=30
107
- )
108
 
109
  if result.returncode == 0:
110
- # Check if PDF was created
111
- expected_pdf = os.path.join(tmpdir, f"{base_name}.pdf")
112
- if os.path.exists(expected_pdf):
113
- # Rename with conversion ID
114
- os.rename(expected_pdf, output_path)
115
- else:
116
- # Try alternative PDF name
117
- for f in os.listdir(tmpdir):
118
- if f.endswith('.pdf'):
119
- os.rename(os.path.join(tmpdir, f), output_path)
120
- break
121
-
122
- else:
123
- raise Exception(f"LibreOffice error: {result.stderr}")
124
-
125
- else:
126
- raise Exception("LibreOffice not found")
127
-
128
- except Exception as libreoffice_error:
129
- # Method 2: Fallback to python-based conversion
130
- app.logger.warning(f"LibreOffice failed: {libreoffice_error}. Trying Python fallback.")
131
-
132
- try:
133
- from docx2pdf import convert
134
- # Install with: pip install docx2pdf
135
- convert(input_path, output_path)
136
- except ImportError:
137
- # Method 3: Final fallback - use python-docx and reportlab
138
- app.logger.warning("docx2pdf not available, using basic conversion")
139
-
140
- if original_filename.endswith('.txt'):
141
- from reportlab.lib.pagesizes import letter
142
- from reportlab.pdfgen import canvas
143
 
144
- with open(input_path, 'r', encoding='utf-8') as f:
145
- text_content = f.read()
146
-
147
- c = canvas.Canvas(output_path, pagesize=letter)
148
- text = c.beginText(40, 750)
149
- text.setFont("Helvetica", 12)
150
-
151
- for line in text_content.split('\n'):
152
- text.textLine(line[:100])
153
-
154
- c.drawText(text)
155
- c.save()
156
- else:
157
- return jsonify({
158
- "error": "Conversion failed. Please ensure LibreOffice is installed or use .txt files"
159
- }), 500
160
-
161
- # Check if PDF was created
162
- if not os.path.exists(output_path):
163
- return jsonify({"error": "PDF conversion failed"}), 500
164
 
165
- # Get file size
166
- file_size = os.path.getsize(output_path)
167
-
168
- # Return the PDF file
169
- return send_file(
170
- output_path,
171
- as_attachment=True,
172
- download_name=pdf_filename,
173
- mimetype='application/pdf'
174
- )
175
 
176
  except subprocess.TimeoutExpired:
177
- return jsonify({"error": "Conversion timeout - file too large or complex"}), 500
178
-
179
- except Exception as e:
180
- app.logger.error(f"Conversion error: {str(e)}")
181
- return jsonify({"error": f"Conversion failed: {str(e)}"}), 500
182
-
183
- @app.route('/api/convert', methods=['POST'])
184
- def api_convert():
185
- """
186
- API endpoint that returns JSON response with download link
187
- """
188
- if 'file' not in request.files:
189
- return jsonify({"error": "No file uploaded"}), 400
190
-
191
- file = request.files['file']
192
-
193
- if file.filename == '':
194
- return jsonify({"error": "No file selected"}), 400
195
-
196
- if not allowed_file(file.filename):
197
- return jsonify({"error": "Invalid file type"}), 400
198
-
199
- conversion_id = str(uuid.uuid4())[:8]
200
-
201
- with tempfile.TemporaryDirectory() as tmpdir:
202
- try:
203
- # Save file
204
- original_filename = secure_filename(file.filename)
205
- input_path = os.path.join(tmpdir, original_filename)
206
- file.save(input_path)
207
-
208
- # Generate output filename
209
- base_name = os.path.splitext(original_filename)[0]
210
- pdf_filename = f"{base_name}_{conversion_id}.pdf"
211
- output_path = os.path.join(tmpdir, pdf_filename)
212
-
213
- # Try LibreOffice conversion
214
- cmd = [
215
- "/usr/bin/libreoffice",
216
- '--headless',
217
- '--convert-to', 'pdf',
218
- '--outdir', tmpdir,
219
- input_path
220
- ]
221
-
222
- subprocess.run(cmd, check=True, timeout=30)
223
-
224
- # Find the generated PDF
225
- pdf_files = [f for f in os.listdir(tmpdir) if f.endswith('.pdf')]
226
- if not pdf_files:
227
- return jsonify({"error": "PDF not generated"}), 500
228
-
229
- # Rename the PDF
230
- os.rename(os.path.join(tmpdir, pdf_files[0]), output_path)
231
-
232
- # In Hugging Face Space, we can't provide direct download link
233
- # So we'll return success message and suggest using the main /convert endpoint
234
- return jsonify({
235
- "success": True,
236
- "message": "File converted successfully",
237
- "conversion_id": conversion_id,
238
- "original_file": original_filename,
239
- "pdf_filename": pdf_filename,
240
- "note": "Use the /convert endpoint directly for file download"
241
- })
242
-
243
  except Exception as e:
244
- return jsonify({"error": str(e)}), 500
245
 
246
  if __name__ == '__main__':
247
- # Hugging Face Spaces uses port 7860
248
  port = int(os.environ.get('PORT', 7860))
249
  app.run(host='0.0.0.0', port=port, debug=False)
 
2
  import subprocess
3
  import tempfile
4
  import uuid
5
+ from flask import Flask, request, send_file, jsonify, render_template_string
6
  from werkzeug.utils import secure_filename
7
  from flask_cors import CORS
8
 
9
  app = Flask(__name__)
10
+ CORS(app)
11
 
12
+ # HTML template for simple UI
13
+ HTML_TEMPLATE = '''
14
+ <!DOCTYPE html>
15
+ <html>
16
+ <head>
17
+ <title>Word to PDF Converter</title>
18
+ <style>
19
+ * { margin: 0; padding: 0; box-sizing: border-box; }
20
+ body {
21
+ font-family: Arial, sans-serif;
22
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
23
+ min-height: 100vh;
24
+ display: flex;
25
+ justify-content: center;
26
+ align-items: center;
27
+ padding: 20px;
28
+ }
29
+ .container {
30
+ background: white;
31
+ border-radius: 20px;
32
+ box-shadow: 0 20px 60px rgba(0,0,0,0.3);
33
+ padding: 40px;
34
+ width: 100%;
35
+ max-width: 500px;
36
+ text-align: center;
37
+ }
38
+ h1 {
39
+ color: #333;
40
+ margin-bottom: 10px;
41
+ font-size: 28px;
42
+ }
43
+ .subtitle {
44
+ color: #666;
45
+ margin-bottom: 30px;
46
+ font-size: 16px;
47
+ }
48
+ .upload-area {
49
+ border: 3px dashed #667eea;
50
+ border-radius: 15px;
51
+ padding: 40px 20px;
52
+ margin: 20px 0;
53
+ background: #f8f9fa;
54
+ cursor: pointer;
55
+ transition: all 0.3s;
56
+ }
57
+ .upload-area:hover {
58
+ background: #eef2ff;
59
+ border-color: #764ba2;
60
+ }
61
+ .upload-icon {
62
+ font-size: 48px;
63
+ color: #667eea;
64
+ margin-bottom: 15px;
65
+ }
66
+ .file-input {
67
+ display: none;
68
+ }
69
+ .file-name {
70
+ margin-top: 10px;
71
+ color: #666;
72
+ font-size: 14px;
73
+ word-break: break-all;
74
+ }
75
+ .convert-btn {
76
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
77
+ color: white;
78
+ border: none;
79
+ padding: 15px 40px;
80
+ font-size: 18px;
81
+ border-radius: 50px;
82
+ cursor: pointer;
83
+ margin-top: 20px;
84
+ width: 100%;
85
+ transition: transform 0.3s;
86
+ }
87
+ .convert-btn:hover {
88
+ transform: translateY(-2px);
89
+ box-shadow: 0 10px 20px rgba(102, 126, 234, 0.4);
90
+ }
91
+ .convert-btn:disabled {
92
+ opacity: 0.6;
93
+ cursor: not-allowed;
94
+ }
95
+ .progress-bar {
96
+ width: 100%;
97
+ height: 6px;
98
+ background: #e0e0e0;
99
+ border-radius: 3px;
100
+ margin: 20px 0;
101
+ overflow: hidden;
102
+ display: none;
103
+ }
104
+ .progress {
105
+ width: 0%;
106
+ height: 100%;
107
+ background: linear-gradient(90deg, #667eea, #764ba2);
108
+ transition: width 0.3s;
109
+ }
110
+ .status {
111
+ margin: 15px 0;
112
+ min-height: 24px;
113
+ }
114
+ .success { color: #10b981; }
115
+ .error { color: #ef4444; }
116
+ .info { color: #3b82f6; }
117
+ .api-info {
118
+ margin-top: 30px;
119
+ padding-top: 20px;
120
+ border-top: 1px solid #e5e7eb;
121
+ text-align: left;
122
+ }
123
+ .api-info h3 {
124
+ color: #333;
125
+ margin-bottom: 10px;
126
+ font-size: 18px;
127
+ }
128
+ .api-info code {
129
+ background: #f3f4f6;
130
+ padding: 2px 6px;
131
+ border-radius: 4px;
132
+ font-size: 14px;
133
+ }
134
+ .supported-formats {
135
+ display: flex;
136
+ justify-content: center;
137
+ gap: 10px;
138
+ margin: 15px 0;
139
+ flex-wrap: wrap;
140
+ }
141
+ .format-badge {
142
+ background: #eef2ff;
143
+ color: #667eea;
144
+ padding: 4px 12px;
145
+ border-radius: 20px;
146
+ font-size: 12px;
147
+ font-weight: bold;
148
+ }
149
+ </style>
150
+ </head>
151
+ <body>
152
+ <div class="container">
153
+ <h1>📄 Word to PDF Converter</h1>
154
+ <p class="subtitle">Convert Word documents to PDF instantly for free</p>
155
+
156
+ <div class="supported-formats">
157
+ <span class="format-badge">.DOC</span>
158
+ <span class="format-badge">.DOCX</span>
159
+ <span class="format-badge">.TXT</span>
160
+ <span class="format-badge">.ODT</span>
161
+ </div>
162
+
163
+ <div class="upload-area" onclick="document.getElementById('fileInput').click()">
164
+ <div class="upload-icon">📤</div>
165
+ <h3>Click to upload Word file</h3>
166
+ <p>or drag and drop</p>
167
+ <p style="font-size: 12px; color: #999; margin-top: 10px;">Max size: 50MB</p>
168
+ <div class="file-name" id="fileName">No file chosen</div>
169
+ </div>
170
+
171
+ <input type="file" id="fileInput" class="file-input" accept=".doc,.docx,.txt,.odt" onchange="updateFileName()">
172
+
173
+ <div class="progress-bar" id="progressBar">
174
+ <div class="progress" id="progress"></div>
175
+ </div>
176
+
177
+ <div class="status" id="status"></div>
178
+
179
+ <button class="convert-btn" onclick="convertFile()" id="convertBtn">Convert to PDF</button>
180
+
181
+ <div class="api-info">
182
+ <h3>API Usage:</h3>
183
+ <p><strong>Endpoint:</strong> <code>POST /convert</code></p>
184
+ <p><strong>Example (curl):</strong></p>
185
+ <code>curl -X POST -F "file=@document.docx" {{ url_for('convert_word_to_pdf') }} --output output.pdf</code>
186
+ </div>
187
+ </div>
188
 
189
+ <script>
190
+ let selectedFile = null;
191
+
192
+ function updateFileName() {
193
+ const fileInput = document.getElementById('fileInput');
194
+ const fileNameDiv = document.getElementById('fileName');
195
+
196
+ if (fileInput.files.length > 0) {
197
+ selectedFile = fileInput.files[0];
198
+ fileNameDiv.textContent = selectedFile.name + ' (' + formatFileSize(selectedFile.size) + ')';
199
+ fileNameDiv.style.color = '#333';
200
+ } else {
201
+ selectedFile = null;
202
+ fileNameDiv.textContent = 'No file chosen';
203
+ fileNameDiv.style.color = '#666';
204
+ }
205
+ }
206
+
207
+ function formatFileSize(bytes) {
208
+ if (bytes < 1024) return bytes + ' bytes';
209
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
210
+ return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
211
+ }
212
+
213
+ function showStatus(message, type) {
214
+ const statusDiv = document.getElementById('status');
215
+ statusDiv.innerHTML = `<p class="${type}">${message}</p>`;
216
+ statusDiv.style.display = 'block';
217
+ }
218
+
219
+ async function convertFile() {
220
+ if (!selectedFile) {
221
+ showStatus('Please select a file first!', 'error');
222
+ return;
223
+ }
224
+
225
+ if (selectedFile.size > 50 * 1024 * 1024) {
226
+ showStatus('File size must be less than 50MB', 'error');
227
+ return;
228
+ }
229
+
230
+ const convertBtn = document.getElementById('convertBtn');
231
+ const progressBar = document.getElementById('progressBar');
232
+ const progress = document.getElementById('progress');
233
+
234
+ // Reset and show progress
235
+ convertBtn.disabled = true;
236
+ convertBtn.textContent = 'Converting...';
237
+ progressBar.style.display = 'block';
238
+ progress.style.width = '30%';
239
+ showStatus('Uploading file...', 'info');
240
+
241
+ const formData = new FormData();
242
+ formData.append('file', selectedFile);
243
+
244
+ try {
245
+ progress.style.width = '60%';
246
+ showStatus('Converting to PDF...', 'info');
247
+
248
+ const response = await fetch('/convert', {
249
+ method: 'POST',
250
+ body: formData
251
+ });
252
+
253
+ progress.style.width = '90%';
254
+
255
+ if (response.ok) {
256
+ const blob = await response.blob();
257
+ const url = window.URL.createObjectURL(blob);
258
+
259
+ // Create download link
260
+ const a = document.createElement('a');
261
+ a.href = url;
262
+ a.download = selectedFile.name.replace(/\.[^/.]+$/, "") + '.pdf';
263
+ document.body.appendChild(a);
264
+ a.click();
265
+ document.body.removeChild(a);
266
+
267
+ progress.style.width = '100%';
268
+ showStatus('✅ Conversion successful! Download started.', 'success');
269
+
270
+ // Reset after 3 seconds
271
+ setTimeout(() => {
272
+ progressBar.style.display = 'none';
273
+ progress.style.width = '0%';
274
+ showStatus('', '');
275
+ }, 3000);
276
+
277
+ } else {
278
+ const error = await response.text();
279
+ showStatus('❌ Error: ' + error, 'error');
280
+ }
281
+
282
+ } catch (error) {
283
+ showStatus('❌ Network error: ' + error.message, 'error');
284
+ } finally {
285
+ convertBtn.disabled = false;
286
+ convertBtn.textContent = 'Convert to PDF';
287
+ setTimeout(() => progress.style.width = '0%', 500);
288
+ }
289
+ }
290
+
291
+ // Drag and drop functionality
292
+ const uploadArea = document.querySelector('.upload-area');
293
+ uploadArea.addEventListener('dragover', (e) => {
294
+ e.preventDefault();
295
+ uploadArea.style.background = '#eef2ff';
296
+ uploadArea.style.borderColor = '#764ba2';
297
+ });
298
+
299
+ uploadArea.addEventListener('dragleave', () => {
300
+ uploadArea.style.background = '#f8f9fa';
301
+ uploadArea.style.borderColor = '#667eea';
302
+ });
303
+
304
+ uploadArea.addEventListener('drop', (e) => {
305
+ e.preventDefault();
306
+ uploadArea.style.background = '#f8f9fa';
307
+ uploadArea.style.borderColor = '#667eea';
308
+
309
+ if (e.dataTransfer.files.length) {
310
+ document.getElementById('fileInput').files = e.dataTransfer.files;
311
+ updateFileName();
312
+ }
313
+ });
314
+ </script>
315
+ </body>
316
+ </html>
317
+ '''
318
 
319
  @app.route('/')
320
  def home():
321
+ return render_template_string(HTML_TEMPLATE)
322
+
323
+ @app.route('/api/help')
324
+ def api_help():
325
  return jsonify({
 
 
326
  "endpoints": {
327
+ "GET /": "Web interface",
328
+ "POST /convert": "Convert Word to PDF (returns PDF file)",
329
+ "GET /api/help": "This help message",
330
+ "GET /api/health": "Health check"
331
  },
332
+ "supported_formats": [".doc", ".docx", ".txt", ".odt"],
333
+ "max_file_size": "50MB",
334
+ "example_curl": "curl -X POST -F 'file=@document.docx' https://shuvo108-convertfilling.hf.space/convert --output output.pdf"
335
  })
336
 
337
+ @app.route('/api/health')
338
  def health_check():
339
  return jsonify({
340
  "status": "healthy",
341
+ "service": "word-to-pdf-converter",
342
+ "version": "1.0.0"
343
  })
344
 
345
  @app.route('/convert', methods=['POST'])
346
  def convert_word_to_pdf():
 
 
 
 
 
 
 
347
  if 'file' not in request.files:
348
+ return 'No file uploaded', 400
349
 
350
  file = request.files['file']
 
 
351
  if file.filename == '':
352
+ return 'No file selected', 400
353
 
354
  # Check file extension
355
+ allowed_ext = ['.doc', '.docx', '.txt', '.odt']
356
+ if not any(file.filename.lower().endswith(ext) for ext in allowed_ext):
357
+ return 'File type not supported. Use: .doc, .docx, .txt, .odt', 400
 
358
 
359
+ # Check file size (50MB limit)
360
+ file.seek(0, 2) # Seek to end
361
+ file_size = file.tell()
362
+ file.seek(0) # Reset to beginning
363
+
364
+ if file_size > 50 * 1024 * 1024:
365
+ return 'File too large. Max 50MB', 400
366
 
 
367
  with tempfile.TemporaryDirectory() as tmpdir:
368
+ # Save uploaded file
369
+ input_path = os.path.join(tmpdir, secure_filename(file.filename))
370
+ file.save(input_path)
371
+
372
+ # Generate output filename
373
+ base_name = os.path.splitext(file.filename)[0]
374
+ pdf_filename = f"{base_name}_converted.pdf"
375
+ output_path = os.path.join(tmpdir, pdf_filename)
376
+
377
  try:
378
+ # Try LibreOffice first
379
+ libreoffice_paths = ['/usr/bin/libreoffice', '/usr/local/bin/libreoffice']
 
 
 
 
 
 
 
380
 
381
+ for libreoffice in libreoffice_paths:
382
+ if os.path.exists(libreoffice):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  cmd = [
384
+ libreoffice,
385
  '--headless',
386
+ '--convert-to', 'pdf',
387
  '--outdir', tmpdir,
388
  input_path
389
  ]
390
 
391
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
 
 
 
 
 
 
392
 
393
  if result.returncode == 0:
394
+ # Find the generated PDF
395
+ for f in os.listdir(tmpdir):
396
+ if f.endswith('.pdf'):
397
+ generated_pdf = os.path.join(tmpdir, f)
398
+ os.rename(generated_pdf, output_path)
399
+ break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
 
401
+ if os.path.exists(output_path):
402
+ return send_file(
403
+ output_path,
404
+ as_attachment=True,
405
+ download_name=pdf_filename,
406
+ mimetype='application/pdf'
407
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
408
 
409
+ # Fallback: if LibreOffice failed
410
+ return 'Conversion failed. Please try a different file.', 500
 
 
 
 
 
 
 
 
411
 
412
  except subprocess.TimeoutExpired:
413
+ return 'Conversion timeout. File might be too large or complex.', 500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
  except Exception as e:
415
+ return f'Error: {str(e)}', 500
416
 
417
  if __name__ == '__main__':
 
418
  port = int(os.environ.get('PORT', 7860))
419
  app.run(host='0.0.0.0', port=port, debug=False)