175:@router.post("/convert-pdf") 176-async def convert_html_to_pdf(template_id: int = Query(..., description="HTML template to render"), 177- db: AsyncSession = Depends(get_db), 178- user: User = Depends(get_current_user), 179- _: None = Depends(require_csrf)): 180- """Convert one of the user's saved HTML templates into a real PDF attachment 181- using WeasyPrint. The generated PDF is stored and can be attached to campaigns.""" 182- tpl = await db.get(EmailTemplate, template_id) 183- if not tpl or tpl.user_id != user.id: 184- raise HTTPException(status_code=404, detail="Template not found") 185- 186- # Substitute variables with real sample values before validation/rendering 187- # (mirrors the template test-pdf flow), so the generated PDF contains 188- # rendered values instead of raw %Tokens%. 189- from app.services.variable_engine import GeneratedValues, VariableEngine 190- 191- engine = VariableEngine({}) 192- g = GeneratedValues(invoice_number=engine.generate_invoice_number(set()), 193- amount=engine.generate_amount(), 194- shipping=engine.generate_shipping(), 195- invoice_date="2026-01-15", due_date="2026-01-30") 196- res = engine.substitute(tpl.body_html, {"first_name": "Sample", "last_name": "Customer", 197- "email": "sample@example.com", 198- "invoice_number": g.invoice_number}, g, 199- extra={"unsubscribe_link": "https://example.com/unsubscribe"}) 200- problems = validate_html(res.text) 201- if problems: # list of problem strings 202- raise HTTPException(status_code=400, 203- detail=f"Template has errors: {'; '.join(problems)}") 204- 205- pdf_bytes = await asyncio.get_event_loop().run_in_executor(None, html_to_pdf, res.text)