lokesh341 commited on
Commit
4e9c018
·
verified ·
1 Parent(s): b27d5c5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +111 -124
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py - Fixed PDF Generator with proper temp file handling
2
 
3
  import gradio as gr
4
  from fpdf import FPDF
@@ -6,188 +6,170 @@ import os
6
  import tempfile
7
  import re
8
  import pandas as pd
9
- import requests
10
  from pathlib import Path
11
  import io
12
 
13
  class UnicodePDF(FPDF):
14
  def __init__(self):
15
  super().__init__()
16
- self.add_font('DejaVu', '', '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', uni=True)
17
- self.add_font('DejaVu', 'B', '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', uni=True)
18
- self.set_font('DejaVu', '', 12)
19
 
20
  def footer(self):
21
  self.set_y(-15)
22
- self.set_font('DejaVu', 'I', 8)
23
  self.cell(0, 10, f'Page {self.page_no()}', 0, 0, 'C')
24
 
25
- def ensure_fonts():
26
- """Ensure Unicode fonts are available"""
27
- font_paths = [
28
- '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf',
29
- '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'
30
- ]
31
- return all(os.path.exists(path) for path in font_paths)
 
 
 
 
 
 
32
 
33
  def generate_pdf(title, plan, names, image_files):
34
- # Validate inputs
35
  if not title or not title.strip():
36
- return None, "❌ Please enter a title!"
37
 
38
  if not plan or not plan.strip():
39
- return None, "❌ Please enter the plan details!"
40
 
41
  names_list = [n.strip() for n in names.split(',') if n.strip()]
42
 
 
 
 
43
  if not image_files:
44
- return None, "❌ Please upload at least one image!"
45
 
46
  if len(names_list) != len(image_files):
47
- return None, f"❌ Mismatch! {len(names_list)} names but {len(image_files)} images."
48
 
49
  try:
50
  pdf = UnicodePDF()
51
  pdf.set_margins(20, 20, 20)
52
- pdf.set_auto_page_break(auto=True, margin=20)
53
 
54
- # Clean title and plan for PDF compatibility
55
- clean_title = re.sub(r'[^\w\s\-–—]', ' ', title)[:100]
56
- clean_plan = re.sub(r'[^\w\s\-\n\.\,\!\?\(\)]', ' ', plan)
57
 
58
  # Title Page
59
  pdf.add_page()
60
- pdf.set_font('DejaVu', 'B', 24)
61
  pdf.cell(0, 20, clean_title, ln=1, align='C')
62
 
63
  pdf.ln(10)
64
- pdf.set_font('DejaVu', '', 12)
65
  pdf.multi_cell(0, 8, clean_plan)
66
 
67
- pdf.ln(10)
68
- pdf.set_font('DejaVu', 'I', 10)
69
  pdf.cell(0, 10, f"Generated: {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M')}", ln=1, align='C')
70
- pdf.cell(0, 10, f"Participants: {len(names_list)}", ln=1, align='C')
71
 
72
- # Content Pages
73
  for i, (name, img_path) in enumerate(zip(names_list, image_files)):
74
  pdf.add_page()
75
 
76
- # Clean name for PDF
77
- clean_name = re.sub(r'[^\w\s\-]', ' ', name)[:50].strip()
78
- pdf.set_font('DejaVu', 'B', 18)
79
  pdf.cell(0, 15, clean_name.upper(), ln=1, align='C')
80
 
81
- pdf.set_font('DejaVu', '', 12)
82
- pdf.cell(0, 8, f"Participant #{i+1}/{len(names_list)}", ln=1, align='C')
83
 
84
- # Add image with proper error handling
85
  try:
86
- # Read image file properly
87
  if os.path.exists(img_path):
88
  img_width = 120
89
  x_pos = (pdf.w - img_width) / 2
90
  pdf.image(img_path, x=x_pos, y=50, w=img_width)
91
 
92
- # Add caption
93
- pdf.set_font('DejaVu', 'I', 10)
94
- pdf.set_xy(x_pos, pdf.get_y() + 5)
95
- pdf.multi_cell(img_width, 5, f"Photo: {clean_name}", align='C')
96
  else:
97
- raise FileNotFoundError(f"Image file not found: {img_path}")
98
-
99
- except Exception as img_error:
100
- pdf.set_font('DejaVu', '', 12)
101
- pdf.cell(0, 10, f"⚠️ Image unavailable for {clean_name}", ln=1, align='C')
102
- pdf.set_font('DejaVu', 'I', 10)
103
- pdf.cell(0, 10, f"Error: {str(img_error)[:80]}", ln=1, align='C')
104
 
105
- # Save PDF to BytesIO (more reliable than temp files)
106
- pdf_output = io.BytesIO()
107
- pdf_output.write(pdf.output(dest='S').encode('latin1')) # FPDF outputs bytes
108
- pdf_output.seek(0)
 
109
 
110
- # Create proper temp file from bytes
111
- with tempfile.NamedTemporaryFile(suffix='.pdf', delete=False) as tmp_file:
112
- tmp_file.write(pdf_output.getvalue())
113
- tmp_file.flush()
114
  output_path = tmp_file.name
115
 
116
- success_msg = f"✅ PDF Generated Successfully!\n📄 Title: {clean_title}\n👥 {len(names_list)} Participants"
117
  return output_path, success_msg
118
 
119
  except Exception as e:
120
- return None, f"❌ Error: {str(e)}\nPlease check inputs and try again."
121
 
122
- # Enhanced UI
123
- with gr.Blocks(
124
- title="📄 Pro PDF Generator",
125
- theme=gr.themes.Soft(),
126
- css="""
127
- .gradio-container {max-width: 900px; margin: auto;}
128
- .status {padding: 15px; border-radius: 8px; margin: 10px 0;}
129
- .success {background: #d4edda; color: #155724; border-left: 4px solid #28a745;}
130
- .error {background: #f8d7da; color: #721c24; border-left: 4px solid #dc3545;}
131
- .input-group {background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 10px 0;}
132
- """
133
- ) as demo:
134
-
135
  gr.Markdown("# 📄 Professional PDF Generator")
136
- gr.Markdown("Create PDFs with **Title**, **Plan**, **Names**, and **Images**")
137
-
138
- with gr.Tabs():
139
- with gr.TabItem("📝 Document Content", icon="edit"):
140
- with gr.Row():
141
- with gr.Column(scale=2):
142
- title_input = gr.Textbox(
143
- label="📛 Document Title *",
144
- placeholder="Enter main title (required)",
145
- lines=1
146
- )
147
- plan_input = gr.Textbox(
148
- label="📋 Plan/Details *",
149
- placeholder="Enter plan, instructions, or description (required)",
150
- lines=4
151
- )
152
-
153
- with gr.Column(scale=1):
154
- names_input = gr.Textbox(
155
- label="👥 Names (comma-separated) *",
156
- placeholder="John Doe, Jane Smith, Bob Wilson",
157
- lines=3
158
- )
159
 
 
 
 
 
 
 
 
160
  images_input = gr.File(
161
- label="🖼️ Upload Images *",
162
  file_count="multiple",
163
- file_types=[".jpg", ".jpeg", ".png"],
164
- elem_classes="input-group"
165
  )
166
-
167
- with gr.TabItem("⚙️ Controls"):
168
- with gr.Row():
169
- generate_btn = gr.Button("🚀 Generate PDF", variant="primary", size="lg")
170
- validate_btn = gr.Button("🔍 Validate", variant="secondary")
171
- clear_btn = gr.Button("🗑️ Clear All", variant="stop")
172
-
173
- status_output = gr.Markdown("Ready to generate PDF", elem_classes="status")
174
 
175
- pdf_output = gr.File(label="📥 Download PDF", elem_classes="input-group")
 
 
 
 
 
176
 
177
  # Event handlers
178
- def validate_inputs(title, plan, names, images):
179
- errors = []
180
- names_list = [n.strip() for n in names.split(',') if n.strip()]
181
-
182
- if not title.strip(): errors.append("• Title is required")
183
- if not plan.strip(): errors.append("• Plan is required")
184
- if not names.strip(): errors.append("• Names are required")
185
- if len(names_list) != len(images or []):
186
- errors.append(f"• Names ({len(names_list)}) ≠ Images ({len(images or [])})")
187
-
188
- if errors:
189
- return f"❌ **Validation Errors:**\n" + "\n".join(errors), gr.update(visible=False)
190
- return "✅ **All inputs valid!** Click Generate PDF.", gr.update(visible=True)
191
 
192
  generate_btn.click(
193
  fn=generate_pdf,
@@ -195,16 +177,21 @@ with gr.Blocks(
195
  outputs=[pdf_output, status_output]
196
  )
197
 
198
- validate_btn.click(
199
- fn=validate_inputs,
200
- inputs=[title_input, plan_input, names_input, images_input],
201
- outputs=[status_output, generate_btn]
202
- )
203
-
204
  clear_btn.click(
205
- fn=lambda: ("", "", "", None, "Form cleared. Ready to create new PDF!"),
206
  outputs=[title_input, plan_input, names_input, images_input, status_output]
207
  )
 
 
 
 
 
 
 
 
 
 
 
208
 
209
  if __name__ == "__main__":
210
  demo.launch()
 
1
+ # app.py - Fixed PDF Generator (Runtime Error Free)
2
 
3
  import gradio as gr
4
  from fpdf import FPDF
 
6
  import tempfile
7
  import re
8
  import pandas as pd
 
9
  from pathlib import Path
10
  import io
11
 
12
  class UnicodePDF(FPDF):
13
  def __init__(self):
14
  super().__init__()
15
+ # Use Arial Unicode MS or fallback to core fonts
16
+ self.set_font('Arial', '', 12)
 
17
 
18
  def footer(self):
19
  self.set_y(-15)
20
+ self.set_font('Arial', 'I', 8)
21
  self.cell(0, 10, f'Page {self.page_no()}', 0, 0, 'C')
22
 
23
+ def clean_text(text, max_length=100):
24
+ """Clean text for PDF compatibility"""
25
+ # Remove or replace problematic Unicode characters
26
+ replacements = {
27
+ '–': '-', '—': '-', '“': '"', '”': '"', '‘': "'", '’': "'",
28
+ '•': '*', '°': 'deg', '™': '(TM)', '®': '(R)', '©': '(C)'
29
+ }
30
+ for old, new in replacements.items():
31
+ text = text.replace(old, new)
32
+
33
+ # Remove remaining problematic characters
34
+ text = re.sub(r'[^\w\s\-.,!?\(\)\[\]:;0-9]', ' ', text)
35
+ return ' '.join(text.split())[:max_length]
36
 
37
  def generate_pdf(title, plan, names, image_files):
38
+ # Input validation
39
  if not title or not title.strip():
40
+ return None, "❌ Title is required!"
41
 
42
  if not plan or not plan.strip():
43
+ return None, "❌ Plan is required!"
44
 
45
  names_list = [n.strip() for n in names.split(',') if n.strip()]
46
 
47
+ if not names_list:
48
+ return None, "❌ At least one name is required!"
49
+
50
  if not image_files:
51
+ return None, "❌ At least one image is required!"
52
 
53
  if len(names_list) != len(image_files):
54
+ return None, f"❌ Mismatch: {len(names_list)} names vs {len(image_files)} images"
55
 
56
  try:
57
  pdf = UnicodePDF()
58
  pdf.set_margins(20, 20, 20)
 
59
 
60
+ # Clean inputs
61
+ clean_title = clean_text(title, 80)
62
+ clean_plan = clean_text(plan, 500)
63
 
64
  # Title Page
65
  pdf.add_page()
66
+ pdf.set_font('Arial', 'B', 22)
67
  pdf.cell(0, 20, clean_title, ln=1, align='C')
68
 
69
  pdf.ln(10)
70
+ pdf.set_font('Arial', '', 12)
71
  pdf.multi_cell(0, 8, clean_plan)
72
 
73
+ pdf.ln(15)
74
+ pdf.set_font('Arial', 'I', 10)
75
  pdf.cell(0, 10, f"Generated: {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M')}", ln=1, align='C')
76
+ pdf.cell(0, 10, f"Total Participants: {len(names_list)}", ln=1, align='C')
77
 
78
+ # Individual participant pages
79
  for i, (name, img_path) in enumerate(zip(names_list, image_files)):
80
  pdf.add_page()
81
 
82
+ # Clean name
83
+ clean_name = clean_text(name, 50)
84
+ pdf.set_font('Arial', 'B', 18)
85
  pdf.cell(0, 15, clean_name.upper(), ln=1, align='C')
86
 
87
+ pdf.set_font('Arial', '', 12)
88
+ pdf.cell(0, 10, f"Participant #{i+1} of {len(names_list)}", ln=1, align='C')
89
 
90
+ # Try to add image
91
  try:
 
92
  if os.path.exists(img_path):
93
  img_width = 120
94
  x_pos = (pdf.w - img_width) / 2
95
  pdf.image(img_path, x=x_pos, y=50, w=img_width)
96
 
97
+ # Image caption
98
+ pdf.set_xy(x_pos, pdf.get_y() + 10)
99
+ pdf.set_font('Arial', 'I', 10)
100
+ pdf.multi_cell(img_width, 6, f"Photo ID: {i+1}", align='C')
101
  else:
102
+ raise FileNotFoundError("Image file not accessible")
103
+ except Exception as img_err:
104
+ pdf.set_font('Arial', '', 12)
105
+ pdf.set_text_color(150, 150, 150)
106
+ pdf.cell(0, 10, f"Image unavailable for {clean_name}", ln=1, align='C')
107
+ pdf.set_text_color(0, 0, 0)
 
108
 
109
+ # Generate PDF file properly
110
+ output_buffer = io.BytesIO()
111
+ pdf_output = pdf.output(dest='S')
112
+ output_buffer.write(pdf_output.encode('latin1'))
113
+ output_buffer.seek(0)
114
 
115
+ # Create temp file
116
+ with tempfile.NamedTemporaryFile(mode='wb', suffix='.pdf', delete=False) as tmp_file:
117
+ tmp_file.write(output_buffer.getvalue())
 
118
  output_path = tmp_file.name
119
 
120
+ success_msg = f"✅ Success!\n📄 Title: {clean_title[:50]}...\n👥 {len(names_list)} participants\n📄 {len(names_list) + 1} pages"
121
  return output_path, success_msg
122
 
123
  except Exception as e:
124
+ return None, f"❌ Error: {str(e)}\nPlease check your inputs."
125
 
126
+ # Clean Gradio UI without problematic parameters
127
+ with gr.Blocks(title="PDF Generator Pro") as demo:
 
 
 
 
 
 
 
 
 
 
 
128
  gr.Markdown("# 📄 Professional PDF Generator")
129
+ gr.Markdown("""
130
+ **Create PDFs with:**
131
+ - Document Title & Plan
132
+ - Individual participant pages
133
+ - Names + Images
134
+ """)
135
+
136
+ with gr.Row():
137
+ with gr.Column(scale=2):
138
+ title_input = gr.Textbox(
139
+ label="📛 Document Title *",
140
+ placeholder="Enter your document title",
141
+ lines=2
142
+ )
143
+
144
+ plan_input = gr.Textbox(
145
+ label="📋 Plan / Description *",
146
+ placeholder="Enter detailed plan or description",
147
+ lines=5
148
+ )
 
 
 
149
 
150
+ names_input = gr.Textbox(
151
+ label="👥 Names (comma-separated) *",
152
+ placeholder="John Doe, Jane Smith, Bob Wilson",
153
+ lines=3
154
+ )
155
+
156
+ with gr.Column(scale=1):
157
  images_input = gr.File(
158
+ label="🖼️ Upload Images *",
159
  file_count="multiple",
160
+ file_types=[".jpg", ".jpeg", ".png", ".webp"]
 
161
  )
 
 
 
 
 
 
 
 
162
 
163
+ with gr.Row():
164
+ generate_btn = gr.Button("🚀 Generate PDF", variant="primary", size="lg")
165
+ clear_btn = gr.Button("🗑️ Clear All", variant="secondary")
166
+
167
+ status_output = gr.Markdown("Ready to generate PDF")
168
+ pdf_output = gr.File(label="📥 Download PDF")
169
 
170
  # Event handlers
171
+ def clear_all():
172
+ return ("", "", "", None, "Form cleared!")
 
 
 
 
 
 
 
 
 
 
 
173
 
174
  generate_btn.click(
175
  fn=generate_pdf,
 
177
  outputs=[pdf_output, status_output]
178
  )
179
 
 
 
 
 
 
 
180
  clear_btn.click(
181
+ fn=clear_all,
182
  outputs=[title_input, plan_input, names_input, images_input, status_output]
183
  )
184
+
185
+ # Real-time validation
186
+ def validate_names(names):
187
+ names_list = [n.strip() for n in names.split(',') if n.strip()]
188
+ return f"Found {len(names_list)} names"
189
+
190
+ names_input.change(
191
+ fn=validate_names,
192
+ inputs=[names_input],
193
+ outputs=[status_output]
194
+ )
195
 
196
  if __name__ == "__main__":
197
  demo.launch()