PADS
PADS Layout, from Siemens, formerly Mentor Graphics, exports a board as an ASCII file with the extension .asc. px.FromKiCad converts it with kicad-cli, the command line tool of KiCad 10 or later, and returns the same dict of layers as for a KiCad board. See KiCad, Altium, Eagle and more for how the tool is found.
[1]:
import collections
import planex as px
The board
The example is the CLAYSIGHT MCU board from The Ohio State University, a two-layer carrier board for a Raspberry Pi Pico W. The file comes from the test data of KiCad’s PADS importer and is licensed under the MIT license, Copyright (c) 2024 John Simonis, John LaRocco and Qudsia Tahmina. Download: ClaySight_MK1.asc and its license.
[3]:
board = px.FromKiCad("ClaySight_MK1.asc")
for name, layer in board.items():
print(f"{name:10} {len(layer.faces):4} faces, {1e6 * layer.mass:9.2f} mm²")
F.Cu 117 faces, 1071.10 mm²
B.Cu 92 faces, 1234.86 mm²
Edge.Cuts 1 faces, 10480.68 mm²
F.Paste 44 faces, 97.22 mm²
F.SilkS 74 faces, 437.64 mm²
F.Mask 44 faces, 97.22 mm²
B.Mask 44 faces, 97.22 mm²
PlanexWarning: FromKiCad: 25 texts left out (not supported yet)
The layers are those of a board drawn in KiCad. The two copper layers have their faces named by net and the drill holes cut out, the outline is Edge.Cuts, and the silkscreen, solder mask and paste layers hold what KiCad prints there. The text on the board is not read yet, and FromKiCad reports how much it left out.
Copper per net
The two copper layers are drawn in copper, with the board outline dashed (Figure 1). Each face carries the name of its net, so the copper can be grouped by net:
[4]:
fcu, bcu, outline = board["F.Cu"], board["B.Cu"], board["Edge.Cuts"]
for name, layer in (("F.Cu", fcu), ("B.Cu", bcu)):
area = collections.Counter()
for f in layer.faces:
area[f.name or "(no net)"] += 1e6 * f.mass
print(name, len(area), "nets, the largest:", [(net, round(a, 1)) for net, a in area.most_common(3)])
F.Cu 33 nets, the largest: [('VCC', 218.9), ('N$15664', 218.0), ('U1_6', 107.3)]
B.Cu 33 nets, the largest: [('VCC', 262.9), ('N$15664', 161.2), ('U1_26', 98.6)]

(a) F.Cu, the top layer

(b) B.Cu, the bottom layer
The solder mask openings F.Mask and the silkscreen F.SilkS laid over the outline show the board as it comes from the manufacturer (Figure 2).

A check against the Pico’s pin grid
The Pico W’s datasheet puts its 40 pins on a 2.54 mm grid with 1 mm holes, in two rows along its long edges. On the board, these are the 40 plated holes of 1 mm, which are cut out of the outline. Their centers give the rows and the pitch:
[7]:
pins = []
for hole in outline.faces[0].inner:
(x0, y0), (x1, y1) = hole.bounding_box
if abs(x1 - x0 - 1e-3) < 1e-7:
pins.append(((x0 + x1) / 2, (y0 + y1) / 2))
x = sorted(p[0] for p in pins)
y = sorted({round(p[1], 9) for p in pins})
pitch = [1e3 * (b - a) for a, b in zip(y, y[1:])]
print(len(pins), "holes of 1 mm, rows", round(1e3 * (x[-1] - x[0]), 6), "mm apart, 7 × 2.54 =", 7 * 2.54)
print(len(y), "pins per row, the pitch differs from 2.54 mm by up to", round(1e3 * max(abs(s - 2.54) for s in pitch), 4),
"µm")
40 holes of 1 mm, rows 17.78 mm apart, 7 × 2.54 = 17.78
20 pins per row, the pitch differs from 2.54 mm by up to 0.254 µm
The rows are seven pitches apart, and the pitch is 2.54 mm to within 0.254 µm, a hundredth of a mil. That difference is in the PADS file itself, which stores some of the pins that far off the grid, and the conversion keeps it.