From 68b2f9bd888f86766fea254d6253f3b1e88cc8a4 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Sat, 14 Jul 2018 15:00:49 +0200 Subject: wip --- src/ee/kicad/export_gerber.py | 259 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100755 src/ee/kicad/export_gerber.py (limited to 'src/ee/kicad/export_gerber.py') diff --git a/src/ee/kicad/export_gerber.py b/src/ee/kicad/export_gerber.py new file mode 100755 index 0000000..511f9ee --- /dev/null +++ b/src/ee/kicad/export_gerber.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python +from __future__ import print_function +import sys +import os +import argparse +from pcbnew import * + +def layer_name_parser(s): + parts = s.split('=') + if len(parts) != 2: + raise argparse.ArgumentTypeError("Invalid layer renaming: " + s) + else: + return parts + +parser = argparse.ArgumentParser(description='KiCAD PCB to GERBER converter') + +parser.add_argument('--pcb', + required=True, + dest='pcb', + action='store', + help='A foo.kicad_pcb file') + +parser.add_argument('--output-directory', + required=True, + dest='output_directory', + action='store', + help='Directory to store output files') + +parser.add_argument('--detect-files-only', + dest='detect_files_only', + action='store', + help='Don\'t create the GERBER files, just write a list of to be created') + +parser.add_argument('--create-drill-map-file', + dest='create_drill_map_file', + action='store_true', + help='Create drill map file') + +parser.add_argument('--protel-extensions', + dest='protel_extensions', + action='store_true', + help='Use Protel filename extensions instead of .gbr') + +parser.add_argument('--extended-gerber-attributes', + dest='extended_gerber_attributes', + action='store_true', + help='Use extended Gerber attributes') + +parser.add_argument('--uppercase-extensions', + action='store_true', + help='Uppercase all extensions') + +parser.add_argument('--layer-extension', + dest='layer_extensions', + metavar='LAYER_AND_EXTENSION', + action='append', + type=layer_name_parser, + help='Set the file extension of a KiCAD layer. Format: =, example: F.SilkS=GSILK') + +args = parser.parse_args() +# print "args: " + str(args) +# print "args.name_layer: " + str(args.name_layers) + +renames = {} +if args.layer_extensions is not None: + for s in args.layer_extensions: + renames[s[0]] = s[1] +# print("renames: " + str(renames)) + +board = LoadBoard(args.pcb) + +class Plan: + def __init__(self, layerNum, layerName, description): + self.layerNum = layerNum + self.layerName = layerName + self.description = description + self.postfix = "" + self.ext = None + + @staticmethod + def standard(layerNum, description): + layerName = board.GetLayerName(layerNum) + return Plan(layerNum, layerName, description) + + @staticmethod + def copper(layerNum): + layerName = board.GetLayerName(layerNum) + description = "Copper Layer " + layerName + return Plan(layerNum, layerName, description) + +plot_plan = [Plan.standard(layerNum, description) for (layerNum, description) in [ + (F_SilkS, "Silk front"), + (F_Mask, "Mask front"), + (F_Paste, "Paste front"), + (B_SilkS, "Silk bottom"), + (B_Mask, "Mask bottom"), + (B_Paste, "Paste bottom"), + (Edge_Cuts, "Edges")]] + +layers = board.GetEnabledLayers() +for layerNum in layers.CuStack(): + plot_plan.append(Plan.copper(layerNum)) + +pctl = PLOT_CONTROLLER(board) +popt = pctl.GetPlotOptions() + +output_directory = args.output_directory +popt.SetOutputDirectory(output_directory) + +if not os.path.isdir(output_directory): + try: + os.makedirs(output_directory) + except: + print("Could not make output directory", file=sys.stderr) + sys.exit(1) + +# A nasty hack to get the base filename +pctl.SetLayer(F_Cu) +pctl.OpenPlotfile("", PLOT_FORMAT_GERBER, "") +filename = pctl.GetPlotFileName() +try: + os.remove(filename) +except: + pass +pctl.ClosePlot() + +# "Use protel filename extensions", default=False +popt.SetUseGerberProtelExtensions(args.protel_extensions) + +basename = os.path.splitext(filename)[0] + +drlFileOut = drlFile = basename + "-PTH.drl" +drlNpthFileOut = drlNpthFile = basename + "-NPTH.drl" + +if args.uppercase_extensions: + n, e = os.path.splitext(drlFileOut) + drlFileOut = n + e.upper() + n, e = os.path.splitext(drlNpthFileOut) + drlNpthFileOut = n + e.upper() + +values = vars(args) +for plan in plot_plan: + pctl.SetLayer(plan.layerNum) + pctl.OpenPlotfile("", PLOT_FORMAT_GERBER, plan.description) + filename = pctl.GetPlotFileName() + pctl.ClosePlot() + # By opening and closing the plot we create an empty plot file. + try: + os.remove(filename) + except: + pass + + filename, ext = os.path.splitext(filename) + if args.uppercase_extensions: + ext = ext.upper() + + if plan.layerName in renames: + ext = "." + renames[plan.layerName] + + # if plan.arg is not None: + # newExt = values[plan.arg] + # if newExt is not None: + # ext = "." + newExt + + plan.postfix = plan.layerName + plan.filename = basename + "-" + plan.postfix + ext + + # print "filename = " + plan.filename + ", postfix=" + plan.postfix + # print "filename: " + plan.filename + +if args.detect_files_only: + with open(args.detect_files_only, "w") as f: + for plan in plot_plan: + print(plan.filename, file=f) + + print(drlFileOut, file=f) + print(drlNpthFileOut, file=f) + sys.exit(0) + +# Set some important plot options: +popt.SetPlotFrameRef(False) + +# "Default line width (mm)", default=0.1mm +popt.SetLineWidth(FromMM(0.1)) + +popt.SetAutoScale(False) + +# "Mirrored plot", default=False. Not applicable for Gerber +popt.SetMirror(False) + +# "Include extended attributes", default=False +popt.SetUseGerberAttributes(args.extended_gerber_attributes) + +# "Drill marks" +popt.SetDrillMarksType(PCB_PLOT_PARAMS.NO_DRILL_SHAPE) + +# "Scaling" +popt.SetScale(1) + +# "Use auxilary axis as origin" +popt.SetUseAuxOrigin(False) + +# This by gerbers only (also the name is truly horrid!) +popt.SetSubtractMaskFromSilk(False) + +for plan in plot_plan: + pctl.SetLayer(plan.layerNum) + + # print "filename = " + plan.filename + ", postfix=" + plan.postfix + pctl.OpenPlotfile(plan.postfix, PLOT_FORMAT_GERBER, plan.description) + + actualFilename = pctl.GetPlotFileName() + expectedFilename = plan.filename + + if expectedFilename != actualFilename: + os.rename(actualFilename, expectedFilename) + + pctl.PlotLayer() + pctl.ClosePlot() + +drlwriter = EXCELLON_WRITER(board) +drlwriter.SetMapFileFormat(PLOT_FORMAT_GERBER) + +# "Drill file options" + +# "Mirror y axis", default=False +mirror = False + +# "Minimal header", default=True +minimalHeader = True + +offset = wxPoint(0, 0) + +# "Merge PTH and NPTH into one file", default=False +mergeNPTH = False +drlwriter.SetOptions(mirror, minimalHeader, offset, mergeNPTH) + +metricFmt = True +drlwriter.SetFormat(metricFmt) + +genDrl = True +genMap = args.create_drill_map_file + +drlwriter.CreateDrillandMapFilesSet(pctl.GetPlotDirName(), genDrl, genMap) + +# This has to be verified that this is the right way to do it. +if False: +# Check that the drill files actually was generated + if not os.path.isfile(drlFile): + print("No drill file generated: {}".format(drlFile), file=sys.stderr) + + if not os.path.isfile(drlNpthFile): + print("No drill file generated: {}".format(drlNpthFile), file=sys.stderr) + +if drlFile != drlFileOut: + os.rename(drlFile, drlFileOut) + +if drlNpthFile != drlNpthFileOut: + os.rename(drlNpthFile, drlNpthFileOut) -- cgit v1.2.3