This tutorial shows how to add custom file format support to LinkCAD using Python.
A format reader converts an external file format into LinkCAD's drawing database.
Example: Simple Coordinate File Reader
from pathlib import Path
from linkcad.plugin import format_reader, FormatReader, Option, DrawingContext
@format_reader(
name="Coordinate File",
extensions=["*.xyz", "*.coord"],
description="Simple X Y coordinate files",
)
class CoordReader(FormatReader):
scale = Option.integer("Scale factor", default=1000, min=1, max=1000000)
layer_name = Option.string("Layer name", default="imported")
def read(self, path: Path, drawing: DrawingContext) -> None:
with drawing.cell(path.stem, main=True) as cell:
with cell.layer(self.layer_name) as layer:
vertices = []
for line in drawing.iter_lines(path):
line = line.strip()
if not line or line.startswith("#"):
# Blank line = end of polygon
if vertices:
layer.polygon(vertices)
vertices = []
continue
parts = line.split()
x = float(parts[0]) * self.scale
y = float(parts[1]) * self.scale
vertices.append((x, y))
# Don't forget the last polygon
if vertices:
layer.polygon(vertices)
Key Concepts
@format_reader() registers the class as an import format
extensions determines which files the reader handles
DrawingContext provides a builder API for creating cells, layers, and shapes
drawing.iter_lines() reads lines with automatic progress tracking
drawing.iter_binary() reads binary data with progress tracking
DrawingContext API
| Method |
Description |
cell(name, main=False) |
Context manager — creates/opens a cell |
iter_lines(path) |
Iterate text file lines with progress |
iter_binary(path, chunk_size) |
Iterate binary chunks with progress |
progress |
Get/set progress (0.0 to 1.0) |
CellContext API
| Method |
Description |
layer(name) |
Context manager — selects a layer |
LayerContext API
| Method |
Description |
polygon(vertices) |
Create a polygon from (x, y) tuples |
polyline(width, vertices, closed) |
Create a polyline |
circle(center, diameter) |
Create a circle |
A format writer exports LinkCAD geometry to a file.
Example: Simple Text Writer
from pathlib import Path
from linkcad.plugin import format_writer, FormatWriter, Option, WriterContext
@format_writer(
name="Simple Text",
extensions=["*.stxt"],
)
class SimpleWriter(FormatWriter):
separator = Option.choice(
"Separator",
choices=["Space", "Comma", "Tab"],
default="Space",
)
precision = Option.integer("Decimal places", default=3, min=0, max=10)
def write(self, path: Path, drawing: WriterContext) -> None:
sep = {"Space": " ", "Comma": ", ", "Tab": "\t"}[self.separator]
with open(path, "w") as f:
for layer_name, shapes in drawing.shapes_by_layer():
f.write(f"# Layer: {layer_name}\n")
for shape in shapes:
for x, y in shape.vertices:
f.write(f"{x:.{self.precision}f}{sep}"
f"{y:.{self.precision}f}\n")
f.write("\n")
WriterContext API
| Method |
Description |
shapes(cell, layer) |
Iterate all shapes (optional cell/layer filter) |
shapes_by_layer(cell) |
Iterate shapes grouped by layer |
shapes_by_cell(layer) |
Iterate shapes grouped by cell |
cells() |
Iterate cell names |
layers() |
Iterate layer names |
cell_count() |
Number of cells |
shape_count() |
Total number of shapes |
main_cell_name() |
Name of the top cell |
layer_names() |
List of all layer names |
flatten |
Get/set hierarchy flattening |
progress |
Get/set progress (0.0 to 1.0) |
ShapeInfo Properties
| Property |
Type |
Description |
layer_name |
str |
Layer name |
cell_name |
str |
Cell name |
vertices |
list[tuple] |
List of (x, y) coordinates |
is_polygon |
bool |
True for polygons, False for polylines |
width |
int |
Polyline width (0 for polygons) |
is_closed |
bool |
Whether the shape is closed |
Error Handling
Use the built-in exception classes for clear error reporting:
from linkcad.plugin import ParseError, WriteError, ValidationError
# In a reader:
raise ParseError("Invalid coordinate", line=42, path=path)
# In a writer:
raise WriteError("Cannot write to locked file")
# In option validation:
raise ValidationError("Scale must be positive")
Next Steps