name: pdf-generation description: Generate 2-page ATS-compliant PDF CVs using WeasyPrint with multi-language support (EN/DE/PL) and visual regression testing. Use when generating final PDF, running visual tests, debugging layout issues, or iterating on CV template design. Trigger on: "generate PDF", "create CV PDF", "render PDF", "visual regression test", or after successful CV validation.
PDF Generation Skill
When to Apply
Trigger this skill when:
- User asks to generate final PDF
- After successful CV validation
- Running visual regression tests
- Debugging template layout issues
- Iterating on CV design changes
- Testing multi-language output (EN/DE/PL)
Prerequisites: CV data must pass validation first (use cv-validation skill)
Core PDF Generation Workflow
Step 1: Pre-Generation Validation
Always validate before generating PDF:
python .claude/skills/cv-validation/scripts/validate_schema.py <cv-json-file>
Only proceed if validation passes. Never generate PDF from invalid data.
Step 2: Call Generation API
Generate PDF via Azure Function:
curl -X POST http://localhost:7071/api/generate-cv-action \
-H "Content-Type: application/json" \
-d '{
"cv_data": {...},
"language": "en",
"source_docx_base64": null
}'
Parameters:
cv_data(required): Complete CV JSON objectlanguage(optional): "en" (default), "de", or "pl"source_docx_base64(optional): Base64-encoded DOCX for photo extraction
Response:
{
"success": true,
"preview_html_url": "blob://sessions/.../preview.html",
"pdf_url": "blob://sessions/.../cv.pdf",
"session_id": "d03cf26e-0dc1-48f9-91c8-7def7a10ddca"
}
Step 3: Download and Display
Save generated files locally:
# Download preview HTML
curl "<preview_html_url>" > tmp/preview_<session_id>.html
# Download PDF
curl "<pdf_url>" > tmp/cv_<session_id>.pdf
Show to user:
✅ PDF Generated Successfully
Preview: [tmp/preview_d03cf26e.html](tmp/preview_d03cf26e.html)
PDF: [tmp/cv_d03cf26e.pdf](tmp/cv_d03cf26e.pdf)
Language: English (EN)
Pages: 2
Size: 245 KB
Next steps:
1. Review PDF visually
2. Run visual regression test (/visual-regression)
3. If satisfied, share with user
Step 4: Visual Regression Testing
Always test generated PDF against baseline:
npm test
# Or use command: /visual-regression
Playwright will:
- Screenshot generated PDF
- Compare with approved baseline
- Report differences (threshold: 5%)
See: references/visual-regression-workflow.md
Extended Thinking Modes
Use Claude's extended thinking for complex PDF generation tasks:
Standard Generation ("think")
Use for: Regular CV generation with validated data Example:
think: Generate PDF for validated CV data with English template
Layout Debugging ("think hard")
Use for: Content overflow, alignment issues, font problems Example:
think hard: Why is the experience section overflowing to page 3?
Analyze template CSS, content estimation, and WeasyPrint rendering.
Multi-Language Edge Cases ("ultrathink")
Use for: Complex German/Polish layout issues, character encoding Example:
ultrathink: German CV with long compound words breaks template layout.
Analyze word-break rules, hyphenation, character spacing, and CSS constraints.
Multi-Language Support
English (en)
Template: cv_template_2pages_2025_en.html
Characteristics:
- Shorter words (easier to fit)
- Standard sections (Experience, Education, Skills)
- Date format: MM/YYYY or YYYY-MM-DD
CSS considerations:
- Default line-height: 1.4
- Word-break: normal
German (de)
Template: cv_template_2pages_2025_de.html
Characteristics:
- Longer compound words (Projektmanagement, Softwareentwicklung)
- Umlauts (ä, ö, ü, ß) require UTF-8
- Section headers: Berufserfahrung, Ausbildung, Fähigkeiten
- Date format: MM.YYYY
CSS considerations:
- Line-height: 1.5 (15% more space)
- Word-break: break-word (allow mid-word breaks)
- Hyphens: auto (enable hyphenation)
WeasyPrint quirk: German hyphenation requires language tag:
<html lang="de">
Polish (pl)
Template: cv_template_2pages_2025_pl.html
Characteristics:
- Polish diacritics (ą, ć, ę, ł, ń, ó, ś, ź, ż)
- Moderately longer words than English
- Section headers: Doświadczenie, Wykształcenie, Umiejętności
- Date format: MM.YYYY
CSS considerations:
- Line-height: 1.45 (10% more space)
- Word-break: normal
- Character encoding: UTF-8 (critical)
Visual Iteration Workflow
For template design changes, use iterative screenshot comparison:
Iteration Pattern
- Make CSS change (e.g., increase heading font size)
- Generate preview (POST /api/generate-cv-action with
preview_only: true) - Screenshot preview (use Playwright MCP)
- Compare with baseline (visual diff)
- Accept or iterate (update baseline if satisfied)
Example workflow:
User: "Increase section heading size and test"
Claude:
1. Edit templates/html/cv_template_2pages_2025.css:
h2 { font-size: 18px; } → h2 { font-size: 20px; }
2. Generate preview:
curl POST /api/generate-cv-action {"cv_data": {...}, "preview_only": true}
3. Use Playwright MCP to screenshot:
npx playwright screenshot tmp/preview_abc123.html tmp/preview_abc123.png
4. Compare with baseline:
compare tmp/preview_abc123.png test-results/cv-en-baseline.png
5. Display diff:
Diff: 3.2% (increased heading size visible, layout intact)
6. Ask: Accept new design? (yes/no)
See: references/visual-iteration-patterns.md
WeasyPrint CSS Compatibility
WeasyPrint renders HTML to PDF using a subset of CSS.
Supported Features
- ✅ Float layouts (main positioning method)
- ✅ Flexbox (basic support, not all properties)
- ✅ Absolute/relative positioning
- ✅ Margins, padding, borders
- ✅ Fonts (embedded via @font-face)
- ✅ Page breaks (page-break-before, page-break-after)
- ✅ Background colors/images
NOT Supported (or limited)
- ❌ CSS Grid (not supported)
- ❌ CSS Transforms (scale, rotate)
- ❌ CSS Animations
- ❌ CSS Variables (--custom-property)
- ⚠️ Flexbox gaps (use margins instead)
- ⚠️ Sticky positioning
Important: Always test template changes with actual PDF generation, not just browser preview.
See: references/weasyprint-quirks.md
Common PDF Generation Issues
Issue 1: Content Overflow (>2 Pages)
Symptom: PDF has 3+ pages Root cause: Too much content or large fonts Fix:
- Run layout estimation:
.claude/skills/cv-validation/scripts/count_template_space.py - If >2.0 pages estimated, reduce content:
- Shorten experience bullets
- Limit experience entries (last 10 years)
- Remove less relevant skills
- Or reduce font sizes (last resort):
body { font-size: 10px; } /* was 11px */
Issue 2: Photo Not Displaying
Symptom: Profile photo missing in PDF Root cause: photo_url invalid or >32KB Fix:
- Validate photo_url size:
python .claude/skills/cv-validation/scripts/validate_schema.py <cv-json> | grep photo_url - If >32KB, compress:
python .claude/skills/cv-validation/scripts/compress_photo.py <image> --max-size=30KB - Verify data URI format:
data:image/png;base64,iVBORw0KG...
Issue 3: Font Rendering Issues
Symptom: Fonts look wrong or fallback to default Root cause: Font files not embedded correctly Fix:
- Check font files exist:
templates/html/fonts/ - Verify @font-face in CSS:
@font-face { font-family: 'Roboto'; src: url('fonts/Roboto-Regular.ttf'); } - Use WeasyPrint font debugging:
from weasyprint import HTML, CSS HTML(string=html).write_pdf('test.pdf', stylesheets=[CSS(string=css)]) # Check terminal for font warnings
Issue 4: Layout Shifts Between Languages
Symptom: German CV overflows but English fits Root cause: Longer German words without proper word-breaking Fix:
- Enable hyphenation in template:
p { hyphens: auto; } - Set HTML lang attribute:
<html lang="de"> - Increase line-height for German:
body[lang="de"] { line-height: 1.5; }
Issue 5: Visual Regression Test Fails
Symptom: Playwright reports >5% diff Root cause: Intentional change or unexpected regression Fix:
- View diff image:
test-results/cv-german-diff.png - If intentional change (e.g., font size update), accept new baseline:
npm test -- --update-snapshots - If unintended regression, investigate:
- Check recent CSS changes
- Review template modifications
- Test WeasyPrint version consistency
Scripts
print_pdf_playwright.mjs
Location: scripts/print_pdf_playwright.mjs Purpose: Headless PDF generation using Playwright Usage:
node scripts/print_pdf_playwright.mjs <html-file> <output-pdf>
Features:
- Headless Chromium rendering
- Pixel-perfect PDF output
- Used by visual regression tests
Alternative to WeasyPrint: Use when debugging browser-specific rendering issues.
Progressive Disclosure
Level 1 (Metadata): Skill name + description (always loaded) Level 2 (This file): SKILL.md body (loaded when skill triggers) Level 3 (References):
- Read references/template-specification.md for template structure
- Read references/weasyprint-quirks.md for CSS compatibility
- Read references/visual-iteration-patterns.md for design workflows
Only load references when:
- Debugging complex layout issues
- Learning template structure
- Implementing new template features
Integration with Other Commands/Skills
cv-validation → pdf-generation
Flow:
/validate-cv→ Pre-check + API validation- If valid: Automatically offer PDF generation
- User confirms
- Generate PDF + preview
pdf-generation → visual-regression
Flow:
- Generate PDF
/visual-regression→ Screenshot + compare- If diff <5%: Accept PDF
- If diff ≥5%: Review changes, update baseline if intentional
Visual iteration loop
Flow:
- Edit template CSS
- Generate preview (no full PDF)
- Screenshot with Playwright MCP
- Compare with design mock (not baseline)
- Iterate until pixel-perfect
- Generate final PDF + update baseline
Performance Optimization
PDF generation timing:
- Validation: ~200ms (API call)
- HTML rendering: ~500ms (template + data injection)
- WeasyPrint PDF: ~1-2s (complex layout)
- Total: ~2-3s per PDF
Optimization tips:
- Cache validated data (don't re-validate on retry)
- Reuse template instances (don't re-parse CSS)
- Parallelize multi-language generation:
# Generate EN, DE, PL in parallel curl POST /api/generate-cv-action {"language": "en"} & curl POST /api/generate-cv-action {"language": "de"} & curl POST /api/generate-cv-action {"language": "pl"} & wait
Headless CI/CD Integration (Phase 3)
GitHub Actions workflow:
name: Visual Regression Tests
on: [pull_request]
jobs:
test-cv-generation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/setup-python@v4
- name: Install dependencies
run: |
npm install
pip install -r requirements.txt
- name: Generate test PDFs
run: |
npm run pretest # Generate test artifacts
python scripts/generate_all_languages.py
- name: Run visual regression
run: npm test
- name: Upload diff images
if: failure()
uses: actions/upload-artifact@v3
with:
name: visual-diffs
path: test-results/**/*-diff.png
Claude Code headless mode:
# Run PDF generation + visual test via Claude agent
claude -p "Generate CV PDF and run visual regression tests" \
--headless \
--output test-results/claude-report.md
References
Read these files for detailed information:
- references/template-specification.md - 2-page template structure
- references/weasyprint-quirks.md - CSS compatibility notes
- references/visual-iteration-patterns.md - Design iteration workflows
- references/visual-regression-workflow.md - Testing patterns
Project files:
- Template files: ../../templates/html/
- Template spec: ../../templates/html/CV_template_2pages_2025.spec.md
- API implementation: ../../src/render.py
- Playwright config: ../../playwright.config.ts
Last updated: 2026-01-22 Skill version: 1.0.0