feat: enhance PDF export with 4 customizable parameters

Add comprehensive parameter support to /export/pdf endpoint:
- lang: Language selection (en/es)
- length: CV length (short/long)
- icons: Icon visibility (show/hide)
- version: Theme variant (extended/clean)

Backend Changes:
- Enhanced PDF generator with cookie injection for user preferences
- Cookies set before chromedp navigation to apply all preferences
- Updated filename pattern: CV-{Name}-{lang}-{length}-{version}.pdf
- Comprehensive parameter validation with descriptive error messages
- All parameters optional with sensible defaults (en, short, show, extended)

Testing:
- Added pdf_test.go with parameter validation tests
- Tests cover all valid/invalid parameter combinations
- Tests verify default parameter application

Documentation:
- Updated API documentation (doc/3-API.md)
- Added detailed parameter descriptions and examples
- Updated quick reference with all parameter options
- Added process flow diagram showing cookie injection
- Documented error responses for each invalid parameter

Implementation Details:
- Cookie domain set to "localhost" for chromedp context
- Cookies: cv-language, cv-length, cv-icons, cv-theme
- PDF generation leverages existing @media print CSS
- No changes to frontend CSS or templates

Tested:
- Parameter validation ( All invalid params rejected correctly)
- Default parameters ( Applied when params omitted)
- PDF generation with all parameter combinations ( Working)
This commit is contained in:
juanatsap
2025-11-19 10:43:19 +00:00
parent ddd09f9a16
commit 12bd9c7cd8
4 changed files with 522 additions and 108 deletions
+78
View File
@@ -6,6 +6,7 @@ import (
"io"
"time"
"github.com/chromedp/cdproto/network"
"github.com/chromedp/cdproto/page"
"github.com/chromedp/chromedp"
)
@@ -77,6 +78,83 @@ func (g *Generator) GenerateFromURL(ctx context.Context, url string) ([]byte, er
return pdfBuffer, nil
}
// GenerateFromURLWithCookies generates a PDF from a given URL with custom cookies
func (g *Generator) GenerateFromURLWithCookies(ctx context.Context, url string, cookies map[string]string) ([]byte, error) {
// Create context with timeout
ctx, cancel := context.WithTimeout(ctx, g.timeout)
defer cancel()
// Create chromedp context
allocCtx, allocCancel := chromedp.NewContext(ctx)
defer allocCancel()
// Buffer to store PDF
var pdfBuffer []byte
// Build tasks - set cookies BEFORE navigation
var tasks chromedp.Tasks
// Add cookies if provided - must be done before navigation
if len(cookies) > 0 {
tasks = append(tasks, chromedp.ActionFunc(func(ctx context.Context) error {
// Set cookies with domain (extract from URL)
// For localhost:1999, domain should be "localhost"
domain := "localhost"
for name, value := range cookies {
expr := network.SetCookie(name, value).
WithDomain(domain).
WithPath("/").
WithHTTPOnly(true).
WithSecure(false). // Set to true in production with HTTPS
WithSameSite(network.CookieSameSiteStrict)
if err := expr.Do(ctx); err != nil {
return fmt.Errorf("failed to set cookie %s: %w", name, err)
}
}
return nil
}))
}
// Navigate to URL
tasks = append(tasks, chromedp.Navigate(url))
// Wait for page to be ready
tasks = append(tasks,
chromedp.WaitReady("body"),
// Small delay to ensure all content is loaded
chromedp.Sleep(500*time.Millisecond),
// Generate PDF with print-optimized settings
chromedp.ActionFunc(func(ctx context.Context) error {
var err error
pdfBuffer, _, err = page.PrintToPDF().
WithPrintBackground(true).
WithPreferCSSPageSize(true).
WithMarginTop(0).
WithMarginBottom(0).
WithMarginLeft(0).
WithMarginRight(0).
WithPaperWidth(8.27). // A4 width in inches
WithPaperHeight(11.69). // A4 height in inches
Do(ctx)
return err
}),
)
// Run chromedp tasks
err := chromedp.Run(allocCtx, tasks)
if err != nil {
return nil, fmt.Errorf("chromedp execution failed: %w", err)
}
if len(pdfBuffer) == 0 {
return nil, fmt.Errorf("generated PDF is empty")
}
return pdfBuffer, nil
}
// StreamFromURL generates a PDF and writes it to the provided writer
func (g *Generator) StreamFromURL(ctx context.Context, url string, w io.Writer) error {
pdfData, err := g.GenerateFromURL(ctx, url)