Skip to content

Writing a Format Plugin

This tutorial shows how to add custom file format support to LinkCAD using Python.

Format Reader (Import)

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

Format Writer (Export)

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