JSON

JSON layout is a UTF-8 text format for a hierarchical 2D layout: boxes, polygons, paths, texts, and cell instances. Coordinates are micrometres. Layer and datatype follow the GDS convention.

It is not a replacement for GDSII or OASIS, but intended for automated tests as AI can easily create it. Properties, nets, text height, fonts, circles as a special type, and the database unit itself are not stored. Use it for readable test layouts, small examples, and round-trips of the geometry the format covers.

A .json file can also be a DRC result (violations). That is a different schema, documented at the end of this page. File Open in the layout window accepts only a layout recipe.

JSON in the LayoutEditor

Action Behaviour
File → Open Replaces the drawing. The top cell of the file becomes the current cell.
File → Save / Save As Writes the current cell as the root object. Every other cell in the drawing is listed in subcells (flat, not nested). Choose filter JSON layout (*.json *.JSON).
File → Attach Adds the recipe cells to the existing drawing. Existing cells are not deleted. If a name already exists, an instance may bind to the older cell.
Update cells For JSON this is the same load as Attach (cells are added, not replaced by name).
Text Editor → Execute If the object has violations, the records are applied to the DRC list of the connected layout. Otherwise a layout recipe is applied: cells are added (the drawing is not cleared). A layout window must be connected.

Extensions: .json, .JSON, .jsonc, .JSONC. The parser is strict JSON (RFC 8259). Comments and trailing commas are rejected, including in .jsonc files.

Save writes indented UTF-8. Empty arrays are omitted. Default instance fields are omitted. Extra keys that are not in this reference are ignored on load and dropped on save, so a private "comment" field can document a hand-written file.

Units. All layout coordinates, widths, and array steps are micrometres. They are converted with the current drawing database unit (default 1 nm, so 1.0 → 1000 dbu). The file does not store databaseunits or the user unit. Change the database unit before Open/Save only if you intend a different rounding.

Layers. Each shape has GDS layer and datatype integers. On load they are mapped into the LayoutEditor layer table (gdsAutoMapDatatypes). On save the mapped GDS layer/datatype are written back, not the internal layer index.

Scripting. drawingField::openFile / saveFile / importFile detect the type from the extension, for example layout->drawing->saveFile("/tmp/cell.json");.

License. JSON can be written in the reduced and node-locked modes, similar to CSV.

Layout recipe — file structure

The file is a single JSON object (not an array). That object is one cell. Further cells are nested under subcells. Instance hierarchy is defined only by instances, not by how deeply subcells are nested. Nested subcells still become ordinary cells in the drawing; Save always flattens them into one subcells array on the root cell.

A file with no boxes, polygons, paths, texts, instances, or subcells is rejected. If it also contains violations, the error states that it is a DRC result, not a layout recipe.

Cell object

Every cell object (root or subcell) uses the same keys.

Key Type Default Meaning
cell string "CASE" Cell name.
boxes array of objects omitted Axis-aligned rectangles.
polygons array of objects omitted Polygons. There is no polys key on load.
paths array of objects omitted Centerline paths with width and cap.
texts array of objects omitted Text anchors.
instances array of objects omitted Cell references and arrays (GDS SREF / AREF).
subcells array of cell objects omitted Further cells, same schema, nested arbitrarily.

Unknown keys are ignored. Numeric fields accept integer or floating JSON numbers. On save, values that are whole numbers after rounding are written as integers.

boxes[]

Axis-aligned box. x, y are the minimum X and minimum Y (origin). w and h are width and height, all in µm.

Key Type Default Required
x number (µm) 0 no
y number (µm) 0 no
w number (µm) 0 no
h number (µm) 0 no
layer integer 1 no
datatype integer 0 no
{
  "cell": "NW_WITH_WIDTH_RECT",
  "boxes": [
    { "x": 0, "y": 0, "w": 3, "h": 0.4, "layer": 1, "datatype": 0 },
    { "x": 5.0, "y": 0.0, "w": 2.0, "h": 1.0, "layer": 1, "datatype": 0 }
  ]
}

polygons[]

A polygon needs at least three vertices after conversion to database units; shorter outlines are skipped. The outline is not closed automatically. Save drops a last vertex that is identical to the first (GDS-style open list). Holes are not a separate construct; use another polygon or a boolean in the editor.

Key Type Default Required
pts_um array of [x, y] (µm) empty yes (or pts)
pts same as pts_um alias if pts_um is missing
layer integer 1 no
datatype integer 0 no

Each point is an array of at least two numbers: [x, y] in µm. Further entries in a point array are ignored.

{
  "cell": "NW_WITH_WIDTH_POLY",
  "polygons": [
    {
      "layer": 1,
      "datatype": 0,
      "pts_um": [
        [0, 0],
        [3, 0],
        [3, 0.4],
        [0.4, 0.4],
        [0.4, 3],
        [0, 3]
      ]
    }
  ]
}

paths[]

A path needs at least two vertices; shorter paths are skipped. Width 0 is stored and drawn as a zero-width path (the DRC engine ignores width 0 when it polygonizes paths).

Key Type Default Required
pts_um array of [x, y] (µm) empty yes (or pts)
pts same as pts_um alias if pts_um is missing
width number (µm) 0 no
width_um number (µm) alias if width is missing
cap integer 0 no
layer integer 1 no
datatype integer 0 no

cap is the GDS path type:

cap Ends
0 Flush (square, ends at the vertices)
1 Round
2 Extend (square, ends extend by half the width)

Save always writes pts_um, width, and cap (not the aliases).

{
  "cell": "NW_TARGET_PATH",
  "paths": [
    {
      "pts_um": [ [0.1, 0], [0.1, 2.0] ],
      "width": 0.2,
      "cap": 0,
      "layer": 1,
      "datatype": 0
    }
  ]
}

texts[]

A text is an anchor point plus a string. Height, font, presentation, and magnification are not stored. Save writes string from the element name.

Key Type Default Required
x number (µm) 0 no
y number (µm) 0 no
layer integer 1 no
datatype integer 0 no
string string "" no
{
  "cell": "LAB_OK",
  "boxes": [
    { "x": 0, "y": 0, "w": 1.0, "h": 1.0, "layer": 1 }
  ],
  "texts": [
    { "x": 0.5, "y": 0.5, "layer": 68, "datatype": 20, "string": "VDD" }
  ]
}

instances[]

Places another cell. The named cell must exist in this file (subcells or, after Attach, already in the drawing). An empty cell string is skipped. A missing name is an error: instance cell not found: ….

Key Type Default Required
cell string yes (skipped if empty)
x number (µm) 0 no
y number (µm) 0 no
mag number 1 no; omitted on save when 1. 0 is ignored (treated as 1)
angle number (degrees) 0 no; omitted on save when 0. Counter-clockwise, GDS STRANS
mirror boolean false no; omitted on save when false. Mirror about X, applied before rotation
nx integer 1 no; values ≤ 0 become 1
ny integer 1 no; values ≤ 0 become 1
step_x number (µm) 0 column pitch along X; written when nx>1 or ny>1
step_y number (µm) 0 row pitch along Y; written when nx>1 or ny>1

If nx>1 or ny>1, the instance is an array (GDS AREF). Pitch is orthogonal: step_x along X, step_y along Y. Diagonal array vectors are not stored.

Transform order matches GDS: mirror about X, then rotation, then magnification.

Single instance:

{
  "cell": "TOP",
  "instances": [
    { "cell": "LEAF", "x": 0, "y": 0, "angle": 90, "mirror": true, "mag": 2 }
  ],
  "subcells": [
    {
      "cell": "LEAF",
      "boxes": [ { "x": 0, "y": 0, "w": 0.2, "h": 2.0, "layer": 1 } ]
    }
  ]
}

Array instance:

{
  "cell": "TOP",
  "instances": [
    { "cell": "LEAF", "x": 0, "y": 0, "nx": 2, "ny": 1, "step_x": 5, "step_y": 0 }
  ],
  "subcells": [
    {
      "cell": "LEAF",
      "boxes": [ { "x": 0, "y": 0, "w": 0.2, "h": 2.0, "layer": 1 } ]
    }
  ]
}

subcells[]

Each entry is a full cell object (same keys as the root). Load creates every nested cell first, then geometry, then instances, so instance order and subcell order do not matter as long as names exist. Duplicate cell names are not checked; findCell returns the first match.

Save does not reconstruct nesting: the current cell is the root, and all other cells of the drawing (referenced or not) appear in one subcells array.

Hierarchy example (two levels of instances):

{
  "cell": "TOP",
  "instances": [
    { "cell": "MID", "x": 0, "y": 0 },
    { "cell": "MID", "x": 5, "y": 0 }
  ],
  "subcells": [
    {
      "cell": "LEAF",
      "boxes": [ { "x": 0, "y": 0, "w": 0.2, "h": 2.0, "layer": 1 } ]
    },
    {
      "cell": "MID",
      "instances": [ { "cell": "LEAF", "x": 0, "y": 0 } ]
    }
  ]
}

Rounding and round-trip

On load, each micrometre value is round(value × dbu_per_µm) to the nearest database unit.

On save, database units are converted back to µm. Values are rounded to 1×10⁻⁹ µm; near-zero becomes 0; values within 5×10⁻¹⁰ of an integer are written as integers.

A round-trip is not byte-identical: subcells are flattened, default fields are dropped, pts/width_um become pts_um/width, numbers may change between integer and float, and element order follows the drawing. Geometry of the supported types round-trips within the database-unit grid.

What is not stored

The following exist in the drawing (or in GDS) but are not part of this format. On save they are omitted. On load they cannot be expressed.

  • Database unit, user unit, library name, layer names, colours, styles
  • Shape properties, net names, pin / terminal records
  • Text height, font, presentation, path magnification
  • Circles / ellipses as a dedicated type (regular polygons are stored as polygons)
  • Non-orthogonal array step vectors
  • Anything that is not a box, polygon, path, text, SREF, or AREF

Errors

Situation Result
File unreadable cannot read …
Invalid JSON parser message
Root is not an object JSON object expected
No geometry, instances, or subcells no boxes, polygons, paths, texts, instances, or subcells
Empty recipe that has violations DRC result JSON (violations), not a layout recipe
Instance name missing in the drawing instance cell not found: …
Save with no current cell no cell
Save cannot create the file cannot write …

Polygons with fewer than three points and paths with fewer than two points are skipped without an error.

DRC result JSON

Execute in the Text Editor on a JSON tab treats an object that contains violations as DRC output, not as a layout. File Open never loads this schema as geometry.

Coordinates here are integer database units, not micrometres.

Root (object or a bare array of records):

Key Type Meaning
cell string Optional. Cell the markers belong to; used to select that cell if it exists.
violations array Marker records. If the root is an array, that array is the violation list.

Each record:

Key Type Meaning
rule string Check id (for example NW.W.1).
distance_dbu integer Measured distance in dbu.
bbox [x1, y1, x2, y2] Integer bounding box in dbu.
kind string "edge_pair" or "bbox".
e1, e2 [[ax, ay], [bx, by]] Edge pair in dbu when kind is edge_pair.
{
  "cell": "NW_WITH_WIDTH_RECT",
  "violations": [
    {
      "rule": "w.fail",
      "distance_dbu": 200,
      "kind": "bbox",
      "bbox": [0, 0, 200, 2000]
    }
  ]
}

The same schema is used as expect.json in DRC test cases (layout --drcverify / --drcselftest). Running a .ledrc file from the Text Editor writes this JSON; Execute on that result fills the violation list. See Checking Designs and the .ledrc reference.

Other .json files that are not layouts

File Role
layout.json This layout recipe. Open/Save in the layout window.
expect.json DRC result / oracle (violations). Not a layout.
meta.json DRC test-case metadata (id, ops, oracle, …). Not a layout.
twin_layers.json Optional KLayout twin mapping for --drcverify. Not a layout.

Opening expect.json or meta.json from the layout window reports an error. Open them in the Text Editor instead.

Complete kitchen-sink example

{
  "cell": "TOP",
  "boxes": [
    { "x": 0, "y": 0, "w": 10, "h": 4, "layer": 1, "datatype": 0 }
  ],
  "polygons": [
    {
      "layer": 2,
      "datatype": 0,
      "pts_um": [ [1, 1], [3, 1], [2, 2.5] ]
    }
  ],
  "paths": [
    {
      "pts_um": [ [0, 5], [4, 5], [4, 8] ],
      "width": 0.3,
      "cap": 2,
      "layer": 1,
      "datatype": 0
    }
  ],
  "texts": [
    { "x": 1, "y": 6, "layer": 10, "datatype": 0, "string": "out" }
  ],
  "instances": [
    { "cell": "VIA", "x": 8, "y": 1, "nx": 3, "ny": 2, "step_x": 0.5, "step_y": 0.5 }
  ],
  "subcells": [
    {
      "cell": "VIA",
      "boxes": [
        { "x": 0, "y": 0, "w": 0.2, "h": 0.2, "layer": 3, "datatype": 0 }
      ]
    }
  ]
}