Permanent laser-engraved metal labels for fruit trees and plants.
93 labels generated across 3 planting years (2024–2026).
- Read the full guide:
docs/PROJECT_GUIDE.md - Labels are ready:
output/labels_batch.csvandoutput/svg/— import into xTool Creative Space - Follow the XCS Batch Engraving Workflow below
- Run a test card first — see the test grid procedure in the full guide
- Add rows to
data/my_trees.csv(one row per physical plant) - Run:
python3 data/generate_labels.py - Run:
python3 data/generate_svgs.py - New
output/labels_batch.csvandoutput/svg/files will be generated
- Engraver: xTool M1 (5W or 10W diode module)
- Material: xTool Black Metal Business Cards (86mm × 54mm, anodized aluminum)
- Software: xTool Creative Space (XCS)
| Field | Example |
|---|---|
| Name | Gravenstein |
| Type | Apple |
| Bloom Period | Mid-season |
| Harvest Period | Late August |
| Fertility | Needs pollinator |
| Use | Fresh eating, Cider |
| Origin | Denmark, 1600s |
| Year Planted | 2024 |
| Footer | Planted by: Peter Brown & Robyn Seely |
This is the step-by-step process to go from labels_batch.csv to engraved cards.
- Open xTool Creative Space (XCS)
- Set the canvas/work area to 86mm × 54mm (landscape)
- Add 9 text elements to the canvas — one for each label field
- Arrange them following this layout:
┌──────────────────────────────────────────────────────────┐
│ │
│ << VARIETY NAME >> │
│ (Type) │
│ │
│ ──────────────────────────────────────────────────────── │
│ │
│ Bloom: __________ │
│ Harvest: __________ │
│ Fertility: __________ │
│ Use: __________ │
│ Origin: __________ │
│ │
│ ──────────────────────────────────────────────────────── │
│ │
│ Planted: 2026 │
│ Planted by: Peter Brown & Robyn Seely │
│ │
└──────────────────────────────────────────────────────────┘
Typography:
| Element | Font Size | Style | Alignment |
|---|---|---|---|
| Variety Name | 4.5mm (~13pt) | Bold | Center |
| Type | 3.2mm (~9pt) | Italic | Center |
| Detail fields | 2.8mm (~8pt) | Regular | Center |
| Planted year | 2.8mm (~8pt) | Bold | Center |
| Footer | 2.5mm (~7pt) | Regular | Center |
- In XCS, go to Top menu → Processing → Batch Processing (may also appear as "Variable Data" or "Data Processing" depending on XCS version)
- Click Import CSV (or Import Data)
- Select
output/labels_batch.csv - XCS will show the CSV columns:
Name, Type, Bloom_Period, Harvest_Period, Fertility, Use, Origin, Year_Planted, Footer
-
Click each text element on the canvas
-
In the properties panel, find the Variable/Data binding option
-
Bind each text element to its matching CSV column:
Canvas Text Element Bind To CSV Column Variety name (large) NameType field TypeBloom field Bloom_PeriodHarvest field Harvest_PeriodFertility field FertilityUse field UseOrigin field OriginYear planted field Year_PlantedFooter text Footer -
For field labels like "Type:", "Bloom:", etc. — these are static text on the template. Only the values are bound to CSV columns. Use XCS text concatenation or place two text boxes side by side:
- Static:
"Type: "(not bound) - Variable: bound to
Typecolumn
- Static:
- XCS will show a preview of each label (93 total)
- Use the navigation arrows to scroll through all labels
- Check that:
- Text fits within the card boundaries (no clipping)
- Font sizes are legible
- Long variety names like "Rhode Island Greening Apple" don't overflow
- If text overflows, reduce font size or abbreviate field labels
Set the engraving parameters for all text elements:
10W Module (recommended):
| Setting | Value |
|---|---|
| Power | 80% |
| Speed | 1000 mm/min |
| Passes | 1 |
| Lines per cm | 300 LPI |
5W Module:
| Setting | Value |
|---|---|
| Power | 100% |
| Speed | 600 mm/min |
| Passes | 1 |
| Lines per cm | 300 LPI |
⚠️ Run a test card first! Engrave one label on a spare card to verify settings produce a bright white mark before committing to all 93 cards.
- Clean card with isopropyl alcohol
- Place card in xTool M1 — secure with painter's tape or card jig
- Focus the laser (auto-focus or manual)
- Click Start / Send to Device
- For batch mode: XCS will prompt you to swap cards between each label
- After engraving, wipe gently with a soft cloth to remove dust
- Save your XCS template — you can re-import a new CSV anytime without rebuilding the layout
- Cards may curl slightly from heat — press flat under a book while cooling
- Do not use air assist at full blast (prevents debris scatter on small cards)
- Process cards in batches of 10-15 to manage workflow
FarmInventory/
├── README.md ← This file
├── data/
│ ├── master_catalog.csv ← 432 variety reference (botanical data)
│ ├── my_trees.csv ← Your inventory (1 row = 1 plant)
│ ├── generate_labels.py ← Joins inventory + catalog → labels CSV
│ ├── generate_svgs.py ← Generates individual SVG files per label
│ ├── scrape_catalog.py ← Refreshes catalog from Trees of Antiquity
│ └── inventory_template.csv ← Blank template for reference
├── output/
│ ├── labels_batch.csv ← 93 labels ready for XCS (CSV)
│ └── svg/ ← 93 individual SVG files (one per card)
└── docs/
└── PROJECT_GUIDE.md ← Full documentation
If XCS does not support CSV batch import, use the individual SVG files:
- Open any SVG from
output/svg/in XCS (e.g.,001_Chandler_Walnut.svg) - Each file is pre-sized to 86mm × 54mm — the exact card dimensions
- All text is centered with appropriate font sizes
- Set laser settings (see above), engrave, then open the next SVG
You can fork or clone this repo and adapt it for your own farm or garden. Here's how:
- Python 3.6+ (no external packages required — stdlib only)
- xTool Creative Space (XCS) for engraving
- xTool M1 (or compatible laser engraver)
- Black anodized aluminum business cards (86mm × 54mm)
git clone https://github.com/PeterBrownUSA/FarmInventory.git
cd FarmInventoryEdit data/generate_labels.py and change the FOOTER constant (line 23):
FOOTER = "Planted by: Your Name Here"The included data/master_catalog.csv contains 432 varieties from Trees of Antiquity. To refresh it:
python3 data/scrape_catalog.pyThis re-scrapes all collections via the Shopify JSON API and overwrites data/master_catalog.csv. No API key is required.
If your trees come from a different nursery, you can replace master_catalog.csv entirely. Just keep the same column headers:
Name,Type,Bloom_Period,Harvest_Period,Fertility,Use,Origin,Source_URL
Edit data/my_trees.csv — add one row per physical plant you own:
ID,Catalog_Name,Year_Planted,Source,Location,Notes
1,Gravenstein,2024,Trees of Antiquity,Orchard Row A,
2,Gravenstein,2025,Trees of Antiquity,Orchard Row B,Second tree
3,Bartlett Pear,2024,Local nursery,,Gift from neighbor| Column | Description |
|---|---|
ID |
Unique number (sequential) |
Catalog_Name |
Must match a Name in master_catalog.csv (partial match supported) |
Year_Planted |
Year the tree went in the ground |
Source |
Where you purchased it |
Location |
Optional — where on your property |
Notes |
Optional — any notes |
Tip: You can have multiple rows for the same variety (e.g., two Gravenstein trees planted in different years). Each row produces its own label.
If you own trees not in the catalog, you have two options:
Option A: Add the variety directly to data/master_catalog.csv:
My Local Apple,Apple,Midseason,Late,Self-fertile,Fresh eating,Oregon 1990s,Option B: Add an override in data/generate_labels.py in the OVERRIDES dict:
OVERRIDES = {
"my local apple": {"Origin": "Oregon, 1990s", "Bloom_Period": "Midseason"},
...
}Overrides always take precedence for the Origin field and fill in any missing fields from the catalog.
# Generate the CSV (used for XCS batch import)
python3 data/generate_labels.py
# Generate individual SVG files (one per card)
python3 data/generate_svgs.pyOutput:
output/labels_batch.csv— all labels in CSV formatoutput/svg/— one SVG file per label (86mm × 54mm each)
Open the SVG files in xTool Creative Space and engrave using the laser settings documented above. See the XCS Batch Engraving Workflow section for full details.
To adjust fonts, spacing, or layout, edit data/generate_svgs.py. Key variables at the top of the file:
CARD_W = 86 # Card width in mm
CARD_H = 54 # Card height in mm
MARGIN = 3 # Margin from edges in mm
FONT_NAME = 4.5 # Variety name font size (mm)
FONT_TYPE = 3.2 # Type line font size (mm)
FONT_DETAIL = 2.8 # Detail fields font size (mm)
FONT_FOOTER = 2.5 # Footer font size (mm)The LAYOUT dict controls vertical positioning (Y coordinates in mm from top). Adjust these to rebalance spacing between fields.
If you're using GitHub Copilot CLI, you can skip the manual inventory entry entirely. Simply paste your nursery order receipt (email confirmation, invoice text, or screenshot text) into a Copilot CLI session and ask it to:
- Parse the receipt — extract variety names, quantities, and purchase year
- Match against the catalog — look up each variety in
master_catalog.csvand resolve name differences - Expand bundles — identify collection/bundle products and break them into individual varieties
- Research missing data — for varieties not in the catalog, look up botanical details (bloom period, origin, fertility, etc.)
- Populate
my_trees.csv— append new rows with correct IDs, catalog names, and planting year - Regenerate labels — run both generation scripts automatically
Example prompt:
Here are the trees I purchased in 2025 and planted in 2026:
Bramley Seedling × 1 $56.95
Lapins Cherry × 2 $52.95/ea
Orchard Starter Bundle × 1 $235.95
Please add these to my_trees.csv, match them against the catalog,
expand any bundles, and regenerate the labels.
This is how the original inventory for this project was built — three years of purchase receipts were processed in a single session.