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)
