⚠️ Experimental Project Disclaimer This project was developed primarily using "vibe coding" techniques with Claude Code - an experimental approach to rapid prototyping and development through AI-assisted coding conversations. This serves as a proof of concept for this development methodology and should be considered experimental software.
A FastAPI-based web API that converts markdown documents with embedded images to professionally branded PDF files. The API accepts ZIP archives containing markdown files and associated images, processes them with company branding templates, and returns high-quality PDF documents with proper page break handling.
- RESTful API: Built with FastAPI for high performance and automatic documentation
- ZIP File Processing: Accepts ZIP archives containing markdown and image files
- Professional PDF Generation: Creates branded PDFs with company logos, headers, and footers
- Image Support: Embeds images from ZIP archives into generated PDFs
- Page Break Control: Intelligent page breaks with customizable CSS classes
- Table of Contents: Auto-generated TOC from markdown headers
- Input Validation: Comprehensive file type and size validation
- Error Handling: Detailed error responses with proper HTTP status codes
- Logging: Structured logging with Loguru
- Web Interface: Clean Vue 3 single-page application
- Drag & Drop Upload: Intuitive file upload with validation feedback
- Real-time Progress: Step-by-step generation progress with loading states
- File Management: Upload single markdown file with multiple image files
- ZIP Creation: Client-side ZIP file creation from uploaded files
- Error Handling: User-friendly error messages and validation
- API Status: Live API health monitoring and status display
- Dark Theme: Built with Nuxt UI for consistent dark styling
- Docker Support: Containerized deployment with Docker Compose
- Multi-service: Separate containers for API and frontend
- Production Ready: Nginx reverse proxy and optimized builds
- Framework: FastAPI 0.104+
- Package Manager: Poetry
- Server: Uvicorn (ASGI)
- Python Version: 3.11+
- PDF Generation: WeasyPrint
- Markdown Processing: Python-Markdown with extensions
- Logging: Loguru
- Containerization: Docker
- Framework: Vue 3
- Build Tool: Vite
- UI Library: Nuxt UI
- Utilities: VueUse
- HTTP Client: Ky
- File Handling: JSZip, File-Saver
- Code Quality: ESLint with Antfu config
- Web Server: Nginx (production)
- Clone the repository:
git clone <repository-url>
cd test-md-to-pdf
- Build and run both services:
docker-compose up --build
- Access the application:
- Frontend UI: http://localhost:3000
- API Documentation: http://localhost:8000/docs
- Health Check: http://localhost:8000/health
- API Info: http://localhost:8000/api/v1/info
- Install Poetry:
curl -sSL https://install.python-poetry.org | python3 -
- Set up the backend:
poetry install
cp .env.example .env
- Start the API development server:
poetry run uvicorn app.main:app --reload
- Navigate to the client directory:
cd client
- Install dependencies:
npm install
- Start the frontend development server:
npm run dev
The frontend will be available at http://localhost:3000 and will proxy API calls to the backend.
POST /api/v1/convert
Parameters:
file
(required): ZIP file containing markdown and imagestitle
(required): Document title for cover pageinclude_toc
(optional): Include table of contents (default: true)
Response: PDF file as binary stream
GET /health
Response:
{
"status": "healthy",
"timestamp": "2024-01-15T10:30:00Z",
"version": "1.0.0"
}
GET /api/v1/info
Response:
{
"name": "Markdown to PDF Converter API",
"version": "1.0.0",
"supported_formats": ["zip"],
"max_file_size": "50MB",
"supported_images": ["png", "jpg", "jpeg"],
"page_formats": ["A4"]
}
GET /api/v1/status
Response:
{
"status": "operational",
"uptime": "2h 30m 15s",
"version": "1.0.0",
"environment": "production"
}
Your ZIP file should contain markdown files and images:
project_proposal.zip
├── main.md # Main markdown file (required)
├── images/ # Images directory
│ ├── logo.png
│ ├── chart1.jpg
│ └── screenshot.png
- Markdown:
.md
,.markdown
- Images:
.png
,.jpg
,.jpeg
- Maximum file size: 50MB
- Maximum extracted size: 200MB
- Headers (H1-H6)
- Paragraphs and line breaks
- Bold and italic text
- Lists (ordered and unordered)
- Links
- Code blocks and inline code
- Tables
- Blockquotes
<!-- Force page break -->
<div class="page-break"></div>
<!-- Keep content together -->
<div class="keep-together">
Content that should not be split across pages
</div>
<!-- Avoid page break before element -->
<div class="avoid-break-before">
Content
</div>


<img src="images/chart.png" alt="Alt text">
Create a .env
file based on .env.example
:
# API Configuration
API_HOST=0.0.0.0
API_PORT=8000
API_ENV=development
DEBUG=true
# File Upload Limits
MAX_FILE_SIZE=52428800 # 50MB
MAX_EXTRACTED_SIZE=209715200 # 200MB
UPLOAD_TIMEOUT=300 # 5 minutes
# PDF Generation
COMPANY_NAME="Your Company Name"
DEFAULT_PAGE_FORMAT=A4
# Logging
LOG_LEVEL=INFO
# Security
ALLOWED_ORIGINS=["http://localhost:3000"]
The API uses a hardcoded company template that includes:
- Cover Page: Company logo and branding
- Headers/Footers: Company name and page numbers
- Color Scheme: Professional brand colors
- Typography: Company fonts and styling
To customize the branding, modify:
app/templates/company_template.css
app/templates/cover_page.html
app/templates/assets/logo.svg
The project includes tools for maintaining code quality:
# Format code
poetry run black app/
# Sort imports
poetry run isort app/
# Lint code
poetry run flake8 app/
# Type checking
poetry run mypy app/
markdown-pdf-api/
├── app/ # Main application package
│ ├── main.py # FastAPI application entry point
│ ├── config.py # Configuration management
│ ├── models/ # Pydantic models
│ │ ├── requests.py # Request models
│ │ └── responses.py # Response models
│ ├── services/ # Business logic
│ │ ├── pdf_generator.py # PDF generation service
│ │ └── zip_processor.py # ZIP file processing
│ ├── utils/ # Utility functions
│ │ ├── file_helpers.py # File manipulation utilities
│ │ └── validators.py # Input validation
│ └── templates/ # PDF templates
│ ├── company_template.css # Company template styles
│ ├── cover_page.html # Cover page template
│ └── assets/ # Company assets
├── scripts/ # Development scripts
├── logs/ # Log files
├── pyproject.toml # Poetry configuration
├── Dockerfile # Docker configuration
└── README.md # Project documentation
- Update environment variables for production
- Use the deployment script:
chmod +x scripts/deploy.sh
./scripts/deploy.sh
For production deployment with Docker:
# Build the image
docker build -t markdown-pdf-api .
# Run with production settings
docker run -d \
--name markdown-pdf-api \
--restart unless-stopped \
-p 8000:8000 \
-v $(pwd)/logs:/app/logs \
-e API_ENV=production \
-e DEBUG=false \
-e LOG_LEVEL=INFO \
markdown-pdf-api
The API returns standardized error responses:
{
"error": {
"code": "INVALID_FILE_FORMAT",
"message": "The uploaded file must be a ZIP archive",
"details": {
"received_type": "text/plain",
"expected_types": ["application/zip"]
},
"timestamp": "2024-01-15T10:30:00Z",
"request_id": "req_123456789"
}
}
INVALID_FILE_FORMAT
: File is not a ZIP archiveFILE_TOO_LARGE
: File exceeds size limitNO_MARKDOWN_FOUND
: No markdown files in ZIPINVALID_MARKDOWN
: Markdown parsing errorIMAGE_NOT_FOUND
: Referenced image missingPDF_GENERATION_FAILED
: PDF creation error
- Small files (<1MB): < 5 seconds
- Medium files (1-10MB): < 15 seconds
- Large files (10-50MB): < 60 seconds
- Health check: < 100ms
- Concurrent requests: 10+ simultaneous conversions
- Memory usage: < 1GB per conversion
- Horizontal scaling support
- Stateless design
- File type validation using magic bytes
- File size limits enforcement
- Input sanitization and validation
- CORS configuration
- Security headers
- Non-root container user
The application provides structured logging and metrics:
- Request/response logging
- Performance metrics
- Error tracking
- Health monitoring
Log files are stored in the logs/
directory:
app.log
: General application logserrors.log
: Error-specific logs
For issues and questions:
- Check the API documentation at
/docs
- Review the logs in the
logs/
directory - Verify your ZIP file structure and content
- Ensure all required dependencies are installed
This project is licensed under the MIT License.