Skip to main content

tile-takeoff

Tile quantities computed from a floor plan, not guessed.

العربية · MIT licensed · Python 3.10+

Every tiling job starts with the same question: how many square metres do we need? Because almost nobody works it out properly, the habit across the industry is to add a blanket waste allowance — usually 10–20% over the net area. That number isn't an estimate. It's insurance against not knowing how the tiles will be cut.

Two things end up in the skip: money spent on material that was never laid, and the material itself. Broken ceramic is among the hardest construction waste to recycle, so it goes to landfill.

This tool replaces the guess with a calculation. Point it at a PDF drawing, give it the slab or tile size, and it returns the cutting layout, the real waste percentage, and the quantity to order.

SLAB  295 × 175 cm   (5.1625 m²)

RECOMMENDED   147.5 × 87.5 cm
  2×2 cut · 4 tiles per slab · 0.0% waste · 1.291 m² per tile
  on this floor: 7 × 147.5cm across (border 17.5cm), 9 × 87.5cm up (border 22.5cm)

OFF-THE-SHELF SIZES (for comparison)
          tile cm  per slab     waste
            60×60         8     44.2%
            80×80         6     25.6%
           60×120         4     44.2%

QUANTITIES   floor = 98.00 m²
  tiles needed            : 76
  slabs, +10% allowance   : 21

Cutting that same slab into 60×60 tiles throws away 44% of it. The tool doesn't decide for you — it shows you what each choice costs.

Install

pip install "tile-takeoff[all]"

[all] pulls in PyMuPDF for reading PDFs and matplotlib for the layout diagram. The cutting maths itself has no dependencies at all — pip install tile-takeoff is enough if you already know your areas.

Use it

Survey the drawing — how many sheets, what size, and whether each page carries a real text layer (exported from CAD) or is a scan that has to be read by eye:

tile-takeoff survey plan.pdf

Render pages so they can actually be read. Dimension text is small; a crop at 350 dpi beats the whole sheet at 150 dpi every time:

tile-takeoff render plan.pdf --out ./pages --pages 1,3 --dpi 150
tile-takeoff render plan.pdf --out ./pages --pages 2 --dpi 350 --crop 0.5,0,1,0.5
tile-takeoff render plan.pdf --out ./pages --pages 2 --dpi 300 --grid 2x3

--crop takes fractions of the page (x0,y0,x1,y1, each 0–1). --grid sweeps a crowded A0/A1 sheet as overlapping tiles, so a dimension sitting on a seam still lands whole in one of them.

Pull the text layer, which confirms what you read visually and catches what you missed:

tile-takeoff text plan.pdf --pages 1-4

Work out the cut and the quantities:

tile-takeoff optimize --slab 295x175
tile-takeoff optimize --slab 295x175 --area 98
tile-takeoff optimize --slab 295x175 --floor 1050x810 --area 98 --draw layout.png
Flag Meaning
--slab raw slab size in cm, e.g. 295x175
--area floor area in m²
--floor room size in cm, so the tool can pick the orientation with the smallest offcut
--max-side largest practical tile side, default 150 cm — above that they're hard to carry and easy to crack
--min-side smallest side worth considering, default 40 cm
--allowance breakage and site-cut allowance, default 0.10
--draw write a layout diagram to a PNG

As a library

from tile_takeoff.cutting import recommend, fit_to_floor, Takeoff

best = recommend(295, 175, floor_width_cm=1050, floor_height_cm=810)
print(best.width_cm, best.height_cm, best.per_slab)     # 147.5 87.5 4

quantities = Takeoff(area_m2=98, tile_area_m2=best.area_m2, slab_area_m2=5.1625)
print(quantities.slabs_with_allowance)                   # 21
from tile_takeoff.blueprint import survey, render, extract_text

In the browser

web/ is a static page that does the whole job without a server: it opens the PDF with a bundled copy of pdf.js, runs the same cutting maths compiled to JavaScript, and draws the layout on a canvas. The drawing never leaves the machine — there is no upload endpoint to send it to, and the page works offline once loaded.

Live at https://hanysyria.github.io/tile-takeoff/, or run it locally:

python -m http.server 8777 --directory web

web/cutting.js mirrors src/tile_takeoff/cutting.py, and web/cutting.test.mjs asserts the same reference case in both languages — if the two implementations ever disagree about a quantity, CI fails.

node --test web/cutting.test.mjs

As an agent skill

skill/ holds an installable Agent Skill that teaches a coding agent to drive this tool: how to read a drawing without guessing, how to verify the scale, and where it must stop and ask instead of inventing a number. Copy the folder into ~/.claude/skills/tile-takeoff/.

What it will not do

Never guess a dimension. A drawing with no written dimensions and no readable scale produces a request for information, not a takeoff. Somebody is going to spend money on this number.

Never trust the printed scale alone. Exporting and printing change the page size without updating the "1:50" printed on it. To measure off an image, calibrate pixels per metre against a written dimension in that same image, then check the calibration against a second written dimension before relying on it.

Watch the units. Architectural drawings are usually in millimetres — 3600 means 3.60 m — but not always. Infer from context (a wall is 200–300 mm thick) and state which unit you concluded.

Say where every number came from. Read off the drawing, measured by calibration, or assumed — these are three different levels of confidence and the output should never blur them.

Waste allowances

The defaults are conventional, not laws of physics:

  • 10% for a straight lay
  • 15% for a diagonal lay, rooms with many corners, or large-format tiles (60×120 and up), because more tiles get cut
  • Natural stone runs slightly higher than porcelain — raise it when in doubt

Once the cutting layout is actually computed, the allowance covers breakage and site error rather than ignorance, which is why it can come down.

Development

git clone https://github.com/hanysyria/tile-takeoff.git && cd tile-takeoff
pip install -e ".[dev]"
pytest
python examples/make_example_plan.py   # regenerate the synthetic test drawing

examples/example_plan.pdf is generated by the repo itself — a small synthetic sheet with four rooms and written dimensions. No real project drawings are included, and none should be: a floor plan is somebody's home.

Contributing

Issues and pull requests welcome — see CONTRIBUTING.md. Particularly useful: drawings in units or conventions the reader mishandles, tile shapes it doesn't model (hexagons, herringbone, borders), and translations.

Metadata

Release files for tile-takeoff 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tile-takeoff 0.1.0
File Size Uploaded
tile_takeoff-0.1.0.tar.gz 547.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tile-takeoff 0.1.0
File Interpreter ABI Platform
tile_takeoff-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 563.2 kB

Release files / tile_takeoff-0.1.0.tar.gz

Download URL tile_takeoff-0.1.0.tar.gz
Size 547.9 kB
Tags Source
SHA-256 checksum
How to use checksums
9238cb50681f26db57388dbe3f7c870be51d8bdec29d8b65af1fea5899a20306
BLAKE2b-256 checksum
How to use checksums
447dae0dca1c9ea440e7b6e111e20b0f37ac87dc274c713826ee587dfe9093de
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 29, 2026.

Transparency log

Release files / tile_takeoff-0.1.0-py3-none-any.whl

Download URL tile_takeoff-0.1.0-py3-none-any.whl
Size 15.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
dc5cff8e2d45da98868bdc748b426314f1dfefa80478e3af3f94127f0a309129
BLAKE2b-256 checksum
How to use checksums
4752104250af99400e2d7d7b21f970a494ec9cb45068fb986e9d3450cae499cc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 29, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page