harshdhane commited on
Commit
1e6c2a6
·
verified ·
1 Parent(s): 84b7264

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +243 -0
  2. static/style.css +645 -0
  3. templates/index.html +518 -0
app.py ADDED
@@ -0,0 +1,243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ from flask import Flask, render_template, request, jsonify, send_file
4
+ from werkzeug.utils import secure_filename
5
+ import PyPDF2
6
+ from io import BytesIO
7
+ from reportlab.pdfgen import canvas
8
+ from reportlab.lib.pagesizes import letter
9
+ import nltk
10
+ from nltk.tokenize import sent_tokenize, word_tokenize
11
+ from nltk.corpus import stopwords
12
+ from collections import defaultdict
13
+ import heapq
14
+ import tempfile
15
+ import json
16
+
17
+ # ==============================
18
+ # FIXED NLTK DOWNLOAD SECTION
19
+ # ==============================
20
+
21
+ def download_nltk_resources():
22
+ resources = ['punkt', 'punkt_tab', 'stopwords']
23
+
24
+ for resource in resources:
25
+ try:
26
+ nltk.data.find(f'tokenizers/{resource}')
27
+ except LookupError:
28
+ try:
29
+ nltk.data.find(f'corpora/{resource}')
30
+ except LookupError:
31
+ nltk.download(resource)
32
+
33
+ download_nltk_resources()
34
+
35
+ # ==============================
36
+
37
+ app = Flask(__name__)
38
+ app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size
39
+ app.config['UPLOAD_FOLDER'] = tempfile.gettempdir()
40
+ app.config['SECRET_KEY'] = 'your-secret-key-here'
41
+
42
+ ALLOWED_EXTENSIONS = {'pdf'}
43
+
44
+ def allowed_file(filename):
45
+ return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
46
+
47
+
48
+ def extract_text_from_pdf(pdf_file):
49
+ """Extract text from uploaded PDF file"""
50
+ pdf_reader = PyPDF2.PdfReader(pdf_file)
51
+ text = ""
52
+ for page in pdf_reader.pages:
53
+ page_text = page.extract_text()
54
+ if page_text:
55
+ text += page_text + "\n"
56
+ return text
57
+
58
+
59
+ def summarize_text(text, num_sentences=5):
60
+ """Summarize text using frequency-based scoring"""
61
+
62
+ sentences = sent_tokenize(text)
63
+
64
+ if len(sentences) <= num_sentences:
65
+ return text
66
+
67
+ stop_words = set(stopwords.words('english'))
68
+ words = word_tokenize(text.lower())
69
+ words = [word for word in words if word.isalnum() and word not in stop_words]
70
+
71
+ word_freq = defaultdict(int)
72
+ for word in words:
73
+ word_freq[word] += 1
74
+
75
+ sentence_scores = defaultdict(int)
76
+ for i, sentence in enumerate(sentences):
77
+ sentence_words = word_tokenize(sentence.lower())
78
+ for word in sentence_words:
79
+ if word in word_freq:
80
+ sentence_scores[i] += word_freq[word]
81
+
82
+ top_sentences = heapq.nlargest(num_sentences, sentence_scores, key=sentence_scores.get)
83
+ top_sentences.sort()
84
+
85
+ summary = [sentences[i] for i in top_sentences]
86
+ return ' '.join(summary)
87
+
88
+
89
+ def analyze_pdf_statistics(text):
90
+ """Analyze PDF and return statistics"""
91
+ sentences = sent_tokenize(text)
92
+ words = word_tokenize(text)
93
+ characters = len(text)
94
+
95
+ return {
96
+ 'page_count': text.count('\f') + 1,
97
+ 'sentence_count': len(sentences),
98
+ 'word_count': len(words),
99
+ 'character_count': characters,
100
+ 'avg_sentence_length': round(len(words) / len(sentences), 2) if sentences else 0,
101
+ 'avg_word_length': round(sum(len(word) for word in words) / len(words), 2) if words else 0
102
+ }
103
+
104
+
105
+ def create_summary_pdf(summary, filename="summary.pdf"):
106
+ """Create a downloadable PDF from summary"""
107
+ buffer = BytesIO()
108
+ pdf = canvas.Canvas(buffer, pagesize=letter)
109
+ pdf.setTitle("PDF Summary")
110
+
111
+ pdf.setFont("Helvetica-Bold", 16)
112
+ pdf.drawString(72, 750, "PDF Document Summary")
113
+ pdf.line(72, 745, 540, 745)
114
+
115
+ pdf.setFont("Helvetica", 12)
116
+ y_position = 720
117
+ max_width = 500
118
+ words = summary.split()
119
+ line = []
120
+
121
+ for word in words:
122
+ line.append(word)
123
+ test_line = ' '.join(line)
124
+
125
+ if pdf.stringWidth(test_line, "Helvetica", 12) > max_width:
126
+ line.pop()
127
+ pdf.drawString(72, y_position, ' '.join(line))
128
+ y_position -= 20
129
+ line = [word]
130
+
131
+ if y_position < 50:
132
+ pdf.showPage()
133
+ pdf.setFont("Helvetica", 12)
134
+ y_position = 750
135
+
136
+ if line:
137
+ pdf.drawString(72, y_position, ' '.join(line))
138
+
139
+ pdf.save()
140
+ buffer.seek(0)
141
+ return buffer
142
+
143
+
144
+ @app.route('/')
145
+ def index():
146
+ return render_template('index.html')
147
+
148
+
149
+ @app.route('/upload', methods=['POST'])
150
+ def upload_pdf():
151
+ if 'pdf_file' not in request.files:
152
+ return jsonify({'error': 'No file uploaded'}), 400
153
+
154
+ file = request.files['pdf_file']
155
+
156
+ if file.filename == '':
157
+ return jsonify({'error': 'No file selected'}), 400
158
+
159
+ if not allowed_file(file.filename):
160
+ return jsonify({'error': 'Only PDF files are allowed'}), 400
161
+
162
+ try:
163
+ text = extract_text_from_pdf(file)
164
+
165
+ if not text.strip():
166
+ return jsonify({'error': 'Could not extract text from PDF. The PDF might be scanned or image-based.'}), 400
167
+
168
+ summary_ratio = float(request.form.get('summary_ratio', 0.3))
169
+ total_sentences = len(sent_tokenize(text))
170
+ num_sentences = max(3, int(total_sentences * summary_ratio))
171
+
172
+ summary = summarize_text(text, num_sentences)
173
+ stats = analyze_pdf_statistics(text)
174
+
175
+ original_words = len(word_tokenize(text))
176
+ summary_words = len(word_tokenize(summary))
177
+
178
+ compression_ratio = (
179
+ ((original_words - summary_words) / original_words) * 100
180
+ if original_words > 0 else 0
181
+ )
182
+
183
+ return jsonify({
184
+ 'success': True,
185
+ 'summary': summary,
186
+ 'statistics': stats,
187
+ 'compression_ratio': round(compression_ratio, 2),
188
+ 'summary_length': summary_words,
189
+ 'filename': secure_filename(file.filename)
190
+ })
191
+
192
+ except Exception as e:
193
+ return jsonify({'error': f'Error processing PDF: {str(e)}'}), 500
194
+
195
+
196
+ @app.route('/download', methods=['POST'])
197
+ def download_summary():
198
+ data = request.json
199
+ summary = data.get('summary', '')
200
+ filename = data.get('filename', 'summary.pdf')
201
+
202
+ if not summary:
203
+ return jsonify({'error': 'No summary to download'}), 400
204
+
205
+ try:
206
+ pdf_buffer = create_summary_pdf(summary, filename)
207
+
208
+ return send_file(
209
+ pdf_buffer,
210
+ as_attachment=True,
211
+ download_name=filename.replace('.pdf', '_summary.pdf'),
212
+ mimetype='application/pdf'
213
+ )
214
+
215
+ except Exception as e:
216
+ return jsonify({'error': f'Error creating PDF: {str(e)}'}), 500
217
+
218
+
219
+ @app.route('/export', methods=['POST'])
220
+ def export_summary():
221
+ data = request.json
222
+ summary = data.get('summary', '')
223
+
224
+ if not summary:
225
+ return jsonify({'error': 'No summary to export'}), 400
226
+
227
+ return send_file(
228
+ BytesIO(summary.encode()),
229
+ as_attachment=True,
230
+ download_name='summary.txt',
231
+ mimetype='text/plain'
232
+ )
233
+
234
+
235
+ if __name__ == '__main__':
236
+ os.makedirs('uploads', exist_ok=True)
237
+ os.makedirs('templates', exist_ok=True)
238
+ os.makedirs('static', exist_ok=True)
239
+
240
+ print("Starting PDF Summarizer Server...")
241
+ print("Open your browser and navigate to: http://localhost:5000")
242
+
243
+ app.run(debug=True, host='0.0.0.0', port=5000)
static/style.css ADDED
@@ -0,0 +1,645 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ body {
8
+ font-family: 'Poppins', sans-serif;
9
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
10
+ min-height: 100vh;
11
+ color: #333;
12
+ }
13
+
14
+ .container {
15
+ max-width: 1400px;
16
+ margin: 0 auto;
17
+ padding: 20px;
18
+ }
19
+
20
+ /* Header */
21
+ .header {
22
+ text-align: center;
23
+ margin-bottom: 30px;
24
+ padding: 20px;
25
+ background: white;
26
+ border-radius: 15px;
27
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
28
+ }
29
+
30
+ .logo {
31
+ display: flex;
32
+ align-items: center;
33
+ justify-content: center;
34
+ gap: 15px;
35
+ margin-bottom: 10px;
36
+ }
37
+
38
+ .logo i {
39
+ font-size: 2.5rem;
40
+ color: #e74c3c;
41
+ }
42
+
43
+ .logo h1 {
44
+ font-size: 2.2rem;
45
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
46
+ -webkit-background-clip: text;
47
+ -webkit-text-fill-color: transparent;
48
+ }
49
+
50
+ .tagline {
51
+ color: #666;
52
+ font-size: 1.1rem;
53
+ }
54
+
55
+ /* Main Content */
56
+ .main-content {
57
+ display: grid;
58
+ grid-template-columns: 1fr 1.5fr;
59
+ gap: 25px;
60
+ margin-bottom: 30px;
61
+ }
62
+
63
+ @media (max-width: 1024px) {
64
+ .main-content {
65
+ grid-template-columns: 1fr;
66
+ }
67
+ }
68
+
69
+ /* Cards */
70
+ .card {
71
+ background: white;
72
+ border-radius: 15px;
73
+ padding: 25px;
74
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
75
+ margin-bottom: 25px;
76
+ }
77
+
78
+ .card h2 {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: 10px;
82
+ color: #2c3e50;
83
+ margin-bottom: 20px;
84
+ font-size: 1.4rem;
85
+ }
86
+
87
+ .card h2 i {
88
+ color: #3498db;
89
+ }
90
+
91
+ /* Upload Section */
92
+ .upload-section .description {
93
+ color: #666;
94
+ margin-bottom: 20px;
95
+ font-size: 0.95rem;
96
+ }
97
+
98
+ .upload-area {
99
+ border: 3px dashed #3498db;
100
+ border-radius: 12px;
101
+ padding: 40px 20px;
102
+ text-align: center;
103
+ cursor: pointer;
104
+ transition: all 0.3s ease;
105
+ background: #f8fafc;
106
+ display: flex;
107
+ flex-direction: column;
108
+ align-items: center;
109
+ gap: 15px;
110
+ }
111
+
112
+ .upload-area:hover {
113
+ background: #e3f2fd;
114
+ border-color: #2980b9;
115
+ }
116
+
117
+ .upload-area.highlight {
118
+ background: #e1f5fe;
119
+ border-color: #0288d1;
120
+ }
121
+
122
+ .upload-icon {
123
+ font-size: 3rem;
124
+ color: #3498db;
125
+ }
126
+
127
+ .upload-text {
128
+ font-size: 1.2rem;
129
+ font-weight: 500;
130
+ color: #2c3e50;
131
+ }
132
+
133
+ .upload-subtext {
134
+ color: #666;
135
+ font-size: 0.9rem;
136
+ }
137
+
138
+ .file-info {
139
+ color: #7f8c8d;
140
+ font-size: 0.85rem;
141
+ margin-top: 10px;
142
+ }
143
+
144
+ /* File Info */
145
+ .file-info-container {
146
+ margin-top: 20px;
147
+ }
148
+
149
+ .selected-file {
150
+ display: flex;
151
+ align-items: center;
152
+ gap: 12px;
153
+ background: #e8f4fc;
154
+ padding: 15px;
155
+ border-radius: 10px;
156
+ border-left: 4px solid #3498db;
157
+ }
158
+
159
+ .selected-file i {
160
+ color: #e74c3c;
161
+ font-size: 1.2rem;
162
+ }
163
+
164
+ .selected-file span {
165
+ flex: 1;
166
+ font-weight: 500;
167
+ color: #2c3e50;
168
+ }
169
+
170
+ .btn-remove {
171
+ background: #ff6b6b;
172
+ color: white;
173
+ border: none;
174
+ width: 30px;
175
+ height: 30px;
176
+ border-radius: 50%;
177
+ cursor: pointer;
178
+ transition: background 0.3s;
179
+ }
180
+
181
+ .btn-remove:hover {
182
+ background: #ff5252;
183
+ }
184
+
185
+ /* Controls */
186
+ .controls-section {
187
+ margin-top: 20px;
188
+ }
189
+
190
+ .control-group {
191
+ margin-bottom: 25px;
192
+ }
193
+
194
+ .control-group label {
195
+ display: flex;
196
+ justify-content: space-between;
197
+ align-items: center;
198
+ margin-bottom: 10px;
199
+ font-weight: 500;
200
+ color: #2c3e50;
201
+ }
202
+
203
+ #summaryRatio {
204
+ width: 100%;
205
+ height: 8px;
206
+ -webkit-appearance: none;
207
+ background: linear-gradient(to right, #4CAF50, #FFC107, #F44336);
208
+ border-radius: 4px;
209
+ outline: none;
210
+ }
211
+
212
+ #summaryRatio::-webkit-slider-thumb {
213
+ -webkit-appearance: none;
214
+ width: 22px;
215
+ height: 22px;
216
+ background: #3498db;
217
+ border-radius: 50%;
218
+ cursor: pointer;
219
+ border: 3px solid white;
220
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
221
+ }
222
+
223
+ .range-labels {
224
+ display: flex;
225
+ justify-content: space-between;
226
+ margin-top: 8px;
227
+ color: #666;
228
+ font-size: 0.9rem;
229
+ }
230
+
231
+ /* Buttons */
232
+ .btn-group {
233
+ display: flex;
234
+ gap: 15px;
235
+ margin-top: 25px;
236
+ }
237
+
238
+ .btn {
239
+ padding: 14px 28px;
240
+ border: none;
241
+ border-radius: 10px;
242
+ cursor: pointer;
243
+ font-weight: 600;
244
+ font-size: 1rem;
245
+ transition: all 0.3s ease;
246
+ display: flex;
247
+ align-items: center;
248
+ justify-content: center;
249
+ gap: 10px;
250
+ flex: 1;
251
+ }
252
+
253
+ .btn-primary {
254
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
255
+ color: white;
256
+ }
257
+
258
+ .btn-primary:hover:not(:disabled) {
259
+ transform: translateY(-2px);
260
+ box-shadow: 0 7px 14px rgba(102, 126, 234, 0.3);
261
+ }
262
+
263
+ .btn-primary:disabled {
264
+ opacity: 0.5;
265
+ cursor: not-allowed;
266
+ }
267
+
268
+ .btn-secondary {
269
+ background: #ecf0f1;
270
+ color: #2c3e50;
271
+ }
272
+
273
+ .btn-secondary:hover {
274
+ background: #d5dbdb;
275
+ transform: translateY(-2px);
276
+ }
277
+
278
+ .btn-download {
279
+ background: #2ecc71;
280
+ color: white;
281
+ padding: 10px 20px;
282
+ font-size: 0.9rem;
283
+ }
284
+
285
+ .btn-download:hover {
286
+ background: #27ae60;
287
+ }
288
+
289
+ .btn-copy {
290
+ background: #3498db;
291
+ color: white;
292
+ padding: 10px 20px;
293
+ font-size: 0.9rem;
294
+ }
295
+
296
+ .btn-copy:hover {
297
+ background: #2980b9;
298
+ }
299
+
300
+ /* Results Section */
301
+ .results-header {
302
+ display: flex;
303
+ justify-content: space-between;
304
+ align-items: center;
305
+ margin-bottom: 20px;
306
+ flex-wrap: wrap;
307
+ gap: 15px;
308
+ }
309
+
310
+ .actions {
311
+ display: flex;
312
+ gap: 10px;
313
+ flex-wrap: wrap;
314
+ }
315
+
316
+ .results-content {
317
+ min-height: 400px;
318
+ position: relative;
319
+ }
320
+
321
+ .placeholder-content {
322
+ text-align: center;
323
+ padding: 60px 20px;
324
+ color: #7f8c8d;
325
+ }
326
+
327
+ .placeholder-content i {
328
+ font-size: 4rem;
329
+ color: #bdc3c7;
330
+ margin-bottom: 20px;
331
+ }
332
+
333
+ .placeholder-content h3 {
334
+ font-size: 1.5rem;
335
+ margin-bottom: 10px;
336
+ color: #95a5a6;
337
+ }
338
+
339
+ .summary-header {
340
+ margin-bottom: 25px;
341
+ padding-bottom: 15px;
342
+ border-bottom: 2px solid #ecf0f1;
343
+ }
344
+
345
+ .summary-header h3 {
346
+ color: #2c3e50;
347
+ font-size: 1.3rem;
348
+ margin-bottom: 15px;
349
+ }
350
+
351
+ .summary-meta {
352
+ display: flex;
353
+ gap: 15px;
354
+ flex-wrap: wrap;
355
+ }
356
+
357
+ .badge {
358
+ display: inline-flex;
359
+ align-items: center;
360
+ gap: 6px;
361
+ padding: 6px 15px;
362
+ border-radius: 20px;
363
+ font-size: 0.85rem;
364
+ font-weight: 500;
365
+ }
366
+
367
+ .badge-high {
368
+ background: #ffebee;
369
+ color: #c62828;
370
+ }
371
+
372
+ .badge-medium {
373
+ background: #fff3e0;
374
+ color: #ef6c00;
375
+ }
376
+
377
+ .badge-low {
378
+ background: #e8f5e9;
379
+ color: #2e7d32;
380
+ }
381
+
382
+ .badge i {
383
+ font-size: 0.9rem;
384
+ }
385
+
386
+ .summary-text {
387
+ line-height: 1.8;
388
+ color: #34495e;
389
+ font-size: 1.05rem;
390
+ white-space: pre-wrap;
391
+ max-height: 500px;
392
+ overflow-y: auto;
393
+ padding: 15px;
394
+ background: #f8f9fa;
395
+ border-radius: 10px;
396
+ border-left: 4px solid #3498db;
397
+ }
398
+
399
+ /* Statistics */
400
+ .stats-section .stats-placeholder {
401
+ text-align: center;
402
+ padding: 40px 20px;
403
+ color: #95a5a6;
404
+ }
405
+
406
+ .stats-section .stats-placeholder i {
407
+ font-size: 3rem;
408
+ margin-bottom: 15px;
409
+ color: #bdc3c7;
410
+ }
411
+
412
+ .stats-grid {
413
+ display: grid;
414
+ grid-template-columns: repeat(2, 1fr);
415
+ gap: 20px;
416
+ }
417
+
418
+ .stat-item {
419
+ display: flex;
420
+ align-items: center;
421
+ gap: 15px;
422
+ padding: 15px;
423
+ background: #f8fafc;
424
+ border-radius: 10px;
425
+ transition: transform 0.3s;
426
+ }
427
+
428
+ .stat-item:hover {
429
+ transform: translateY(-3px);
430
+ background: #e3f2fd;
431
+ }
432
+
433
+ .stat-icon {
434
+ width: 50px;
435
+ height: 50px;
436
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
437
+ border-radius: 10px;
438
+ display: flex;
439
+ align-items: center;
440
+ justify-content: center;
441
+ color: white;
442
+ font-size: 1.2rem;
443
+ }
444
+
445
+ .stat-info {
446
+ display: flex;
447
+ flex-direction: column;
448
+ }
449
+
450
+ .stat-label {
451
+ font-size: 0.85rem;
452
+ color: #7f8c8d;
453
+ margin-bottom: 4px;
454
+ }
455
+
456
+ .stat-value {
457
+ font-size: 1.5rem;
458
+ font-weight: 600;
459
+ color: #2c3e50;
460
+ }
461
+
462
+ /* Tips Section */
463
+ .tips-list {
464
+ list-style: none;
465
+ padding: 0;
466
+ }
467
+
468
+ .tips-list li {
469
+ display: flex;
470
+ align-items: center;
471
+ gap: 12px;
472
+ padding: 12px 0;
473
+ border-bottom: 1px solid #ecf0f1;
474
+ color: #555;
475
+ }
476
+
477
+ .tips-list li:last-child {
478
+ border-bottom: none;
479
+ }
480
+
481
+ .tips-list i {
482
+ color: #2ecc71;
483
+ font-size: 1rem;
484
+ }
485
+
486
+ /* Error Message */
487
+ .error-message {
488
+ text-align: center;
489
+ padding: 40px 20px;
490
+ color: #c0392b;
491
+ }
492
+
493
+ .error-message i {
494
+ font-size: 3rem;
495
+ margin-bottom: 20px;
496
+ }
497
+
498
+ .error-message h3 {
499
+ margin-bottom: 15px;
500
+ font-size: 1.4rem;
501
+ }
502
+
503
+ /* Loader */
504
+ .loader {
505
+ position: absolute;
506
+ top: 0;
507
+ left: 0;
508
+ right: 0;
509
+ bottom: 0;
510
+ background: rgba(255, 255, 255, 0.9);
511
+ display: flex;
512
+ flex-direction: column;
513
+ align-items: center;
514
+ justify-content: center;
515
+ border-radius: 15px;
516
+ z-index: 10;
517
+ }
518
+
519
+ .spinner {
520
+ width: 50px;
521
+ height: 50px;
522
+ border: 5px solid #f3f3f3;
523
+ border-top: 5px solid #3498db;
524
+ border-radius: 50%;
525
+ animation: spin 1s linear infinite;
526
+ margin-bottom: 20px;
527
+ }
528
+
529
+ @keyframes spin {
530
+ 0% { transform: rotate(0deg); }
531
+ 100% { transform: rotate(360deg); }
532
+ }
533
+
534
+ .loader p {
535
+ color: #3498db;
536
+ font-weight: 500;
537
+ font-size: 1.1rem;
538
+ }
539
+
540
+ /* Toast */
541
+ .toast {
542
+ position: fixed;
543
+ bottom: 30px;
544
+ right: 30px;
545
+ padding: 15px 25px;
546
+ border-radius: 10px;
547
+ color: white;
548
+ font-weight: 500;
549
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
550
+ transform: translateY(100px);
551
+ opacity: 0;
552
+ transition: all 0.3s ease;
553
+ z-index: 1000;
554
+ max-width: 350px;
555
+ }
556
+
557
+ .toast.show {
558
+ transform: translateY(0);
559
+ opacity: 1;
560
+ }
561
+
562
+ .toast-success {
563
+ background: linear-gradient(135deg, #2ecc71 0%, #27ae60 100%);
564
+ }
565
+
566
+ .toast-error {
567
+ background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
568
+ }
569
+
570
+ .toast-info {
571
+ background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
572
+ }
573
+
574
+ /* Footer */
575
+ .footer {
576
+ text-align: center;
577
+ padding: 20px;
578
+ color: #7f8c8d;
579
+ font-size: 0.9rem;
580
+ background: white;
581
+ border-radius: 15px;
582
+ margin-top: 20px;
583
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
584
+ }
585
+
586
+ .footer i {
587
+ color: #e74c3c;
588
+ margin: 0 5px;
589
+ }
590
+
591
+ /* Responsive */
592
+ @media (max-width: 768px) {
593
+ .container {
594
+ padding: 15px;
595
+ }
596
+
597
+ .logo h1 {
598
+ font-size: 1.8rem;
599
+ }
600
+
601
+ .main-content {
602
+ gap: 15px;
603
+ }
604
+
605
+ .card {
606
+ padding: 20px;
607
+ }
608
+
609
+ .btn-group {
610
+ flex-direction: column;
611
+ }
612
+
613
+ .stats-grid {
614
+ grid-template-columns: 1fr;
615
+ }
616
+
617
+ .actions {
618
+ flex-direction: column;
619
+ width: 100%;
620
+ }
621
+
622
+ .btn-download, .btn-copy {
623
+ width: 100%;
624
+ }
625
+ }
626
+
627
+ /* Scrollbar Styling */
628
+ ::-webkit-scrollbar {
629
+ width: 8px;
630
+ height: 8px;
631
+ }
632
+
633
+ ::-webkit-scrollbar-track {
634
+ background: #f1f1f1;
635
+ border-radius: 4px;
636
+ }
637
+
638
+ ::-webkit-scrollbar-thumb {
639
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
640
+ border-radius: 4px;
641
+ }
642
+
643
+ ::-webkit-scrollbar-thumb:hover {
644
+ background: linear-gradient(135deg, #5a6fd8 0%, #6b4198 100%);
645
+ }
templates/index.html ADDED
@@ -0,0 +1,518 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>PDF Summarizer | AI-Powered Document Analysis</title>
7
+ <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
10
+ </head>
11
+ <body>
12
+ <div class="container">
13
+ <!-- Header -->
14
+ <header class="header">
15
+ <div class="logo">
16
+ <i class="fas fa-file-pdf"></i>
17
+ <h1>PDF Summarizer</h1>
18
+ </div>
19
+ <p class="tagline">Extract key insights from your documents in seconds</p>
20
+ </header>
21
+
22
+ <!-- Main Content -->
23
+ <div class="main-content">
24
+ <!-- Left Panel - Upload & Controls -->
25
+ <div class="left-panel">
26
+ <div class="upload-section card">
27
+ <h2><i class="fas fa-upload"></i> Upload PDF</h2>
28
+ <p class="description">Upload your PDF document to generate an AI-powered summary</p>
29
+
30
+ <div class="upload-area" id="dropArea">
31
+ <i class="fas fa-cloud-upload-alt upload-icon"></i>
32
+ <p class="upload-text">Drag & Drop your PDF file here</p>
33
+ <p class="upload-subtext">or click to browse</p>
34
+ <input type="file" id="pdfInput" accept=".pdf" hidden>
35
+ <button class="btn btn-primary" id="browseBtn">
36
+ <i class="fas fa-folder-open"></i> Browse Files
37
+ </button>
38
+ <p class="file-info">Max file size: 16MB</p>
39
+ </div>
40
+
41
+ <div id="fileInfo" class="file-info-container" style="display: none;">
42
+ <div class="selected-file">
43
+ <i class="fas fa-file-pdf"></i>
44
+ <span id="fileName"></span>
45
+ <button class="btn-remove" id="removeFile">
46
+ <i class="fas fa-times"></i>
47
+ </button>
48
+ </div>
49
+ </div>
50
+ </div>
51
+
52
+ <!-- Controls Section -->
53
+ <div class="controls-section card">
54
+ <h2><i class="fas fa-sliders-h"></i> Summary Settings</h2>
55
+
56
+ <div class="control-group">
57
+ <label for="summaryRatio">
58
+ <i class="fas fa-compress-alt"></i> Summary Ratio
59
+ <span id="ratioValue">30%</span>
60
+ </label>
61
+ <input type="range" id="summaryRatio" min="10" max="70" value="30" step="5">
62
+ <div class="range-labels">
63
+ <span>Brief</span>
64
+ <span>Detailed</span>
65
+ </div>
66
+ </div>
67
+
68
+ <div class="btn-group">
69
+ <button class="btn btn-primary" id="summarizeBtn" disabled>
70
+ <i class="fas fa-magic"></i> Generate Summary
71
+ </button>
72
+ <button class="btn btn-secondary" id="resetBtn">
73
+ <i class="fas fa-redo"></i> Reset
74
+ </button>
75
+ </div>
76
+ </div>
77
+
78
+ <!-- Statistics Section -->
79
+ <div class="stats-section card">
80
+ <h2><i class="fas fa-chart-bar"></i> Document Statistics</h2>
81
+ <div id="statsContent">
82
+ <div class="stats-placeholder">
83
+ <i class="fas fa-chart-pie"></i>
84
+ <p>Upload a PDF to view statistics</p>
85
+ </div>
86
+ </div>
87
+ </div>
88
+ </div>
89
+
90
+ <!-- Right Panel - Results -->
91
+ <div class="right-panel">
92
+ <div class="results-section card">
93
+ <div class="results-header">
94
+ <h2><i class="fas fa-file-alt"></i> Summary</h2>
95
+ <div class="actions" id="downloadActions" style="display: none;">
96
+ <button class="btn btn-download" id="downloadPdf">
97
+ <i class="fas fa-file-pdf"></i> Save as PDF
98
+ </button>
99
+ <button class="btn btn-download" id="downloadTxt">
100
+ <i class="fas fa-file-text"></i> Save as TXT
101
+ </button>
102
+ <button class="btn btn-copy" id="copyBtn">
103
+ <i class="far fa-copy"></i> Copy
104
+ </button>
105
+ </div>
106
+ </div>
107
+
108
+ <div class="results-content">
109
+ <div id="summaryPlaceholder">
110
+ <div class="placeholder-content">
111
+ <i class="fas fa-search"></i>
112
+ <h3>No Summary Yet</h3>
113
+ <p>Upload a PDF file and click "Generate Summary" to see the results here.</p>
114
+ </div>
115
+ </div>
116
+ <div id="summaryContent" style="display: none;">
117
+ <div class="summary-header">
118
+ <h3 id="summaryTitle"></h3>
119
+ <div class="summary-meta">
120
+ <span class="badge" id="compressionBadge">
121
+ <i class="fas fa-compress-alt"></i> <span id="compressionValue">0%</span> compressed
122
+ </span>
123
+ <span class="badge" id="lengthBadge">
124
+ <i class="fas fa-text-width"></i> <span id="wordCount">0</span> words
125
+ </span>
126
+ </div>
127
+ </div>
128
+ <div class="summary-text" id="summaryText"></div>
129
+ </div>
130
+
131
+ <div id="errorMessage" class="error-message" style="display: none;">
132
+ <i class="fas fa-exclamation-triangle"></i>
133
+ <h3>Error Processing PDF</h3>
134
+ <p id="errorText"></p>
135
+ </div>
136
+ </div>
137
+
138
+ <div class="loader" id="loader" style="display: none;">
139
+ <div class="spinner"></div>
140
+ <p>Analyzing your document...</p>
141
+ </div>
142
+ </div>
143
+
144
+ <!-- Tips Section -->
145
+ <div class="tips-section card">
146
+ <h2><i class="fas fa-lightbulb"></i> Tips for Better Results</h2>
147
+ <ul class="tips-list">
148
+ <li><i class="fas fa-check-circle"></i> Use text-based PDFs for best results</li>
149
+ <li><i class="fas fa-check-circle"></i> For scanned PDFs, use OCR software first</li>
150
+ <li><i class="fas fa-check-circle"></i> Adjust summary ratio based on document length</li>
151
+ <li><i class="fas fa-check-circle"></i> Longer documents may take more time to process</li>
152
+ <li><i class="fas fa-check-circle"></i> Results are more accurate with well-structured documents</li>
153
+ </ul>
154
+ </div>
155
+ </div>
156
+ </div>
157
+
158
+ <!-- Footer -->
159
+ <footer class="footer">
160
+ <p>© 2024 PDF Summarizer | Built with Flask & NLTK | <i class="fas fa-heart"></i> Made with passion</p>
161
+ </footer>
162
+ </div>
163
+
164
+ <!-- Notification Toast -->
165
+ <div id="toast" class="toast"></div>
166
+
167
+ <script>
168
+ // DOM Elements
169
+ const pdfInput = document.getElementById('pdfInput');
170
+ const browseBtn = document.getElementById('browseBtn');
171
+ const dropArea = document.getElementById('dropArea');
172
+ const summarizeBtn = document.getElementById('summarizeBtn');
173
+ const resetBtn = document.getElementById('resetBtn');
174
+ const summaryRatio = document.getElementById('summaryRatio');
175
+ const ratioValue = document.getElementById('ratioValue');
176
+ const fileInfo = document.getElementById('fileInfo');
177
+ const fileName = document.getElementById('fileName');
178
+ const removeFile = document.getElementById('removeFile');
179
+ const summaryPlaceholder = document.getElementById('summaryPlaceholder');
180
+ const summaryContent = document.getElementById('summaryContent');
181
+ const summaryText = document.getElementById('summaryText');
182
+ const summaryTitle = document.getElementById('summaryTitle');
183
+ const compressionValue = document.getElementById('compressionValue');
184
+ const wordCount = document.getElementById('wordCount');
185
+ const downloadActions = document.getElementById('downloadActions');
186
+ const downloadPdf = document.getElementById('downloadPdf');
187
+ const downloadTxt = document.getElementById('downloadTxt');
188
+ const copyBtn = document.getElementById('copyBtn');
189
+ const errorMessage = document.getElementById('errorMessage');
190
+ const errorText = document.getElementById('errorText');
191
+ const loader = document.getElementById('loader');
192
+ const statsContent = document.getElementById('statsContent');
193
+ const toast = document.getElementById('toast');
194
+
195
+ let currentFile = null;
196
+
197
+ // Event Listeners
198
+ browseBtn.addEventListener('click', () => pdfInput.click());
199
+ pdfInput.addEventListener('change', handleFileSelect);
200
+ summarizeBtn.addEventListener('click', processPDF);
201
+ resetBtn.addEventListener('click', resetAll);
202
+ removeFile.addEventListener('click', removeSelectedFile);
203
+ summaryRatio.addEventListener('input', updateRatioValue);
204
+ downloadPdf.addEventListener('click', () => downloadSummary('pdf'));
205
+ downloadTxt.addEventListener('click', () => downloadSummary('txt'));
206
+ copyBtn.addEventListener('click', copySummary);
207
+
208
+ // Drag and Drop functionality
209
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
210
+ dropArea.addEventListener(eventName, preventDefaults, false);
211
+ });
212
+
213
+ ['dragenter', 'dragover'].forEach(eventName => {
214
+ dropArea.addEventListener(eventName, highlight, false);
215
+ });
216
+
217
+ ['dragleave', 'drop'].forEach(eventName => {
218
+ dropArea.addEventListener(eventName, unhighlight, false);
219
+ });
220
+
221
+ dropArea.addEventListener('drop', handleDrop, false);
222
+
223
+ // Functions
224
+ function preventDefaults(e) {
225
+ e.preventDefault();
226
+ e.stopPropagation();
227
+ }
228
+
229
+ function highlight() {
230
+ dropArea.classList.add('highlight');
231
+ }
232
+
233
+ function unhighlight() {
234
+ dropArea.classList.remove('highlight');
235
+ }
236
+
237
+ function handleDrop(e) {
238
+ const dt = e.dataTransfer;
239
+ const file = dt.files[0];
240
+
241
+ if (file && file.type === 'application/pdf') {
242
+ handleFile(file);
243
+ } else {
244
+ showToast('Please drop a valid PDF file', 'error');
245
+ }
246
+ }
247
+
248
+ function handleFileSelect(e) {
249
+ const file = e.target.files[0];
250
+ if (file) {
251
+ handleFile(file);
252
+ }
253
+ }
254
+
255
+ function handleFile(file) {
256
+ if (file.size > 16 * 1024 * 1024) {
257
+ showToast('File size exceeds 16MB limit', 'error');
258
+ return;
259
+ }
260
+
261
+ currentFile = file;
262
+ fileName.textContent = file.name;
263
+ fileInfo.style.display = 'block';
264
+ summarizeBtn.disabled = false;
265
+ dropArea.style.display = 'none';
266
+
267
+ showToast('PDF uploaded successfully!', 'success');
268
+ }
269
+
270
+ function removeSelectedFile() {
271
+ currentFile = null;
272
+ pdfInput.value = '';
273
+ fileInfo.style.display = 'none';
274
+ summarizeBtn.disabled = true;
275
+ dropArea.style.display = 'flex';
276
+ }
277
+
278
+ function updateRatioValue() {
279
+ const value = summaryRatio.value;
280
+ ratioValue.textContent = `${value}%`;
281
+ }
282
+
283
+ async function processPDF() {
284
+ if (!currentFile) return;
285
+
286
+ showLoader(true);
287
+ hideError();
288
+ hideSummary();
289
+
290
+ const formData = new FormData();
291
+ formData.append('pdf_file', currentFile);
292
+ formData.append('summary_ratio', summaryRatio.value / 100);
293
+
294
+ try {
295
+ const response = await fetch('/upload', {
296
+ method: 'POST',
297
+ body: formData
298
+ });
299
+
300
+ const data = await response.json();
301
+
302
+ if (data.success) {
303
+ displaySummary(data);
304
+ displayStatistics(data.statistics, data.compression_ratio, data.summary_length);
305
+ showToast('Summary generated successfully!', 'success');
306
+ } else {
307
+ showError(data.error || 'Failed to process PDF');
308
+ }
309
+ } catch (error) {
310
+ showError('Network error. Please try again.');
311
+ } finally {
312
+ showLoader(false);
313
+ }
314
+ }
315
+
316
+ function displaySummary(data) {
317
+ summaryTitle.textContent = `Summary of ${data.filename}`;
318
+ summaryText.textContent = data.summary;
319
+ compressionValue.textContent = `${data.compression_ratio}%`;
320
+ wordCount.textContent = data.summary_length;
321
+
322
+ summaryPlaceholder.style.display = 'none';
323
+ summaryContent.style.display = 'block';
324
+ downloadActions.style.display = 'flex';
325
+
326
+ // Update badges based on compression
327
+ const compressionBadge = document.getElementById('compressionBadge');
328
+ if (data.compression_ratio > 70) {
329
+ compressionBadge.className = 'badge badge-high';
330
+ } else if (data.compression_ratio > 40) {
331
+ compressionBadge.className = 'badge badge-medium';
332
+ } else {
333
+ compressionBadge.className = 'badge badge-low';
334
+ }
335
+ }
336
+
337
+ function displayStatistics(stats, compressionRatio, summaryLength) {
338
+ statsContent.innerHTML = `
339
+ <div class="stats-grid">
340
+ <div class="stat-item">
341
+ <div class="stat-icon">
342
+ <i class="fas fa-file"></i>
343
+ </div>
344
+ <div class="stat-info">
345
+ <span class="stat-label">Pages</span>
346
+ <span class="stat-value">${stats.page_count}</span>
347
+ </div>
348
+ </div>
349
+ <div class="stat-item">
350
+ <div class="stat-icon">
351
+ <i class="fas fa-paragraph"></i>
352
+ </div>
353
+ <div class="stat-info">
354
+ <span class="stat-label">Sentences</span>
355
+ <span class="stat-value">${stats.sentence_count}</span>
356
+ </div>
357
+ </div>
358
+ <div class="stat-item">
359
+ <div class="stat-icon">
360
+ <i class="fas fa-font"></i>
361
+ </div>
362
+ <div class="stat-info">
363
+ <span class="stat-label">Words</span>
364
+ <span class="stat-value">${stats.word_count}</span>
365
+ </div>
366
+ </div>
367
+ <div class="stat-item">
368
+ <div class="stat-icon">
369
+ <i class="fas fa-ruler"></i>
370
+ </div>
371
+ <div class="stat-info">
372
+ <span class="stat-label">Avg. Word Length</span>
373
+ <span class="stat-value">${stats.avg_word_length.toFixed(1)}</span>
374
+ </div>
375
+ </div>
376
+ <div class="stat-item">
377
+ <div class="stat-icon">
378
+ <i class="fas fa-compress-alt"></i>
379
+ </div>
380
+ <div class="stat-info">
381
+ <span class="stat-label">Compression</span>
382
+ <span class="stat-value">${compressionRatio}%</span>
383
+ </div>
384
+ </div>
385
+ <div class="stat-item">
386
+ <div class="stat-icon">
387
+ <i class="fas fa-text-height"></i>
388
+ </div>
389
+ <div class="stat-info">
390
+ <span class="stat-label">Summary Words</span>
391
+ <span class="stat-value">${summaryLength}</span>
392
+ </div>
393
+ </div>
394
+ </div>
395
+ `;
396
+ }
397
+
398
+ async function downloadSummary(format) {
399
+ if (!summaryText.textContent) return;
400
+
401
+ try {
402
+ if (format === 'pdf') {
403
+ const response = await fetch('/download', {
404
+ method: 'POST',
405
+ headers: {
406
+ 'Content-Type': 'application/json',
407
+ },
408
+ body: JSON.stringify({
409
+ summary: summaryText.textContent,
410
+ filename: currentFile.name
411
+ })
412
+ });
413
+
414
+ if (response.ok) {
415
+ const blob = await response.blob();
416
+ const url = window.URL.createObjectURL(blob);
417
+ const a = document.createElement('a');
418
+ a.href = url;
419
+ a.download = currentFile.name.replace('.pdf', '_summary.pdf');
420
+ document.body.appendChild(a);
421
+ a.click();
422
+ window.URL.revokeObjectURL(url);
423
+ document.body.removeChild(a);
424
+ showToast('PDF downloaded successfully!', 'success');
425
+ }
426
+ } else if (format === 'txt') {
427
+ const response = await fetch('/export', {
428
+ method: 'POST',
429
+ headers: {
430
+ 'Content-Type': 'application/json',
431
+ },
432
+ body: JSON.stringify({
433
+ summary: summaryText.textContent
434
+ })
435
+ });
436
+
437
+ if (response.ok) {
438
+ const blob = await response.blob();
439
+ const url = window.URL.createObjectURL(blob);
440
+ const a = document.createElement('a');
441
+ a.href = url;
442
+ a.download = 'summary.txt';
443
+ document.body.appendChild(a);
444
+ a.click();
445
+ window.URL.revokeObjectURL(url);
446
+ document.body.removeChild(a);
447
+ showToast('Text file downloaded successfully!', 'success');
448
+ }
449
+ }
450
+ } catch (error) {
451
+ showToast('Failed to download file', 'error');
452
+ }
453
+ }
454
+
455
+ function copySummary() {
456
+ const text = summaryText.textContent;
457
+ navigator.clipboard.writeText(text)
458
+ .then(() => showToast('Summary copied to clipboard!', 'success'))
459
+ .catch(() => showToast('Failed to copy text', 'error'));
460
+ }
461
+
462
+ function resetAll() {
463
+ currentFile = null;
464
+ pdfInput.value = '';
465
+ fileInfo.style.display = 'none';
466
+ summarizeBtn.disabled = true;
467
+ dropArea.style.display = 'flex';
468
+ hideSummary();
469
+ hideError();
470
+ showLoader(false);
471
+
472
+ statsContent.innerHTML = `
473
+ <div class="stats-placeholder">
474
+ <i class="fas fa-chart-pie"></i>
475
+ <p>Upload a PDF to view statistics</p>
476
+ </div>
477
+ `;
478
+
479
+ summaryRatio.value = 30;
480
+ updateRatioValue();
481
+
482
+ showToast('All fields have been reset', 'info');
483
+ }
484
+
485
+ function hideSummary() {
486
+ summaryPlaceholder.style.display = 'flex';
487
+ summaryContent.style.display = 'none';
488
+ downloadActions.style.display = 'none';
489
+ }
490
+
491
+ function showError(message) {
492
+ errorText.textContent = message;
493
+ errorMessage.style.display = 'block';
494
+ hideSummary();
495
+ }
496
+
497
+ function hideError() {
498
+ errorMessage.style.display = 'none';
499
+ }
500
+
501
+ function showLoader(show) {
502
+ loader.style.display = show ? 'flex' : 'none';
503
+ }
504
+
505
+ function showToast(message, type = 'info') {
506
+ toast.textContent = message;
507
+ toast.className = `toast toast-${type} show`;
508
+
509
+ setTimeout(() => {
510
+ toast.classList.remove('show');
511
+ }, 3000);
512
+ }
513
+
514
+ // Initialize
515
+ updateRatioValue();
516
+ </script>
517
+ </body>
518
+ </html>