firepenguindisopanda commited on
Commit
7108b9e
·
1 Parent(s): f8aef8d

Fixed: pdf creation issue

Browse files
Files changed (2) hide show
  1. app/routers/web.py +23 -29
  2. scripts/debug_pdf_uv.py +24 -0
app/routers/web.py CHANGED
@@ -74,6 +74,18 @@ def generate_pdf_document(project_description: str, markdown_outputs: dict[str,
74
  pdf = FPDF()
75
  pdf.set_auto_page_break(auto=True, margin=15)
76
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  # Add title page
78
  pdf.add_page()
79
  pdf.set_font("Arial", "B", 24)
@@ -94,12 +106,7 @@ def generate_pdf_document(project_description: str, markdown_outputs: dict[str,
94
  for line in description_lines:
95
  cleaned_line = clean_markdown(line.strip())
96
  if cleaned_line:
97
- try:
98
- pdf.multi_cell(0, 6, cleaned_line)
99
- except Exception:
100
- # Handle encoding issues
101
- safe_line = cleaned_line.encode('latin-1', 'replace').decode('latin-1')
102
- pdf.multi_cell(0, 6, safe_line)
103
 
104
  pdf.ln(10)
105
 
@@ -159,11 +166,7 @@ def generate_pdf_document(project_description: str, markdown_outputs: dict[str,
159
  else:
160
  pdf.set_font("Arial", "B", 10)
161
 
162
- try:
163
- pdf.multi_cell(0, 6, heading_text)
164
- except Exception:
165
- safe_text = heading_text.encode('latin-1', 'replace').decode('latin-1')
166
- pdf.multi_cell(0, 6, safe_text)
167
 
168
  pdf.set_font("Arial", "", 10)
169
  pdf.ln(2)
@@ -177,40 +180,32 @@ def generate_pdf_document(project_description: str, markdown_outputs: dict[str,
177
  # Handle bullet points
178
  if line.strip().startswith(('- ', '* ', '+ ')):
179
  cleaned = clean_markdown(line.strip()[2:])
180
- try:
181
- pdf.multi_cell(0, 5, f" • {cleaned}")
182
- except Exception:
183
- safe_text = cleaned.encode('latin-1', 'replace').decode('latin-1')
184
- pdf.multi_cell(0, 5, f" • {safe_text}")
185
  continue
186
 
187
  # Handle numbered lists
188
  if re.match(r'^\d+\.\s', line.strip()):
189
  cleaned = clean_markdown(line.strip())
190
- try:
191
- pdf.multi_cell(0, 5, f" {cleaned}")
192
- except Exception:
193
- safe_text = cleaned.encode('latin-1', 'replace').decode('latin-1')
194
- pdf.multi_cell(0, 5, f" {safe_text}")
195
  continue
196
 
197
  # Regular text
198
  if line.strip():
199
  cleaned = clean_markdown(line)
200
- try:
201
- pdf.multi_cell(0, 5, cleaned)
202
- except Exception:
203
- safe_text = cleaned.encode('latin-1', 'replace').decode('latin-1')
204
- pdf.multi_cell(0, 5, safe_text)
205
  else:
206
  pdf.ln(3)
207
 
208
  # Generate PDF in memory
209
  pdf_output = BytesIO()
210
- pdf_bytes = pdf.output()
 
 
 
 
211
  pdf_output.write(pdf_bytes)
212
  pdf_output.seek(0)
213
-
214
  return pdf_output
215
 
216
 
@@ -249,4 +244,3 @@ async def download_pdf(request: PDFRequest):
249
  )
250
 
251
 
252
- # NOTE: HTML UI routes removed in favor of API-only implementation.
 
74
  pdf = FPDF()
75
  pdf.set_auto_page_break(auto=True, margin=15)
76
 
77
+ def safe_multi_cell(height: int, text: str):
78
+ """Safely write text to PDF, handling encoding and layout errors."""
79
+ if not text or not text.strip():
80
+ return
81
+ # Sanitize text for latin-1 encoding used by core fonts
82
+ safe_text = text.encode('latin-1', 'replace').decode('latin-1')
83
+ try:
84
+ pdf.multi_cell(0, height, safe_text)
85
+ except Exception:
86
+ # If multi_cell still fails (e.g., layout issue), skip this line
87
+ pass
88
+
89
  # Add title page
90
  pdf.add_page()
91
  pdf.set_font("Arial", "B", 24)
 
106
  for line in description_lines:
107
  cleaned_line = clean_markdown(line.strip())
108
  if cleaned_line:
109
+ safe_multi_cell(6, cleaned_line)
 
 
 
 
 
110
 
111
  pdf.ln(10)
112
 
 
166
  else:
167
  pdf.set_font("Arial", "B", 10)
168
 
169
+ safe_multi_cell(6, heading_text)
 
 
 
 
170
 
171
  pdf.set_font("Arial", "", 10)
172
  pdf.ln(2)
 
180
  # Handle bullet points
181
  if line.strip().startswith(('- ', '* ', '+ ')):
182
  cleaned = clean_markdown(line.strip()[2:])
183
+ safe_multi_cell(5, f" - {cleaned}")
 
 
 
 
184
  continue
185
 
186
  # Handle numbered lists
187
  if re.match(r'^\d+\.\s', line.strip()):
188
  cleaned = clean_markdown(line.strip())
189
+ safe_multi_cell(5, f" {cleaned}")
 
 
 
 
190
  continue
191
 
192
  # Regular text
193
  if line.strip():
194
  cleaned = clean_markdown(line)
195
+ safe_multi_cell(5, cleaned)
 
 
 
 
196
  else:
197
  pdf.ln(3)
198
 
199
  # Generate PDF in memory
200
  pdf_output = BytesIO()
201
+ pdf_str = pdf.output(dest='S')
202
+ if isinstance(pdf_str, str):
203
+ pdf_bytes = pdf_str.encode('latin-1')
204
+ else:
205
+ pdf_bytes = pdf_str
206
  pdf_output.write(pdf_bytes)
207
  pdf_output.seek(0)
208
+
209
  return pdf_output
210
 
211
 
 
244
  )
245
 
246
 
 
scripts/debug_pdf_uv.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ import sys
3
+ import tempfile
4
+
5
+ # Ensure project package imports work when run from workspace root
6
+ sys.path.append(str(Path(__file__).resolve().parents[1]))
7
+
8
+ from app.routers.web import generate_pdf_document
9
+
10
+ if __name__ == '__main__':
11
+ pdf = generate_pdf_document(
12
+ "Test project description for UV run",
13
+ {
14
+ "product_owner": "# Product Vision\nThis is a test product.\n- Item A\n- Item B",
15
+ "developer": "## Implementation\n- Use FastAPI\n- Use React"
16
+ }
17
+ )
18
+
19
+ out_path = Path(tempfile.gettempdir()) / "srs_test_uv.pdf"
20
+ with open(out_path, "wb") as f:
21
+ f.write(pdf.getvalue())
22
+
23
+ print(f"Wrote PDF to: {out_path}")
24
+ print(f"Size: {out_path.stat().st_size} bytes")