Convert architectural PDF drawings into editable CAD outputs with a FastAPI/Celery conversion pipeline and a Next.js upload, preview, history, and download UI.
DrawLift is a proof-of-concept full-stack application for PDF-to-CAD conversion. Users upload architectural drawings, choose 2D or 3D options, watch progress over Server-Sent Events, preview extracted pages and generated GLB models, then download DXF, optional DWG, or GLB artifacts.
The project is intentionally scoped as a realistic local/dev stack: Next.js frontend, FastAPI API, Celery worker, Redis, PostgreSQL, local file storage, and an operator-supplied DWG converter command when true DWG output is required.
Do not expose this proof-of-concept directly to untrusted public traffic without adding authentication, quota controls, malware scanning, and production storage hardening.
Current protections include PDF extension/MIME validation, Pydantic config validation, safe page-image path resolution, generated-file download checks, CORS configuration, environment-based secrets, and cleanup of expired job files. See docs/security.md for the scoped security model, risks, and recommended production controls.
Architectural PDF drawings often arrive as rasterized plans or mixed vector/raster documents. Turning those drawings into usable CAD is a multi-step process:
- Render PDF pages into images.
- Clean and normalize image data.
- Segment walls, doors, windows, rooms, and text.
- Vectorize detected features into CAD primitives.
- Export layered DXF, optionally generate 3D geometry and GLB, and optionally convert DXF to DWG.
DrawLift separates the interactive web workflow from CPU-heavy conversion work. FastAPI handles validation, job metadata, file serving, and SSE. Celery runs the conversion pipeline out of band so long-running conversions do not block HTTP requests.
- Docker and Docker Compose
- Python 3.11 for host backend development
- Node.js and npm for host frontend development
- Optional:
libredwg/dwgwriteor ODA FileConverter for true DWG output
cp .env.example .envThe default Docker Compose stack uses PostgreSQL, Redis, local storage volumes, and NEXT_PUBLIC_API_URL=http://localhost:8000.
make docker-up
make docker-migrate
open http://localhost:3000Useful endpoints:
- Frontend: http://localhost:3000\
- Backend health: http://localhost:8000/api/v1/health\
- OpenAPI docs: http://localhost:8000/docs\
Run PostgreSQL, Redis, and the worker in Docker while running FastAPI and Next.js on the host:
cd backend
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cd ../frontend
npm install
cd ..
make local-upStop services with:
make local-down- Open http://localhost:3000\.
- Drop or select a PDF file.
- Choose conversion options:
mode:2dor3ddpi: render resolution, constrained by backend validationfloor_height_m,slab_thickness_m, andinclude_ceilingfor 3D jobsoutput_format:dxf,dwg, orbothsegmenter:classicorml
- Submit the job and watch progress on
/jobs/{id}. - Preview extracted page images and, for 3D jobs, the GLB model.
- Download available outputs after completion.
- Use
/historyto filter, reopen, retry failed jobs, or delete jobs.
DWG is proprietary, so DrawLift does not bundle converter binaries. Configure an external converter command when needed:
DWG_CONVERTER_COMMAND='dwgwrite {input} {output}' make docker-upSupported placeholders are {input}, {output}, {input_dir}, {output_dir}, and {stem}. The optional Compose profile can be started with:
make docker-up-dwgBase path: /api/v1.
| Method | Endpoint | Description |
|---|---|---|
GET |
/health |
Liveness/readiness check. |
POST |
/jobs |
Upload a PDF and JSON config as multipart form data. |
GET |
/jobs |
List jobs, newest first, with pagination and optional status filtering. |
GET |
/jobs/{job_id} |
Fetch status, progress, config, timestamps, errors, and page count. |
GET |
/jobs/{job_id}/stream |
Server-Sent Events progress stream backed by Redis Pub/Sub. |
GET |
/jobs/{job_id}/pages/{page_number} |
Serve an extracted page PNG. |
GET |
/jobs/{job_id}/download?format=dxf|dwg|glb |
Download completed outputs. |
POST |
/jobs/{job_id}/retry |
Re-enqueue a failed job with its stored input/config. |
DELETE |
/jobs/{job_id} |
Delete job metadata and local job files. |
Example upload:
curl -X POST http://localhost:8000/api/v1/jobs \
-F 'file=@floorplan.pdf;type=application/pdf' \
-F 'config={"mode":"3d","dpi":300,"floor_height_m":3.0,"output_format":"both","segmenter":"classic"}'See docs/api.md for payloads, status semantics, and edge cases.
Important content from the previous README, TODO, and implementation plan has been split into focused documents:
- Architecture — system boundaries, diagrams, data flow, job lifecycle.
- API — REST/SSE contract and examples.
- Pipeline — conversion stages, config, outputs, and edge cases.
- Design system — frontend conventions, tokens, shared components, accessibility.
- Security — scoped controls, concerns, and production hardening.
- Trade-offs and alternatives — decision log and alternatives considered.
- Risks and edge cases — realistic operational and product risks.
- Operations — setup, service management, tests, migrations, cleanup.
- Roadmap — distilled project stages from
TODO.mdand implementation plan.
TODO.md remains the detailed issue-tracking source of truth for user stories and historical progress.
flowchart LR
UI[Next.js frontend] -->|REST upload/status| API[FastAPI API]
UI -->|SSE progress| API
API -->|metadata| DB[(PostgreSQL)]
API -->|enqueue| Redis[(Redis broker/pubsub)]
Worker[Celery worker] -->|consume jobs| Redis
Worker -->|read/write status| DB
Worker -->|PDF/pages/DXF/DWG/GLB| Storage[(Local storage volume)]
API -->|serve pages/downloads| Storage
The worker composes pluggable pipeline steps: PDF parsing, preprocessing, segmentation, vectorization, optional extrusion, DXF writing, optional GLB writing, and optional DWG conversion.
Common quality checks:
cd backend
ruff check .
ruff format --check .
mypy app/
pytest
cd ../frontend
npm run lint
npm run format:check
npm run buildRoot service commands:
make help
make docker-up
make docker-down
make local-up
make local-status
make logs-workerIssues, branches, and PRs are managed in GitHub. Use the user-story and issue conventions documented in TODO.md and the delivery summary in docs/roadmap.md.
Before opening a PR:
- Keep changes scoped to one issue or concern.
- Run relevant backend/frontend checks.
- Update docs when behavior, commands, API payloads, or trade-offs change.
- Reference the GitHub issue in the branch name or PR body where applicable.