summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md7
-rw-r--r--gerber.md73
-rw-r--r--gerber.py129
3 files changed, 209 insertions, 0 deletions
diff --git a/README.md b/README.md
index 31ca1d7..d929f94 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,9 @@
gerber-tools
============
+
+This hopefully will be a useful set of tools to handle Gerber files in Python.
+
+Right now we have a working parser and I am working on simple Gerber to SVG converter.
+
+See gerber.md for some random information regardind Gerber format.
+
diff --git a/gerber.md b/gerber.md
new file mode 100644
index 0000000..865d177
--- /dev/null
+++ b/gerber.md
@@ -0,0 +1,73 @@
+
+# Gerber (RS-274X or Extended Gerber) is a bilevel, resolution independent image format.
+
+# // graphic objects
+# // draw: line segment, thickness, round or square line endings. (solid circle and rectangule apertures only)
+# // arc: circular arc, thickness, round endings. (solid circle standard aperture only)
+# // flash: replication of a given apertura (shape)
+# // region: are defined by a countour (linear/arc segments.)
+#
+# // draw/arc: can have zero length (just flash the aperture)
+# // flash: any aperture can be flashed
+#
+# // operation codes operates on coordinate data blocks. each operation code is for one coordinate data block pair and vice-versa.
+# // D01: stroke an aperture from current point to coordinate pair. region mode off. lights-on move.
+# // D02: move current point to this coordinate pair
+# // D03: flash current aperture at this coordinate pair.
+#
+# // graphics state
+# // all state controlled by codes and parameters, except current point
+# //
+# // state fixed? initial value
+# // coordinate format fixed undefined
+# // unit fixed undefined
+# // image polarity fixed positive
+# // steps/repeat variable 1,1,-,-
+# // level polarity variable dark
+# // region mode variable off
+# // current aperture variable undefined
+# // quadrant mode variable undefined
+# // interpolation mode variable undefined
+# // current point variable (0,0)
+#
+# // attributes: metadata, both standard and custom. No change on image.
+#
+# // G01: linear
+# // G04: comment
+# // M02: end of file
+# // D: select aperture
+# // G75: multi quadrant mode (circles)
+# // G36: region begin
+# // G37: region end
+#
+# // [G01] [Xnnfffff] [Ynnffff] D01*
+#
+# // ASCII 32-126, CR LF.
+# // * end-of-block
+# // % parameer delimiter
+# // , field separator
+# // <space> only in comments
+# // case sensitive
+#
+# // int: +/- 32 bit signed
+# // decimal: +/- digits
+# // names: [a-zA-Z_$]{[a-zA-Z_$0-9]+} (255)
+# // strings: [a-zA-Z0-9_+-/!?<>”’(){}.\|&@# ]+ (65535)
+#
+# // data block: end in *
+# // statement: one or more data block, if contain parameters starts and end in % (parameter statement)
+# // statement: [%]<Data Block>{<Data Block>}[%]
+# // statements: function code, coordinate data, parameters
+#
+# // function code: operation codes (D01..) or code that set state.
+# // function codes applies before operation codes act on coordinates
+#
+# // coordinate data: <Coordinate data>: [X<Number>][Y<Number>][I<Number>][J<Number>](D01|D02|D03)
+# // offsets are not modal
+#
+# // parameter: %Parameter code<required modifiers>[optional modifiers]*%
+# // code: 2 characters
+#
+# // parameters can have line separators: %<Parameter>{{<Line separator>}<Parameter>}%
+#
+# // function code: (GDM){1}[number], parameters: [AZ]{2} \ No newline at end of file
diff --git a/gerber.py b/gerber.py
new file mode 100644
index 0000000..74da114
--- /dev/null
+++ b/gerber.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import re
+
+def red(s):
+ return '\033[1;31m{0}\033[0;m'.format(s)
+
+class Statement:
+ pass
+
+class ParamStmt(Statement):
+ pass
+
+class CoordStmt(Statement):
+ pass
+
+class ApertureStmt(Statement):
+ pass
+
+class CommentStmt(Statement):
+ def __init__(self, comment):
+ self.comment = comment
+
+class EofStmt(Statement):
+ pass
+
+class UnexpectedStmt(Statement):
+ def __init__(self, line):
+ self.line = line
+
+class GerberContext:
+ x = 0
+ y = 0
+
+class Gerber:
+ NUMBER = r"[\+-]?\d+"
+ FUNCTION = r"G\d{2}"
+ STRING = r"[a-zA-Z0-9_+-/!?<>”’(){}.\|&@# :]+"
+
+ COORD_OP = r"D[0]?[123]"
+
+ PARAM_STMT = re.compile(r"%.*%")
+
+ COORD_STMT = re.compile((
+ r"(?P<f>{f})?"
+ r"(X(?P<x>{number}))?(Y(?P<y>{number}))?"
+ r"(I(?P<i>{number}))?(J(?P<j>{number}))?"
+ r"(?P<op>{op})?\*".format(number=NUMBER, f=FUNCTION, op=COORD_OP)))
+
+ APERTURE_STMT = re.compile(r"(G54)?D\d+\*")
+
+ COMMENT_STMT = re.compile(r"G04(?P<comment>{string})\*".format(string=STRING))
+
+ EOF_STMT = re.compile(r"M02\*")
+
+ def __init__(self):
+ self.apertures = {}
+ self.ctx = GerberContext()
+
+ def parse(self, filename):
+
+ fp = open(filename, "r")
+ data = fp.readlines()
+
+ self.tokens = list(self.tokenize(data))
+ for token in self.tokens:
+ if isinstance(token, UnexpectedStmt):
+ print filename
+ print red("[UNEXPECTED TOKEN]")
+ print self.COORD_STMT.pattern
+ print token.line
+
+ def tokenize(self, data):
+ multiline = None
+
+ for i, line in enumerate(data):
+ # remove EOL
+ if multiline:
+ line = multiline + line.strip()
+ else:
+ line = line.strip()
+
+ # deal with multi-line parameters
+ if line.startswith("%") and not line.endswith("%"):
+ multiline = line
+ continue
+ else:
+ multiline = None
+
+ # parameter
+ match = self.PARAM_STMT.match(line)
+ if match:
+ yield ParamStmt()
+ continue
+
+ # coord
+ matches = self.COORD_STMT.finditer(line)
+ if matches:
+ for match in matches:
+ yield CoordStmt()
+ continue
+
+ # aperture selection
+ match = self.APERTURE_STMT.match(line)
+ if match:
+ yield ApertureStmt()
+ continue
+
+ # comment
+ match = self.COMMENT_STMT.match(line)
+ if match:
+ yield CommentStmt(match.groupdict("comment"))
+ continue
+
+ # eof
+ match = self.EOF_STMT.match(line)
+ if match:
+ yield EofStmt()
+ continue
+
+ yield UnexpectedStmt(line)
+
+if __name__ == "__main__":
+ import sys
+
+ for f in sys.argv[1:]:
+ g = Gerber()
+ g.parse(f)