lokesh341 commited on
Commit
3967fb3
Β·
verified Β·
1 Parent(s): a849d04

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +101 -123
app.py CHANGED
@@ -1,50 +1,41 @@
1
- # app.py - Fixed PDF Generator with Unicode support and Title + Plan layout
2
 
3
  import gradio as gr
4
  from fpdf import FPDF
5
  import os
6
  import tempfile
7
  import re
 
 
8
  from pathlib import Path
 
9
 
10
  class UnicodePDF(FPDF):
11
  def __init__(self):
12
  super().__init__()
13
- self.add_font('DejaVu', '', 'DejaVuSans.ttf', uni=True)
14
- self.add_font('DejaVu', 'B', 'DejaVuSans-Bold.ttf', uni=True)
15
- self.default_font = 'DejaVu'
16
-
17
- def set_default_font(self):
18
  self.set_font('DejaVu', '', 12)
19
-
20
- def download_dejavu_fonts():
21
- """Download DejaVu fonts if not present (for Hugging Face Spaces)"""
22
- import urllib.request
23
- import os
24
-
25
- font_dir = "fonts"
26
- os.makedirs(font_dir, exist_ok=True)
27
 
28
- fonts = {
29
- 'DejaVuSans.ttf': 'https://github.com/dejavu-fonts/dejavu-fonts/raw/master/ttf/DejaVuSans.ttf',
30
- 'DejaVuSans-Bold.ttf': 'https://github.com/dejavu-fonts/dejavu-fonts/raw/master/ttf/DejaVuSans-Bold.ttf'
31
- }
32
-
33
- for font_name, url in fonts.items():
34
- font_path = os.path.join(font_dir, font_name)
35
- if not os.path.exists(font_path):
36
- try:
37
- urllib.request.urlretrieve(url, font_path)
38
- print(f"Downloaded {font_name}")
39
- except:
40
- print(f"Failed to download {font_name}, using built-in fonts")
41
 
42
  def generate_pdf(title, plan, names, image_files):
43
  # Validate inputs
44
- if not title or title.strip() == "":
45
  return None, "❌ Please enter a title!"
46
 
47
- if not plan or plan.strip() == "":
48
  return None, "❌ Please enter the plan details!"
49
 
50
  names_list = [n.strip() for n in names.split(',') if n.strip()]
@@ -53,163 +44,150 @@ def generate_pdf(title, plan, names, image_files):
53
  return None, "❌ Please upload at least one image!"
54
 
55
  if len(names_list) != len(image_files):
56
- return None, f"❌ Mismatch! {len(names_list)} names but {len(image_files)} images. Please match counts."
57
 
58
  try:
59
- # Download fonts if needed (for HF Spaces)
60
- download_dejavu_fonts()
61
-
62
  pdf = UnicodePDF()
63
  pdf.set_margins(20, 20, 20)
64
  pdf.set_auto_page_break(auto=True, margin=20)
65
 
 
 
 
 
66
  # Title Page
67
  pdf.add_page()
68
  pdf.set_font('DejaVu', 'B', 24)
69
- pdf.set_text_color(30, 30, 30)
70
- pdf.cell(0, 20, title.upper(), ln=1, align='C')
71
 
72
  pdf.ln(10)
73
- pdf.set_font('DejaVu', '', 14)
74
- pdf.multi_cell(0, 8, plan, align='C')
75
 
76
  pdf.ln(10)
77
- pdf.set_font('DejaVu', 'I', 12)
78
- pdf.cell(0, 10, f"Generated on: {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M')}", ln=1, align='C')
79
- pdf.cell(0, 10, f"Total Participants: {len(names_list)}", ln=1, align='C')
80
 
81
  # Content Pages
82
  for i, (name, img_path) in enumerate(zip(names_list, image_files)):
83
  pdf.add_page()
84
 
85
- # Name header
 
86
  pdf.set_font('DejaVu', 'B', 18)
87
- pdf.set_text_color(50, 50, 150)
88
- clean_name = re.sub(r'[^\w\s-]', '', name) # Clean special chars
89
  pdf.cell(0, 15, clean_name.upper(), ln=1, align='C')
90
 
91
- # Participant info
92
  pdf.set_font('DejaVu', '', 12)
93
- pdf.cell(0, 8, f"Participant #{i+1} of {len(names_list)}", ln=1, align='C')
94
 
95
- # Add image
96
  try:
97
- page_width = pdf.w - 40 # Account for margins
98
- img_width = min(140, page_width * 0.8)
99
- x_pos = (page_width - img_width) / 2 + 20 # Center with margins
100
-
101
- pdf.image(img_path, x=x_pos, y=50, w=img_width)
102
-
103
- # Add image caption
104
- pdf.set_font('DejaVu', 'I', 10)
105
- pdf.set_xy(x_pos, pdf.get_y() + 5)
106
- pdf.multi_cell(img_width, 5, f"Image for {clean_name}", align='C')
107
-
 
 
108
  except Exception as img_error:
109
  pdf.set_font('DejaVu', '', 12)
110
- pdf.set_text_color(200, 50, 50)
111
- pdf.cell(0, 10, f"⚠️ Could not load image for {clean_name}", ln=1, align='C')
112
- pdf.cell(0, 10, f"Error: {str(img_error)[:100]}...", ln=1, align='C')
113
 
114
- # Create output file
115
- with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf', delete=True) as tmp_file:
 
 
 
 
 
 
 
116
  output_path = tmp_file.name
117
- pdf.output(output_path)
118
 
119
- success_msg = f"βœ… PDF generated successfully!\nπŸ“„ Title: {title}\nπŸ“‹ Plan included\nπŸ‘₯ {len(names_list)} participants"
120
  return output_path, success_msg
121
 
122
  except Exception as e:
123
- return None, f"❌ PDF Generation Error: {str(e)}\n\nPlease check your inputs and try again."
124
 
125
- # UI with enhanced layout
126
  with gr.Blocks(
127
- title="πŸ“„ Professional PDF Generator",
128
  theme=gr.themes.Soft(),
129
  css="""
130
  .gradio-container {max-width: 900px; margin: auto;}
131
- .status-message {font-size: 16px; padding: 15px; border-radius: 8px; margin: 10px 0;}
132
- .success {background: linear-gradient(135deg, #d4edda, #c3e6cb); color: #155724; border: 1px solid #c3e6cb;}
133
- .error {background: linear-gradient(135deg, #f8d7da, #f5c6cb); color: #721c24; border: 1px solid #f5c6cb;}
134
- .input-section {background: #f8f9fa; padding: 20px; border-radius: 10px; margin: 10px 0;}
135
- .btn-primary {background: linear-gradient(45deg, #007bff, #0056b3);}
136
  """
137
  ) as demo:
138
 
139
- gr.Markdown("""
140
- # πŸ“„ Professional PDF Generator
141
- ### Create structured PDFs with Title, Plan, Names & Images
142
-
143
- **How to use:**
144
- 1. **Title**: Enter your document title
145
- 2. **Plan**: Add detailed plan/instructions
146
- 3. **Names**: Comma-separated participant names
147
- 4. **Images**: Upload matching number of images
148
- 5. **Generate**: Create professional PDF
149
- """)
150
 
151
  with gr.Tabs():
152
- with gr.TabItem("πŸ“ Document Setup"):
153
  with gr.Row():
154
- with gr.Column(scale=1):
155
  title_input = gr.Textbox(
156
- label="πŸ“› Document Title",
157
- placeholder="Enter your main title here...",
158
  lines=1
159
  )
160
-
161
  plan_input = gr.Textbox(
162
- label="πŸ“‹ Plan/Details",
163
- placeholder="Enter your plan, instructions, or additional details...",
164
  lines=4
165
  )
166
 
167
  with gr.Column(scale=1):
168
  names_input = gr.Textbox(
169
- label="πŸ‘₯ Participant Names",
170
  placeholder="John Doe, Jane Smith, Bob Wilson",
171
  lines=3
172
  )
173
 
174
- with gr.Row():
175
- images_input = gr.File(
176
- label="πŸ–ΌοΈ Upload Images",
177
- file_count="multiple",
178
- file_types=["image"],
179
- elem_classes="input-section"
180
- )
181
 
182
- with gr.TabItem("βš™οΈ Preview & Generate"):
183
- gr.Markdown("### πŸ“Š Input Summary")
184
- summary = gr.Markdown("No inputs yet. Fill the form above!")
185
-
186
  with gr.Row():
187
- generate_btn = gr.Button("πŸš€ Generate Professional PDF", variant="primary", size="lg")
188
- clear_btn = gr.Button("πŸ—‘οΈ Clear All", variant="secondary")
189
- validate_btn = gr.Button("πŸ” Validate Inputs", variant="secondary")
 
 
190
 
191
- status_output = gr.Markdown("", elem_classes="status-message")
192
- pdf_output = gr.File(label="πŸ“₯ Download Professional PDF", elem_classes="input-section")
193
 
194
  # Event handlers
195
  def validate_inputs(title, plan, names, images):
196
- issues = []
197
  names_list = [n.strip() for n in names.split(',') if n.strip()]
198
 
199
- if not title.strip():
200
- issues.append("❌ Title is required")
201
- if not plan.strip():
202
- issues.append("❌ Plan is required")
203
- if len(names_list) != len(images or []):
204
- issues.append(f"❌ Names ({len(names_list)}) and images ({len(images or [])}) count mismatch")
205
 
206
- if issues:
207
- return "\n".join(issues), gr.update(visible=False)
208
- else:
209
- return "βœ… All inputs valid! Ready to generate PDF.", gr.update(visible=True)
210
-
211
- def clear_all():
212
- return ("", "", "", None, "Ready to create new document!", gr.update(value=None), "No inputs yet.")
213
 
214
  generate_btn.click(
215
  fn=generate_pdf,
@@ -224,8 +202,8 @@ with gr.Blocks(
224
  )
225
 
226
  clear_btn.click(
227
- fn=clear_all,
228
- outputs=[title_input, plan_input, names_input, images_input, status_output, pdf_output, summary]
229
  )
230
 
231
  if __name__ == "__main__":
 
1
+ # app.py - Fixed PDF Generator with proper temp file handling
2
 
3
  import gradio as gr
4
  from fpdf import FPDF
5
  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()]
 
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,
 
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__":