feat: add proposal for cover letter PDF conversion
Add OpenSpec proposal for converting markdown cover letters to PDF format using Swiss business letter standards. The proposal includes: - Swiss letter formatting with scrlttr2 and Swiss Norm configuration - Environment validation for Pandoc and LaTeX - Comprehensive error handling and user guidance - Support for Swiss German formatting and address window positioning This addresses step 7 "Generate PDFs" in the project workflow. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,499 @@
|
||||
Convert a markdown cover letter to a professionally formatted PDF using Swiss business letter standards.
|
||||
|
||||
# Instructions
|
||||
|
||||
You are converting a markdown cover letter to PDF format. The output will follow Swiss business letter standards (scrlttr2 with Swiss Norm) for proper formatting and address window positioning.
|
||||
|
||||
## Step 1: Check for Cover Letter
|
||||
|
||||
### Verify File Exists
|
||||
|
||||
1. Check if `cover-letter.md` exists in the current directory
|
||||
2. If file exists → proceed to Step 2
|
||||
3. If file does not exist → show error and stop
|
||||
|
||||
### Error: Cover Letter Not Found
|
||||
|
||||
```
|
||||
❌ Cover letter not found
|
||||
|
||||
Could not find cover-letter.md in the current directory.
|
||||
|
||||
Please either:
|
||||
1. Navigate to an application folder containing a cover letter:
|
||||
cd applications/pending/[application-folder]/
|
||||
/convert-cover-letter
|
||||
|
||||
2. Generate a cover letter first:
|
||||
/write-cover-letter
|
||||
|
||||
Current directory: [show current working directory]
|
||||
```
|
||||
STOP. Do not proceed.
|
||||
|
||||
## Step 2: Environment Validation
|
||||
|
||||
**CRITICAL**: Check for required tools before attempting conversion.
|
||||
|
||||
Show progress: `🔍 Checking system requirements...`
|
||||
|
||||
### Check for Pandoc
|
||||
|
||||
Run: `which pandoc` or `pandoc --version`
|
||||
|
||||
**If Pandoc is NOT installed:**
|
||||
|
||||
```
|
||||
❌ Pandoc not found
|
||||
|
||||
Pandoc is required for PDF conversion but is not installed on your system.
|
||||
|
||||
## Installation Instructions:
|
||||
|
||||
### Ubuntu/Debian:
|
||||
sudo apt update
|
||||
sudo apt install pandoc
|
||||
|
||||
### macOS (Homebrew):
|
||||
brew install pandoc
|
||||
|
||||
### Arch Linux:
|
||||
sudo pacman -S pandoc
|
||||
|
||||
### Windows:
|
||||
winget install pandoc
|
||||
|
||||
### Other Systems:
|
||||
Download from: https://pandoc.org/installing.html
|
||||
|
||||
---
|
||||
|
||||
After installation, run: pandoc --version
|
||||
Then try /convert-cover-letter again.
|
||||
```
|
||||
STOP. Do not proceed with conversion.
|
||||
|
||||
**If Pandoc is installed:**
|
||||
Show progress: `✓ Pandoc found (version X.X.X)`
|
||||
|
||||
### Check for LaTeX (pdflatex)
|
||||
|
||||
Run: `which pdflatex` or `pdflatex --version`
|
||||
|
||||
**If LaTeX is NOT installed:**
|
||||
|
||||
```
|
||||
❌ LaTeX not found
|
||||
|
||||
LaTeX (pdflatex) is required for PDF generation but is not installed.
|
||||
|
||||
## Installation Instructions:
|
||||
|
||||
### Ubuntu/Debian:
|
||||
sudo apt update
|
||||
sudo apt install texlive-latex-base texlive-lang-german texlive-latex-extra
|
||||
|
||||
### macOS (Homebrew):
|
||||
brew install texlive
|
||||
# Note: This is a large download (~4GB). Alternative: MacTeX from https://www.tug.org/mactex/
|
||||
|
||||
### Arch Linux:
|
||||
sudo pacman -S texlive-core texlive-latexextra
|
||||
|
||||
### Windows:
|
||||
# Install MiKTeX from: https://miktex.org/download
|
||||
# Or TeX Live from: https://www.tug.org/texlive/
|
||||
|
||||
---
|
||||
|
||||
**Required packages**:
|
||||
- texlive-latex-base (core LaTeX)
|
||||
- texlive-lang-german (Swiss German hyphenation and spelling)
|
||||
- texlive-latex-extra (KOMA-Script including scrlttr2)
|
||||
|
||||
After installation, run: pdflatex --version
|
||||
Then try /convert-cover-letter again.
|
||||
```
|
||||
STOP. Do not proceed with conversion.
|
||||
|
||||
**If LaTeX is installed:**
|
||||
Show progress: `✓ LaTeX found (pdfTeX X.X.X)`
|
||||
|
||||
**All prerequisites met:**
|
||||
Show progress: `✅ All requirements satisfied`
|
||||
|
||||
## Step 3: Read and Validate Cover Letter
|
||||
|
||||
Show progress: `📄 Reading cover-letter.md...`
|
||||
|
||||
### Read File Content
|
||||
|
||||
1. Read the entire `cover-letter.md` file
|
||||
2. Separate frontmatter (YAML between `---` delimiters) from body content
|
||||
3. Parse the YAML frontmatter
|
||||
4. Extract the markdown body (everything after the second `---`)
|
||||
|
||||
### Validate Frontmatter Structure
|
||||
|
||||
**Required fields:**
|
||||
- `from.name` - Sender's full name
|
||||
- `from.street` - Sender's street address
|
||||
- `from.city` - Sender's city with postal code (e.g., "CH-8000 Zürich")
|
||||
- `to` - Array of recipient address lines (at least 2 lines)
|
||||
- `subject` - Letter subject line
|
||||
- `opening` - Letter opening salutation
|
||||
- `closing` - Letter closing phrase
|
||||
- `signature` - Name for signature
|
||||
|
||||
**Optional fields:**
|
||||
- `from.phone` - Sender's phone number
|
||||
- `from.email` - Sender's email address
|
||||
- `date` - Letter date (defaults to current date if missing)
|
||||
- `enclosures` - Array of enclosure items
|
||||
|
||||
### Error: Invalid YAML
|
||||
|
||||
**If frontmatter cannot be parsed as YAML:**
|
||||
|
||||
```
|
||||
❌ Invalid frontmatter format
|
||||
|
||||
The YAML frontmatter in cover-letter.md could not be parsed.
|
||||
|
||||
Error details: [specific YAML parsing error]
|
||||
|
||||
Please check that:
|
||||
1. Frontmatter is enclosed between --- markers
|
||||
2. YAML syntax is correct (proper indentation, colons, hyphens)
|
||||
3. No special characters are unescaped
|
||||
|
||||
Example of correct frontmatter:
|
||||
---
|
||||
from:
|
||||
name: Max Mustermann
|
||||
street: Musterstrasse 42
|
||||
city: CH-8000 Zürich
|
||||
phone: +41 44 123 45 67
|
||||
email: max.mustermann@example.ch
|
||||
to:
|
||||
- Firma AG
|
||||
- Personalabteilung
|
||||
- Hauptstrasse 100
|
||||
- CH-3000 Bern
|
||||
subject: Bewerbung als Software-Entwickler
|
||||
opening: Sehr geehrte Damen und Herren
|
||||
closing: Freundliche Grüsse
|
||||
signature: Max Mustermann
|
||||
---
|
||||
```
|
||||
STOP.
|
||||
|
||||
### Error: Missing Required Fields
|
||||
|
||||
**If any required fields are missing:**
|
||||
|
||||
```
|
||||
❌ Incomplete frontmatter
|
||||
|
||||
Your cover letter is missing required fields in the frontmatter:
|
||||
|
||||
Missing fields:
|
||||
[List each missing field with description]
|
||||
|
||||
Required frontmatter structure:
|
||||
---
|
||||
from:
|
||||
name: [Your full name]
|
||||
street: [Your street address]
|
||||
city: [Your city with postal code]
|
||||
phone: [Your phone] (optional)
|
||||
email: [Your email] (optional)
|
||||
to:
|
||||
- [Company name]
|
||||
- [Department or contact person]
|
||||
- [Street address]
|
||||
- [City with postal code]
|
||||
date: [DD.MM.YYYY] (optional, defaults to today)
|
||||
subject: [Letter subject]
|
||||
opening: [Greeting, e.g., "Sehr geehrte Damen und Herren"]
|
||||
closing: [Sign-off, e.g., "Freundliche Grüsse"]
|
||||
signature: [Your name for signature]
|
||||
enclosures: (optional)
|
||||
- [Document 1]
|
||||
- [Document 2]
|
||||
---
|
||||
|
||||
[Body content follows]
|
||||
```
|
||||
STOP.
|
||||
|
||||
**If all required fields are present:**
|
||||
Show progress: `✓ Frontmatter validated`
|
||||
|
||||
## Step 4: Convert to PDF
|
||||
|
||||
Show progress: `🔄 Converting to PDF...`
|
||||
|
||||
### Prepare Pandoc Command
|
||||
|
||||
Build the conversion command:
|
||||
|
||||
```bash
|
||||
pandoc cover-letter.md \
|
||||
--from markdown \
|
||||
--to latex \
|
||||
--template=[path-to-swiss-letter.tex] \
|
||||
--pdf-engine=pdflatex \
|
||||
--output=cover-letter.pdf
|
||||
```
|
||||
|
||||
**Template path**: Use the template at `src/.claude/templates/swiss-letter.tex` relative to the framework root.
|
||||
|
||||
### Execute Conversion
|
||||
|
||||
1. Run the Pandoc command
|
||||
2. Capture stdout and stderr
|
||||
3. Check exit code
|
||||
|
||||
### Handle Conversion Errors
|
||||
|
||||
**If pdflatex fails to compile:**
|
||||
|
||||
```
|
||||
❌ PDF compilation failed
|
||||
|
||||
LaTeX encountered an error while compiling your cover letter.
|
||||
|
||||
Error output:
|
||||
[Show relevant error lines from stderr]
|
||||
|
||||
Common causes:
|
||||
1. Special characters not properly escaped (e.g., &, %, $, #, _)
|
||||
2. Formatting issues in the markdown body
|
||||
3. Very long lines or complex formatting
|
||||
4. Missing LaTeX packages
|
||||
|
||||
Troubleshooting:
|
||||
1. Check for special characters in your cover letter text
|
||||
2. Try simplifying complex formatting
|
||||
3. Verify that all required LaTeX packages are installed:
|
||||
- texlive-latex-base
|
||||
- texlive-lang-german
|
||||
- texlive-latex-extra
|
||||
|
||||
The intermediate .tex file has been preserved at: [path-to-.tex-file]
|
||||
You can inspect this file to identify the issue.
|
||||
|
||||
Need help? Share the error output above for assistance.
|
||||
```
|
||||
STOP.
|
||||
|
||||
**If file permissions error:**
|
||||
|
||||
```
|
||||
❌ Permission denied
|
||||
|
||||
Could not write cover-letter.pdf to the current directory.
|
||||
|
||||
Please check:
|
||||
1. You have write permissions in: [current directory]
|
||||
2. The file is not open in another application
|
||||
3. Sufficient disk space is available
|
||||
|
||||
Current directory permissions:
|
||||
[Show output of: ls -ld .]
|
||||
```
|
||||
STOP.
|
||||
|
||||
**If disk space error:**
|
||||
|
||||
```
|
||||
❌ Insufficient disk space
|
||||
|
||||
Could not create PDF file due to insufficient disk space.
|
||||
|
||||
Please:
|
||||
1. Free up disk space
|
||||
2. Or choose a different output location
|
||||
|
||||
Available disk space:
|
||||
[Show output of: df -h .]
|
||||
```
|
||||
STOP.
|
||||
|
||||
**If conversion times out (> 30 seconds):**
|
||||
|
||||
```
|
||||
❌ Conversion timeout
|
||||
|
||||
PDF generation took longer than expected (> 30 seconds).
|
||||
|
||||
This might indicate:
|
||||
1. Very long or complex document
|
||||
2. LaTeX compilation stuck on an error
|
||||
3. System resource constraints
|
||||
|
||||
Please try:
|
||||
1. Simplifying the cover letter content
|
||||
2. Checking system resources
|
||||
3. Running pdflatex manually for debugging
|
||||
|
||||
Manual debugging:
|
||||
pandoc cover-letter.md --from markdown --to latex --template=[template-path] -o cover-letter.tex
|
||||
pdflatex cover-letter.tex
|
||||
```
|
||||
STOP.
|
||||
|
||||
## Step 5: Success Output
|
||||
|
||||
**If conversion succeeded:**
|
||||
|
||||
```
|
||||
✅ PDF generated successfully: cover-letter.pdf
|
||||
|
||||
## Conversion Summary:
|
||||
|
||||
📄 Input: cover-letter.md
|
||||
📑 Output: cover-letter.pdf
|
||||
📐 Format: Swiss business letter (scrlttr2, Swiss Norm)
|
||||
|
||||
## Document Details:
|
||||
|
||||
✓ Sender: [from.name]
|
||||
✓ Recipient: [to[0]] (first line of address)
|
||||
✓ Subject: [subject]
|
||||
✓ Date: [date]
|
||||
$if(enclosures)$
|
||||
✓ Enclosures: [N] items
|
||||
$endif$
|
||||
|
||||
## Formatting Applied:
|
||||
|
||||
- Swiss address window positioning (compatible with standard Swiss envelopes)
|
||||
- Swiss German hyphenation and spelling
|
||||
- Professional business letter layout
|
||||
- Clickable email links
|
||||
- Proper margins and spacing
|
||||
|
||||
## Next Steps:
|
||||
|
||||
1. **Review the PDF**: Open cover-letter.pdf and verify:
|
||||
- All information is correct
|
||||
- Layout looks professional
|
||||
- Address fits in envelope window (if printing)
|
||||
- No formatting issues or typos
|
||||
|
||||
2. **Print test** (if mailing):
|
||||
- Print the PDF
|
||||
- Check address position with a Swiss envelope window
|
||||
- Verify text is clear and readable
|
||||
|
||||
3. **Digital submission**:
|
||||
- Ready to attach to email applications
|
||||
- Filename: cover-letter.pdf
|
||||
|
||||
4. **Rename for organization** (optional):
|
||||
- Consider: [YourName]_Cover_Letter_[Company]_[Date].pdf
|
||||
- Example: Max_Mustermann_Cover_Letter_TechCorp_2025-11-03.pdf
|
||||
|
||||
---
|
||||
|
||||
**Tip**: The generated PDF follows Swiss standards (SN) for business correspondence.
|
||||
The address positioning is optimized for standard Swiss envelope windows.
|
||||
```
|
||||
|
||||
## Step 6: Cleanup (Optional)
|
||||
|
||||
Pandoc may create intermediate files during conversion:
|
||||
- `cover-letter.tex` - Intermediate LaTeX file
|
||||
- `cover-letter.aux`, `cover-letter.log` - LaTeX compilation files
|
||||
|
||||
**If conversion was successful**: These files can be deleted (they're not needed)
|
||||
**If conversion failed**: Preserve the .tex file for debugging
|
||||
|
||||
## Additional Features
|
||||
|
||||
### Custom Template
|
||||
|
||||
Users can customize the Swiss letter template:
|
||||
|
||||
**Location**: `src/.claude/templates/swiss-letter.tex`
|
||||
|
||||
**Customizable elements** (documented in template):
|
||||
- Font size (currently 11pt)
|
||||
- Margins and spacing
|
||||
- Sender address alignment
|
||||
- Header/footer content
|
||||
- Color scheme (currently black/white)
|
||||
|
||||
**To customize**:
|
||||
1. Edit `src/.claude/templates/swiss-letter.tex`
|
||||
2. Re-run `/convert-cover-letter` to apply changes
|
||||
3. Template comments explain each section
|
||||
|
||||
### Multiple Conversions
|
||||
|
||||
Users can run `/convert-cover-letter` multiple times:
|
||||
- Overwrites existing `cover-letter.pdf` (no prompt needed)
|
||||
- Use this after editing `cover-letter.md`
|
||||
- Quick iteration on formatting and content
|
||||
|
||||
## Error Handling Summary
|
||||
|
||||
| Error Condition | User Action Required |
|
||||
|-----------------|---------------------|
|
||||
| No cover-letter.md | Navigate to application folder or run /write-cover-letter |
|
||||
| Pandoc missing | Install Pandoc via package manager |
|
||||
| LaTeX missing | Install texlive-latex-base, texlive-lang-german, texlive-latex-extra |
|
||||
| Invalid YAML | Fix frontmatter syntax in cover-letter.md |
|
||||
| Missing required fields | Add missing fields to frontmatter |
|
||||
| LaTeX compilation error | Check for special characters, inspect .tex file |
|
||||
| Permission denied | Check directory write permissions |
|
||||
| Disk space | Free up disk space |
|
||||
| Timeout | Simplify content or debug manually |
|
||||
|
||||
## Important Notes
|
||||
|
||||
### Swiss Letter Standards
|
||||
|
||||
The conversion follows Swiss Norm (SN) for business letters:
|
||||
- **Address window**: Positioned for standard Swiss envelope windows
|
||||
- **Date format**: DD.MM.YYYY (Swiss convention)
|
||||
- **Layout**: KOMA-Script scrlttr2 with Swiss configuration
|
||||
- **Language support**: Swiss German hyphenation and special characters (ä, ö, ü, ß)
|
||||
|
||||
### File Compatibility
|
||||
|
||||
**Input format**: Markdown with YAML frontmatter
|
||||
**Output format**: PDF (via LaTeX)
|
||||
**Template engine**: Pandoc with custom LaTeX template
|
||||
|
||||
### System Requirements
|
||||
|
||||
**Minimum**:
|
||||
- Pandoc 2.0+
|
||||
- LaTeX (TeX Live or MiKTeX)
|
||||
- Basic LaTeX packages (texlive-latex-base, texlive-lang-german, texlive-latex-extra)
|
||||
|
||||
**Recommended**:
|
||||
- Latest Pandoc version
|
||||
- Full TeX Live installation (includes all packages)
|
||||
- ~500MB disk space for full LaTeX installation
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
**Common issues**:
|
||||
1. **"File not found"** → Run command from application directory
|
||||
2. **"Pandoc not found"** → Install Pandoc first
|
||||
3. **"LaTeX error"** → Check for special characters in text
|
||||
4. **"Missing font"** → Install texlive-fonts-recommended
|
||||
5. **"Address doesn't fit window"** → Verify Swiss envelope type, check template settings
|
||||
|
||||
**For advanced users**:
|
||||
- Intermediate .tex file shows LaTeX source
|
||||
- Run `pdflatex cover-letter.tex` manually for detailed error output
|
||||
- Edit swiss-letter.tex template for custom layouts
|
||||
|
||||
---
|
||||
|
||||
**Remember**: The goal is to produce a professional, Swiss-standard business letter PDF that is ready for submission with job applications. Quality and formatting accuracy are paramount.
|
||||
@@ -0,0 +1,120 @@
|
||||
% Swiss Business Letter Template for Pandoc
|
||||
% This template follows Swiss standards (SN) for business correspondence
|
||||
% Compatible with KOMA-Script scrlttr2 class
|
||||
|
||||
\documentclass[
|
||||
fontsize=11pt,
|
||||
paper=a4,
|
||||
parskip=half, % Half line spacing between paragraphs
|
||||
fromalign=right, % Sender address aligned right
|
||||
fromphone, % Include phone number
|
||||
fromemail, % Include email address
|
||||
version=last % Use latest KOMA-Script features
|
||||
]{scrlttr2}
|
||||
|
||||
% ==================== ENCODING & FONTS ====================
|
||||
% UTF-8 input encoding for special characters (ä, ö, ü, ß)
|
||||
\usepackage[utf8]{inputenc}
|
||||
% T1 font encoding for proper hyphenation and special characters
|
||||
\usepackage[T1]{fontenc}
|
||||
|
||||
% ==================== HYPERLINKS ====================
|
||||
% Enable clickable email links in PDF
|
||||
\usepackage{hyperref}
|
||||
\hypersetup{
|
||||
colorlinks=true,
|
||||
urlcolor=black,
|
||||
pdfborder={0 0 0}
|
||||
}
|
||||
|
||||
% ==================== PANDOC COMPATIBILITY ====================
|
||||
% Define tightlist command for Pandoc-generated lists
|
||||
\providecommand{\tightlist}{%
|
||||
\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
|
||||
|
||||
% ==================== SWISS LETTER CONFIGURATION ====================
|
||||
% Load Swiss Norm (SN) for proper address window positioning
|
||||
% This ensures the recipient address fits in standard Swiss envelope windows
|
||||
\LoadLetterOption{SN}
|
||||
|
||||
% ==================== SENDER INFORMATION ====================
|
||||
% Customize these fields by editing the markdown frontmatter
|
||||
\setkomavar{fromname}{$from.name$}
|
||||
\setkomavar{fromaddress}{$from.street$\\$from.city$}
|
||||
|
||||
$if(from.phone)$
|
||||
\setkomavar{fromphone}{$from.phone$}
|
||||
$endif$
|
||||
|
||||
$if(from.email)$
|
||||
% Email is rendered as a clickable hyperlink
|
||||
\setkomavar{fromemail}{\href{mailto:$from.email$}{$from.email$}}
|
||||
$endif$
|
||||
|
||||
% ==================== LETTER METADATA ====================
|
||||
$if(subject)$
|
||||
\setkomavar{subject}{$subject$}
|
||||
$endif$
|
||||
|
||||
$if(date)$
|
||||
\setkomavar{date}{$date$}
|
||||
$endif$
|
||||
|
||||
$if(signature)$
|
||||
\setkomavar{signature}{$signature$}
|
||||
$endif$
|
||||
|
||||
% ==================== ENCLOSURES ====================
|
||||
$if(enclosures)$
|
||||
% Separator between "Enclosures" label and list (e.g., "Anlagen:")
|
||||
\setkomavar{enclseparator}{:}
|
||||
$endif$
|
||||
|
||||
% ==================== CUSTOMIZATION OPTIONS ====================
|
||||
% Remove backaddress line in envelope window (uncomment to enable)
|
||||
\setkomavar{backaddress}{}
|
||||
|
||||
% You can customize additional elements here:
|
||||
% - \setkomavar{location}{} - Text in the upper right corner
|
||||
% - \setkomavar{place}{} - Place of writing (if different from address)
|
||||
% - Font sizes, margins, spacing can be adjusted via KOMA-Script options
|
||||
|
||||
% ==================== DOCUMENT BODY ====================
|
||||
\begin{document}
|
||||
|
||||
\begin{letter}{%
|
||||
% Recipient address - each line from the 'to' frontmatter array
|
||||
$for(to)$
|
||||
$to$\\
|
||||
$endfor$
|
||||
}
|
||||
|
||||
% ==================== OPENING ====================
|
||||
$if(opening)$
|
||||
\opening{$opening$}
|
||||
$else$
|
||||
% Default opening if not specified
|
||||
\opening{Sehr geehrte Damen und Herren}
|
||||
$endif$
|
||||
|
||||
% ==================== LETTER BODY ====================
|
||||
% The main content from your markdown file is inserted here
|
||||
$body$
|
||||
|
||||
% ==================== CLOSING ====================
|
||||
$if(closing)$
|
||||
\closing{$closing$}
|
||||
$else$
|
||||
% Default closing if not specified
|
||||
\closing{Freundliche Grüsse}
|
||||
$endif$
|
||||
|
||||
% ==================== ENCLOSURES LIST ====================
|
||||
$if(enclosures)$
|
||||
% List of attached documents (e.g., CV, certificates)
|
||||
\encl{$for(enclosures)$$enclosures$$sep$\\$endfor$}
|
||||
$endif$
|
||||
|
||||
\end{letter}
|
||||
|
||||
\end{document}
|
||||
Reference in New Issue
Block a user