PDF in, quantities out.
pdf2takeoff is free, open-source (Apache-2.0) construction takeoff software that measures and counts PDF drawings, scans and DXF files in your browser at pdf2takeoff.com, without uploading them, plus a headless Node CLI and an MCP server for AI agents built on the same engine.
Try it
- In the browser: open pdf2takeoff.com. No account, no install; open a drawing or try the sample sheet.
- From the command line (Node 22.13+, runs straight from GitHub):
npx github:ajflow/pdf2takeoff info drawing.pdf - As an MCP server for AI agents (Claude Code):
claude mcp add pdf2takeoff -- npx -y github:ajflow/pdf2takeoff mcp
pdf2takeoff is quantity takeoff software for estimators, quantity surveyors, builders and trades. Open a drawing, set its scale, and measure lengths, areas, perimeters, volumes, angles and counts. Totals group by label and export to CSV or Excel. Markups save back into the PDF as standard annotations that open in Bluebeam Revu and Adobe Acrobat, so a takeoff moves between pdf2takeoff and the tools a team already uses.
It is free: no account, no file limits and no paid tier. Processing is local: drawings are never uploaded, the app has no telemetry (the website counts page visits with cookie-free Cloudflare Web Analytics, which never sees a drawing), and the app keeps working offline once the page has loaded, so it suits confidential tender documents. It is made by Flowtivity on the Gold Coast, Australia.
- Opens the files you're sent: PDFs, including scanned PDFs (CCITT fax, JBIG2 and JPEG 2000 images); PNG, JPEG, HEIC (iPhone photos), TIFF (multi-page, Group 4 scans included), WebP, GIF, BMP, AVIF and SVG images; pasted screenshots; and several files at once as one set.
- CAD drawings (DXF): an ASCII DXF opens as a vector page with its scale already set from the file's units, and Scan counts its blocks exactly. For DWG, export a DXF or PDF first.
- Scale you can check: pick a preset, click a known dimension, or let OCR read the title-block scale or a printed dimension off the sheet. A scale chip shows whether the current scale can be trusted. On CAD sheets it also measures the printed dimensions against their drawn lines, so a title block that says 1:100 on a sheet drawn at 1:50 is caught before it doubles every length.
- Measure: length, area, perimeter, volume, angle and count. Magic Fill reads a room's area off the walls with one click inside it.
- Scan (auto-count symbols): click one symbol and every match on the page is counted in about 100 ms by local computer vision, with an optional in-browser ONNX detector for cluttered sheets. Results arrive review-first (Keep all, Review, Undo), and doubtful hits stay flagged for a person to check.
- Bluebeam and Acrobat round-trip: save markups into the PDF as standard annotations; reopen the file and the takeoff comes back.
- Export and organise: CSV, Excel (.xlsx) and a printable quantity report; custom formula columns (waste factors, rates), statuses and layers.
- Revision compare: overlay two revisions of a sheet to see what was added and what was removed.
- A focused workspace: light and dark themes, a ⌘K command bar, a quantities panel with live totals, and live sessions that share a takeoff between browser tabs.
- For AI agents and scripts: a headless Node CLI and an MCP server with 15 tools, so Claude, Cursor, Codex and other MCP clients can run a takeoff locally and hand back a marked-up PDF for a person to review. Optional AI counting from the CLI uses your own model key (OpenRouter, OpenAI, Gemini or a local model) and sends only the page you choose to the provider you choose; every AI item arrives flagged for confirmation.
For PDF takeoff, yes: pdf2takeoff is a free, open-source alternative to paid takeoff tools such as Bluebeam Revu, PlanSwift and On-Screen Takeoff. It does not match every feature of those products. The feature-by-feature position against Bluebeam Revu, including what we deliberately won't build, is in docs/feature-comparison.md.
The licence is Apache-2.0. To our knowledge pdf2takeoff is the only permissively licensed takeoff engine in a field of AGPL competitors, so you can embed and bundle it.
We publish what the engine scores, how every number was produced, and what it is still bad at, including a benchmark we built and gave away (CC BY 4.0, usable by anyone, including to beat us):
- ScaledDrawingBench v0.1 (ours, deterministic, seeded): 99.8% on born-digital (CAD-exported) sheets, 37.3% on degraded scans (up from 23.0%; the improvement log is part of the disclosure), 0 hallucinations on 92 unanswerable-by-design questions (benchmark)
- AECV-Bench (external, public): 0.295 five-field exact match with a deterministic engine and no vision model; bedroom/toilet counts at 0.708/0.500 from a label dictionary + OCR alone. With the opt-in AI count on GPT-6.1 Sol: 0.585, against 0.572 for the same model on the benchmark's own prompt and 0.543 for the best published model, so most of that gain is the model (details)
- AEC-Geometric-Bench (external, released scorer): wall pixel F1 0.519, about 1.8× the best frontier VLM on the identical sheets (0.18 to 0.29), area precision 0.943, object F1 0.031 at the frontier-VLM range; a click-free deterministic engine, no GPU, ~23 s per sheet (details)
- ResPlan-FP (external, public, 1,000-plan test split): wall F1 0.635 (tolerance 0.05) from the deterministic engine with no model, no GPU and no clicks. A fine-tuned wall model scores 0.968 and Gemini 3.1 Pro 0.811 on a 200-plan subset, so this is a published gap, not a lead (details)
- ~100 ms CV scan, 1.6 s cached in-browser ONNX scan, 76 MB model: in-browser, nothing uploaded
- A correctness gate on every push: ten behavioural checks + the takeoff bench + MCP smoke + PDF round-trip in CI
Everything regenerates from this repo with one command per table; see evals/README.md.
Phase plan: the Roadmap below. Feature-by-feature position against Bluebeam Revu, and what we deliberately won't build: docs/feature-comparison.md. Hosting cost model: docs/hosting-cost-controls.md. Licensing rationale: Why open source?.
- Viewer: open any PDF drawing; zoom, pan, multi-page (PDF.js)
- Images and scans too: open, drop or paste (⌘V / Ctrl+V) PNG, JPEG, HEIC (iPhone photos; decoded locally with libheif, LGPL-3.0, loaded only when a HEIC is opened, where the browser can't decode it), TIFF (multi-page; black-and-white Group 4 scans pass straight through, never decoded), WebP, GIF, BMP, AVIF or SVG; drop several files (images and PDFs) to open them as one set in file-name order. Each image becomes a page (wrapped as a PDF, so every tool, export and Save into PDF works). A scan that states its resolution (300 dpi and so on) keeps its paper size, so printed ratios like 1:100 apply; other images are calibrated from a dimension, and ratio presets refuse rather than give wrong lengths.
- CAD (DXF): an ASCII DXF opens as a vector page at a standard scale (1:50, 1:100, ...) worked out from its
$INSUNITS, so its scale is set with no calibration (MCPopen_documentreports it and pre-sets it too). Blocks are expanded through their inserts, so Scan counts doors and fittings exactly; TEXT and MTEXT become real text wrapped to its box; hatches show as tones beneath the linework; frozen and off layers and paper space are left out. DWG gets a plain "export DXF or PDF" message. - Scale calibration: click both ends of a known dimension and type its real length, or pick a preset (1:50 … 1:500, 1/8″ = 1′-0″, 1/4″ = 1′-0″, 1″ = 10′/20′/30′). Each page keeps its own scale. Metric and imperial. Multi-scale viewports (W): outline a region drawn at a different scale (a 1:20 detail on a 1:100 sheet) and measurements inside it resolve to that scale automatically.
- OCR auto-read: click both ends of a printed dimension and hit Auto-read from sheet — the value ("12.00") or the title-block scale ("1:100") is read straight off the drawing. First use fetches the OCR language pack (~4 MB, then cached); drawings still never leave your machine.
- Takeoff tools: Length (segmented runs), Area, Perimeter, Volume (area × depth), Angle (scale-free) and Count, with live measurements on the drawing and per-label group totals
- Scan (auto-count): press
Sand click one instance of a repeated symbol: a local computer-vision pass (rasterize → binarize → connected components → shape matching) finds and counts every match on the page in ~100 ms, no model download and nothing uploaded. Hits land as flagged proposals (⚑) — confirm (✓) or delete them, and the totals show a live kept-rate per label (kept/proposed: every deletion is a judged false positive), so the tool's accuracy on your symbols is always visible. A trained v1 detector ships with the site (five classes: downlight, GPO, switch, smoke alarm, diffuser — manifest inpublic/models/symbols.json): on cluttered sheets the same click upgrades automatically to an in-browser ONNX detector (onnxruntime-web, WASM) that labels hits by class, with automatic fallback to the CV pass if the model can't load. The ~76 MB model is fetched once from GitHub and cached; drawings still never leave your machine. Self-hosting or retraining: drop your ownsymbols.onnx+symbols.jsonintopublic/models/— the pipeline is intraining/. - Markups list: rename labels, jump to page, delete; flag / confirm statuses that round-trip through the PDF; filter by status, search by label, sort; one-click Clear; export CSV, a real Excel workbook (.xlsx), or copy straight into a spreadsheet
- Printable report: one click builds a print-ready quantity report: every marked sheet re-rendered with its markup overlay, per-sheet quantity tables, and group totals; print or save as PDF from the browser
- Editing: drag whole markups or individual vertices with live value updates; Ctrl+Z restores the pre-drag geometry; calibrate once and apply the scale to every page in the set
- Magic Fill: click inside a room to read its area off the walls (flood fill + boundary trace). On CAD PDFs that keep the architect's layers, furniture, finish hatching, notes, services and structure-above layers are left out of the fill's own render (what you see is unchanged), so a furnished plan fills to the walls; if that lets a fill run through a doorway into another named room, it fills on every layer instead. Doorways between wall ends close automatically: from the calibrated scale, from room labels in the text layer, from the vector layer (any angle), or, on an uncalibrated scan with none of those, from the door width the sheet itself shows. Wide open-plan openings stay open; a diagonal door on a scan with no vector layer can still leak
- Custom columns: formula columns over quantities (waste factors, rates) in totals and every export;
value * 1.1style, no code - Notes: pin a note anywhere (T); round-trips through the PDF
- Layers: place per trade and hide layers while measuring the next; layer names ride inside the PDF
- Tool library: save a tool + label once (e.g. Length "Skirting", Count "Downlight DL-100"), then click the chip to measure pre-labelled — every placement and scan hit carries the label; presets persist locally across sessions
- Trade templates: load a trade's price sheet (commercial fit-out, electrical or painting) and every line becomes a labelled tool; the template summary exports every line in the sheet's order with its quantity (0 where nothing was measured), as Excel or copied for pasting into an estimating spreadsheet. Feature-wall and wall-paint lines turn wall length into paint area (× 2.7 m). MCP
exporttakestemplatefor the same summary. - Find walls: on CAD PDFs that keep their layers, read the wall runs off the wall layers and name them from the sheet's legend ("proposed partition", "half height", "existing partition"). Each piece is measured along its centreline (curved walls too, corner squares included) and proposed as a flagged length for you to confirm or delete; "Walls (…)" labels count toward a template's Walls line. Existing walls are left out unless asked for (MCP
propose_wallswith include "all"). Drawings that draw walls as plain outlines say so instead of guessing. - Multi-sheet sets: page filmstrip with lazily rendered thumbnails and per-page calibration badges; the bundled sample carries two sheets at different scales (1:100 and 1:50) to show per-page scales
- Live sessions: hit Live and every tab/window with the same drawing shares the takeoff in real time — placements, edits (rename/status/drag), deletions, calibrations and presence (Yjs CRDT over BroadcastChannel; cross-machine relay is the next phase)
- Sessions persist: markups and calibrations autosave to IndexedDB keyed by the file's hash, so a refresh or re-open restores your takeoff (PDF annotations still take precedence when present); the PDF itself is never stored or uploaded
- Save into the PDF: markups are written as standard Line / PolyLine / Polygon / FreeText annotations (pdf-lib) with the measurement in the comment, and they round-trip: reopen the file and your takeoff comes back
- Revision overlay compare: pick two revisions of a sheet; red exists only in A (removed), blue only in B (added), dark ink is unchanged. Auto fit-to-page, 1 pt nudge alignment, and a Changes only mode that fades everything unchanged to grey. Try it with
sample-floor-plan.pdfvssample-floor-plan-rev-b.pdf.
npm install
npm run dev # http://localhost:5173Then hit ▶ Watch the demo: the tool drives itself through a full takeoff on the sample drawing (calibrate → measure → area → scan-count) and shows the output summary with working export buttons. Or skip straight to "Try the sample drawing" and do it yourself.
npm run build # type-check + production build in dist/
npm run sample # regenerate public/examples/sample-floor-plan.pdf
npm run bench # takeoff accuracy bench on synthetic sheets (rooms, counts, scales)npm run bench:rooms -- --dir <folder> scores Magic Fill room areas against the ResPlan floor-plan dataset (setup in scripts/bench/README.md). npm run bench:ledger runs the fast benches and appends their headline numbers, with the commit, to bench/results/ledger.jsonl (--compare prints the delta against the previous run).
Deploy dist/ to any static host. Cloudflare Pages is the reference target (public/_headers is included).
The same engine runs headless on Node (22.13+) — no browser, no bundler, and
no network calls except two: the first ocr run on a scan downloads a ~3 MB
language pack (cached; see PDF2TAKEOFF_TESSDATA below), and ai-count
sends the page you choose to the AI provider you choose:
npx github:ajflow/pdf2takeoff info drawing.pdf # run straight from GitHub, nothing installed
npx github:ajflow/pdf2takeoff scan drawing.pdf --page 1 --at 88,271
npm install -g github:ajflow/pdf2takeoff # or install the commands onceThe first ocr use fetches the ~3 MB English pack described above, whatever
install route you took.
npm run cli -- info drawing.pdf # pages, sizes, annotation counts
npm run cli -- compare r2.pdf r3.pdf --png diff.png # per-sheet change fractions + regions
npm run cli -- compare r2/ r3/ --png-dir ov/ # folders: batch the whole set, pair by filename
npm run cli -- scan drawing.pdf --page 1 --at 88,271 # click-point auto-count, JSON hits
npm run cli -- scan plan.png --page 1 --at 318,447 # PNG/JPG input (every command): one page of W/3 x H/3 points
npm run cli -- export drawing.pdf --scale 1:100 # annotations → quantities CSV
npm run cli -- annotate drawing.pdf --spec takeoff.yaml -o marked.pdf
npm run cli -- run takeoff.yaml --prev run-a.json # regenerate + diff quantities
npm run cli -- set tender/ -o current-issue.pdf # slip-sheet: newest per sheet, sorted
npm run cli -- ocr scan.pdf --page 1 [--region x,y,w,h] # scanned sheet: OCR words, scale note, room labels by classOne command asks a vision model to inventory a sheet:
export OPENROUTER_API_KEY=... # or set it in your shell profile
npm run cli -- ai-count drawing.pdf --model google/gemini-3.5-flashAn OpenRouter key gets you any vision model with one
account; OpenAI, Google Gemini and Z.ai keys work directly (--provider openai|gemini|zai). Prefer nothing to leave the machine? --provider ollama
or lmstudio talks to a local server, and any other OpenAI-compatible
endpoint works through --base-url. Keys come from environment variables
only (OPENROUTER_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY,
ZAI_API_KEY, or PDF2TAKEOFF_AI_KEY for any provider) — never command
lines or files.
What comes back is the model's proposals as flagged count markups (the same
review flow as scan proposals) plus quantities counted under a preset
(--preset aecv or schedule): switching the preset recounts instantly,
with no new model call. --items lists every proposal as JSON; -o marked.pdf writes them into the PDF, types included, so a human can confirm
or delete each one and a later run replaces only the unconfirmed ones. Accuracy depends entirely on the model you point it at — review the
flags before trusting a number.
run is takeoff-as-code: the spec names the scale, one click-point per
symbol type (a class: field is reserved for when the ONNX detector runs
headless; today the CLI scans with the CV tier), and the exports. Re-run it on
every revision with --prev to see exactly which quantities changed — the
CI-friendly revision loop.
Every <file.pdf> above also takes an image directly (PNG, JPG, TIFF, WebP,
GIF, BMP, AVIF or SVG; the MCP open_document included), as well as
image-only (scanned) PDFs, including CCITT fax, JBIG2 and JPEG 2000 images: an image is
one page of width/3 by height/3 points, so a click at pixel (px, py) is
--at px/3,(H - py)/3 and the scan sees the native pixels. compare --json
is designed for pipelines (jq '.changedSheets'); scan
returns the same hits the web Scan tool finds. This is the substrate for the
agent API (MCP) — see specs/headless-engine.md.
Environment variables: PDF2TAKEOFF_MODEL_DIR (or scan --model <dir>)
selects a trained ONNX detector directory — without it the scan stays on the
CV tier; PDF2TAKEOFF_TESSDATA points the OCR language pack at an
offline/frozen copy instead of downloading it; PDF2TAKEOFF_OCR_DUMP
writes the binarised OCR crops to a folder for debugging.
pdf2takeoff mcp starts a local MCP server on stdio with fifteen tools —
open_document, render_page (the agent's eyes: a PNG of any sheet),
read_text (the sanctioned way to read printed scales/dimensions/labels —
exact, positioned, never off the rendered image), set_scale, measure,
fill_area, scan_symbol, propose_items, count_items,
compare_revisions, list_markups, set_status, delete_markup,
export, save_annotated — so an AI agent can run a whole takeoff and hand
the marked-up PDF to a human for review. Local process, local files, nothing
uploaded.
Proposed doors, Windows and spaces land as flagged count markups a human can
confirm or delete, and count_items derives every quantity from those items
under a counting preset (aecv, schedule) — switching the counting
convention recounts instantly, with no new detection pass.
Register it with any MCP client (ZCode, Claude Desktop, Cursor), straight from GitHub:
{
"mcpServers": {
"pdf2takeoff": {
"command": "npx",
"args": ["-y", "github:ajflow/pdf2takeoff", "mcp"]
}
}
}Claude Code: claude mcp add pdf2takeoff -- npx -y github:ajflow/pdf2takeoff mcp.
No public benchmark tests measuring a length or an area from a scaled drawing,
so the repo ships one. bench/scaled-drawing-bench
is a deterministic, CC BY 4.0 set of 200 synthetic floor-plan sheets (half
born-digital, half degraded 150 dpi scans; five scale notations; A3, A1 and
ARCH D; some rotated; some with a revision B) and about 2,000 estimator-style
questions with exact answers: scale, wall lengths, room areas and perimeters,
symbol counts, printed dimensions and revision changes. npm run bench:scaled -- generate
builds it from a seed, score grades a predictions file (within 2 and 5
percent, MAPE, exact counts, change-set F1, hallucination rate) and
reference runs pdf2takeoff's own deterministic solver, whose table is in the
benchmark README: near perfect on the vector half, with the raster half
showing where the engine's scan and OCR paths still fall short.
evals/ is a public, reproducible benchmark suite: room-area fill scored against analytic ground truth, detector precision/recall with every miss attributed to the gate that dropped it, an MCP workflow conformance bench, a perf bench, and a committed baseline that fails CI on any regression — plus a manual anchor on the external AEC-Geometric-Bench. Numbers are published with their run logs under evals/runs/.
src/engine/— the takeoff engine: measurement geometry, scan CV + ONNX decode, compare blend, PDF annotation IO, quantity export. Pure TypeScript with zero DOM/bundler imports (enforced in CI via a no-DOM typecheck + import guard) so the same code runs in the browser and headless in Node.src/app/— the browser UI: PDF.js viewer, tools, markups table, report, persistence. Wires the engine's ports (RasterPage,DetectorSession) to browser adapters.src/headless/— the Node side: the CLI (cli.ts, launched bybin/pdf2takeoff.js), the MCP server (mcp.ts), the takeoff-as-code spec loader, and the PDF.js/raster adapters that wire the engine's ports to Node. Design: specs/headless-engine.md.
| Key | Action |
|---|---|
V / C / W / L / A / N / P / O / G / T / D / S |
pan · calibrate · viewport · length · area · count · perimeter · volume · angle · note · fill · scan |
F |
fit width |
+ / − |
zoom |
← / → |
previous / next page |
Double-click or Enter |
finish a measurement |
Backspace |
remove last vertex while drawing |
Delete |
remove selected markup |
Ctrl/Cmd+Z |
undo last measurement |
Esc |
cancel / deselect |
Viewer + scale calibration✅Length / area / count + markups list + CSV export✅Revision overlay compare (colour-blend the difference between two revisions)✅Headless engine: Node CLI, MCP agent API, takeoff-as-code✅ (specs/headless-engine.md)ML symbol detection in the browser (ONNX Runtime Web, WASM)✅ v1 shipped (five classes); v2 retrain next — FloorPlanCAD transfer stage and the tile contract for large sheets (training/, specs/detector-tiling.md)- Live multi-user sessions — phase 1 shipped (same-machine, experimental — see above); next: cross-machine relay
- Plugin API: AI takeoff helpers, quote generation and trade-specific tools as add-ons anyone can write
There is no real open-source Bluebeam for construction yet. An open core lets builders and developers add trade-specific tools (HVAC, rebar, electrical) and AI takeoff helpers without asking permission. Permissive dependencies only (PDF.js is Apache-2.0, pdf-lib is MIT); we deliberately avoid AGPL components.
Issues and PRs welcome; read CONTRIBUTING.md first (short version: issue before PR, small scoped changes, CI green, and the contributor licence terms under Licensing your contribution). Trade-specific feature ideas (what does your takeoff actually look like?) are the most valuable input of all.
Apache-2.0 © pdf2takeoff contributors