summaryrefslogtreecommitdiff
path: root/gerber
diff options
context:
space:
mode:
Diffstat (limited to 'gerber')
-rw-r--r--gerber/gerber_statements.py434
-rw-r--r--gerber/rs274x.py47
2 files changed, 327 insertions, 154 deletions
diff --git a/gerber/gerber_statements.py b/gerber/gerber_statements.py
index 05c84b8..1a6f646 100644
--- a/gerber/gerber_statements.py
+++ b/gerber/gerber_statements.py
@@ -23,13 +23,6 @@ Gerber (RS-274X) Statements
from .utils import parse_gerber_value, write_gerber_value, decimal_string
-__all__ = ['FSParamStmt', 'MOParamStmt', 'IPParamStmt', 'OFParamStmt',
- 'LPParamStmt', 'ADParamStmt', 'AMParamStmt', 'INParamStmt',
- 'LNParamStmt', 'CoordStmt', 'ApertureStmt', 'CommentStmt',
- 'EofStmt', 'QuadrantModeStmt', 'RegionModeStmt', 'UnknownStmt',
- 'ParamStmt']
-
-
class Statement(object):
""" Gerber statement Base class
@@ -128,17 +121,21 @@ class FSParamStmt(ParamStmt):
self.notation = notation
self.format = format
- def to_gerber(self):
- zero_suppression = 'L' if self.zero_suppression == 'leading' else 'T'
- notation = 'A' if self.notation == 'absolute' else 'I'
- fmt = ''.join(map(str, self.format))
- return '%FS{0}{1}X{2}Y{3}*%'.format(zero_suppression, notation,
- fmt, fmt)
+ def to_gerber(self, settings=None):
+ if settings:
+ zero_suppression = 'L' if settings.zero_suppression == 'leading' else 'T'
+ notation = 'A' if settings.notation == 'absolute' else 'I'
+ fmt = ''.join(map(str, settings.format))
+ else:
+ zero_suppression = 'L' if self.zero_suppression == 'leading' else 'T'
+ notation = 'A' if self.notation == 'absolute' else 'I'
+ fmt = ''.join(map(str, self.format))
+
+ return '%FS{0}{1}X{2}Y{3}*%'.format(zero_suppression, notation, fmt, fmt)
def __str__(self):
return ('<Format Spec: %d:%d %s zero suppression %s notation>' %
- (self.format[0], self.format[1], self.zero_suppression,
- self.notation))
+ (self.format[0], self.format[1], self.zero_suppression, self.notation))
class MOParamStmt(ParamStmt):
@@ -176,7 +173,7 @@ class MOParamStmt(ParamStmt):
ParamStmt.__init__(self, param)
self.mode = mo
- def to_gerber(self):
+ def to_gerber(self, settings=None):
mode = 'MM' if self.mode == 'metric' else 'IN'
return '%MO{0}*%'.format(mode)
@@ -185,95 +182,6 @@ class MOParamStmt(ParamStmt):
return ('<Mode: %s>' % mode_str)
-class IPParamStmt(ParamStmt):
- """ IP - Gerber Image Polarity Statement. (Deprecated)
- """
- @classmethod
- def from_dict(cls, stmt_dict):
- param = stmt_dict.get('param')
- ip = 'positive' if stmt_dict.get('ip') == 'POS' else 'negative'
- return cls(param, ip)
-
- def __init__(self, param, ip):
- """ Initialize IPParamStmt class
-
- Parameters
- ----------
- param : string
- Parameter string.
-
- ip : string
- Image polarity. May be either'positive' or 'negative'
-
- Returns
- -------
- ParamStmt : IPParamStmt
- Initialized IPParamStmt class.
-
- """
- ParamStmt.__init__(self, param)
- self.ip = ip
-
- def to_gerber(self):
- ip = 'POS' if self.ip == 'positive' else 'NEG'
- return '%IP{0}*%'.format(ip)
-
- def __str__(self):
- return ('<Image Polarity: %s>' % self.ip)
-
-
-class OFParamStmt(ParamStmt):
- """ OF - Gerber Offset statement (Deprecated)
- """
-
- @classmethod
- def from_dict(cls, stmt_dict):
- param = stmt_dict.get('param')
- a = float(stmt_dict.get('a', 0))
- b = float(stmt_dict.get('b', 0))
- return cls(param, a, b)
-
- def __init__(self, param, a, b):
- """ Initialize OFParamStmt class
-
- Parameters
- ----------
- param : string
- Parameter
-
- a : float
- Offset along the output device A axis
-
- b : float
- Offset along the output device B axis
-
- Returns
- -------
- ParamStmt : OFParamStmt
- Initialized OFParamStmt class.
-
- """
- ParamStmt.__init__(self, param)
- self.a = a
- self.b = b
-
- def to_gerber(self):
- ret = '%OF'
- if self.a is not None:
- ret += 'A' + decimal_string(self.a, precision=5)
- if self.b is not None:
- ret += 'B' + decimal_string(self.b, precision=5)
- return ret + '*%'
-
- def __str__(self):
- offset_str = ''
- if self.a is not None:
- offset_str += ('X: %f' % self.a)
- if self.b is not None:
- offset_str += ('Y: %f' % self.b)
- return ('<Offset: %s>' % offset_str)
-
-
class LPParamStmt(ParamStmt):
""" LP - Gerber Level Polarity statement
"""
@@ -304,7 +212,7 @@ class LPParamStmt(ParamStmt):
ParamStmt.__init__(self, param)
self.lp = lp
- def to_gerber(self):
+ def to_gerber(self, settings=None):
lp = 'C' if self.lp == 'clear' else 'D'
return '%LP{0}*%'.format(lp)
@@ -351,13 +259,15 @@ class ADParamStmt(ParamStmt):
self.d = d
self.shape = shape
if modifiers is not None:
- self.modifiers = [[x for x in m.split("X")] for m in modifiers.split(",")]
+ self.modifiers = [[x for x in m.split("X")] for m in modifiers.split(",") if len(m)]
else:
self.modifiers = []
- def to_gerber(self):
- return '%ADD{0}{1},{2}*%'.format(self.d, self.shape,
- ','.join(['X'.join(e) for e in self.modifiers]))
+ def to_gerber(self, settings=None):
+ if len(self.modifiers):
+ return '%ADD{0}{1},{2}*%'.format(self.d, self.shape, ','.join(['X'.join(e) for e in self.modifiers]))
+ else:
+ return '%ADD{0}{1}*%'.format(self.d, self.shape)
def __str__(self):
if self.shape == 'C':
@@ -404,15 +314,51 @@ class AMParamStmt(ParamStmt):
self.name = name
self.macro = macro
- def to_gerber(self):
+ def to_gerber(self, settings=None):
return '%AM{0}*{1}*%'.format(self.name, self.macro)
def __str__(self):
return '<Aperture Macro %s: %s>' % (self.name, self.macro)
+class ASParamStmt(ParamStmt):
+ """ AS - Axis Select. (Deprecated)
+ """
+ @classmethod
+ def from_dict(cls, stmt_dict):
+ param = stmt_dict.get('param')
+ mode = stmt_dict.get('mode')
+ return cls(param, mode)
+
+ def __init__(self, param, ip):
+ """ Initialize ASParamStmt class
+
+ Parameters
+ ----------
+ param : string
+ Parameter string.
+
+ mode : string
+ Axis select. May be either 'AXBY' or 'AYBX'
+
+ Returns
+ -------
+ ParamStmt : ASParamStmt
+ Initialized ASParamStmt class.
+
+ """
+ ParamStmt.__init__(self, param)
+ self.mode = mode
+
+ def to_gerber(self, settings=None):
+ return '%AS{0}*%'.format(self.mode)
+
+ def __str__(self):
+ return ('<Axis Select: %s>' % self.mode)
+
+
class INParamStmt(ParamStmt):
- """ IN - Image Name Statement
+ """ IN - Image Name Statement (Deprecated)
"""
@classmethod
def from_dict(cls, stmt_dict):
@@ -438,13 +384,235 @@ class INParamStmt(ParamStmt):
ParamStmt.__init__(self, param)
self.name = name
- def to_gerber(self):
+ def to_gerber(self, settings=None):
return '%IN{0}*%'.format(self.name)
def __str__(self):
return '<Image Name: %s>' % self.name
+class IPParamStmt(ParamStmt):
+ """ IP - Gerber Image Polarity Statement. (Deprecated)
+ """
+ @classmethod
+ def from_dict(cls, stmt_dict):
+ param = stmt_dict.get('param')
+ ip = 'positive' if stmt_dict.get('ip') == 'POS' else 'negative'
+ return cls(param, ip)
+
+ def __init__(self, param, ip):
+ """ Initialize IPParamStmt class
+
+ Parameters
+ ----------
+ param : string
+ Parameter string.
+
+ ip : string
+ Image polarity. May be either'positive' or 'negative'
+
+ Returns
+ -------
+ ParamStmt : IPParamStmt
+ Initialized IPParamStmt class.
+
+ """
+ ParamStmt.__init__(self, param)
+ self.ip = ip
+
+ def to_gerber(self, settings=None):
+ ip = 'POS' if self.ip == 'positive' else 'NEG'
+ return '%IP{0}*%'.format(ip)
+
+ def __str__(self):
+ return ('<Image Polarity: %s>' % self.ip)
+
+
+class IRParamStmt(ParamStmt):
+ """ IR - Image Rotation Param (Deprecated)
+ """
+ @classmethod
+ def from_dict(cls, stmt_dict):
+ return cls(**stmt_dict)
+
+ def __init__(self, param, angle):
+ """ Initialize IRParamStmt class
+
+ Parameters
+ ----------
+ param : string
+ Parameter code
+
+ angle : int
+ Image angle
+
+ Returns
+ -------
+ ParamStmt : IRParamStmt
+ Initialized IRParamStmt class.
+
+ """
+ ParamStmt.__init__(self, param)
+ self.angle = angle
+
+ def to_gerber(self, settings=None):
+ return '%IR{0}*%'.format(self.angle)
+
+ def __str__(self):
+ return '<Image Angle: %s>' % self.angle
+
+
+class MIParamStmt(ParamStmt):
+ """ MI - Image Mirror Param (Deprecated)
+ """
+ @classmethod
+ def from_dict(cls, stmt_dict):
+ param = stmt_dict.get('param')
+ a = int(stmt_dict.get('a', 0))
+ b = int(stmt_dict.get('b', 0))
+ return cls(param, a, b)
+
+ def __init__(self, param, a, b):
+ """ Initialize MIParamStmt class
+
+ Parameters
+ ----------
+ param : string
+ Parameter code
+
+ a : int
+ Mirror for A output devices axis (0=disabled, 1=mirrored)
+
+ b : int
+ Mirror for B output devices axis (0=disabled, 1=mirrored)
+
+ Returns
+ -------
+ ParamStmt : MIParamStmt
+ Initialized MIParamStmt class.
+
+ """
+ ParamStmt.__init__(self, param)
+ self.a = a
+ self.b = b
+
+ def to_gerber(self, settings=None):
+ ret = "%MI"
+ if self.a is not None:
+ ret += "A{0}".format(self.a)
+ if self.b is not None:
+ ret += "B{0}".format(self.b)
+
+ return ret
+
+ def __str__(self):
+ return '<Image Mirror: A=%d B=%d>' % (self.a, self.b)
+
+
+class OFParamStmt(ParamStmt):
+ """ OF - Gerber Offset statement (Deprecated)
+ """
+
+ @classmethod
+ def from_dict(cls, stmt_dict):
+ param = stmt_dict.get('param')
+ a = float(stmt_dict.get('a', 0))
+ b = float(stmt_dict.get('b', 0))
+ return cls(param, a, b)
+
+ def __init__(self, param, a, b):
+ """ Initialize OFParamStmt class
+
+ Parameters
+ ----------
+ param : string
+ Parameter
+
+ a : float
+ Offset along the output device A axis
+
+ b : float
+ Offset along the output device B axis
+
+ Returns
+ -------
+ ParamStmt : OFParamStmt
+ Initialized OFParamStmt class.
+
+ """
+ ParamStmt.__init__(self, param)
+ self.a = a
+ self.b = b
+
+ def to_gerber(self, settings=None):
+ ret = '%OF'
+ if self.a is not None:
+ ret += 'A' + decimal_string(self.a, precision=5)
+ if self.b is not None:
+ ret += 'B' + decimal_string(self.b, precision=5)
+ return ret + '*%'
+
+ def __str__(self):
+ offset_str = ''
+ if self.a is not None:
+ offset_str += ('X: %f' % self.a)
+ if self.b is not None:
+ offset_str += ('Y: %f' % self.b)
+ return ('<Offset: %s>' % offset_str)
+
+
+class SFParamStmt(ParamStmt):
+ """ SF - Scale Factor Param (Deprecated)
+ """
+
+ @classmethod
+ def from_dict(cls, stmt_dict):
+ param = stmt_dict.get('param')
+ a = float(stmt_dict.get('a', 1))
+ b = float(stmt_dict.get('b', 1))
+ return cls(param, a, b)
+
+ def __init__(self, param, a, b):
+ """ Initialize OFParamStmt class
+
+ Parameters
+ ----------
+ param : string
+ Parameter
+
+ a : float
+ Scale factor for the output device A axis
+
+ b : float
+ Scale factor for the output device B axis
+
+ Returns
+ -------
+ ParamStmt : SFParamStmt
+ Initialized SFParamStmt class.
+
+ """
+ ParamStmt.__init__(self, param)
+ self.a = a
+ self.b = b
+
+ def to_gerber(self, settings=None):
+ ret = '%SF'
+ if self.a is not None:
+ ret += 'A' + decimal_string(self.a, precision=5)
+ if self.b is not None:
+ ret += 'B' + decimal_string(self.b, precision=5)
+ return ret + '*%'
+
+ def __str__(self):
+ scale_factor = ''
+ if self.a is not None:
+ scale_factor += ('X: %f' % self.a)
+ if self.b is not None:
+ scale_factor += ('Y: %f' % self.b)
+ return ('<Scale Factor: %s>' % scale_factor)
+
+
class LNParamStmt(ParamStmt):
""" LN - Level Name Statement (Deprecated)
"""
@@ -472,7 +640,7 @@ class LNParamStmt(ParamStmt):
ParamStmt.__init__(self, param)
self.name = name
- def to_gerber(self):
+ def to_gerber(self, settings=None):
return '%LN{0}*%'.format(self.name)
def __str__(self):
@@ -485,8 +653,6 @@ class CoordStmt(Statement):
@classmethod
def from_dict(cls, stmt_dict, settings):
- zeros = settings.zero_suppression
- format = settings.format
function = stmt_dict['function']
x = stmt_dict.get('x')
y = stmt_dict.get('y')
@@ -495,17 +661,13 @@ class CoordStmt(Statement):
op = stmt_dict.get('op')
if x is not None:
- x = parse_gerber_value(stmt_dict.get('x'),
- format, zeros)
+ x = parse_gerber_value(stmt_dict.get('x'), settings.format, settings.zero_suppression)
if y is not None:
- y = parse_gerber_value(stmt_dict.get('y'),
- format, zeros)
+ y = parse_gerber_value(stmt_dict.get('y'), settings.format, settings.zero_suppression)
if i is not None:
- i = parse_gerber_value(stmt_dict.get('i'),
- format, zeros)
+ i = parse_gerber_value(stmt_dict.get('i'), settings.format, settings.zero_suppression)
if j is not None:
- j = parse_gerber_value(stmt_dict.get('j'),
- format, zeros)
+ j = parse_gerber_value(stmt_dict.get('j'), settings.format, settings.zero_suppression)
return cls(function, x, y, i, j, op, settings)
def __init__(self, function, x, y, i, j, op, settings):
@@ -541,8 +703,6 @@ class CoordStmt(Statement):
"""
Statement.__init__(self, "COORD")
- self.zero_suppression = settings.zero_suppression
- self.format = settings.format
self.function = function
self.x = x
self.y = y
@@ -550,18 +710,18 @@ class CoordStmt(Statement):
self.j = j
self.op = op
- def to_gerber(self):
+ def to_gerber(self, settings=None):
ret = ''
if self.function:
ret += self.function
if self.x is not None:
- ret += 'X{0}'.format(write_gerber_value(self.x, self.format, self.zero_suppression))
+ ret += 'X{0}'.format(write_gerber_value(self.x, settings.format, settings.zero_suppression))
if self.y is not None:
- ret += 'Y{0}'.format(write_gerber_value(self.y, self.format, self.zero_suppression))
+ ret += 'Y{0}'.format(write_gerber_value(self.y, settings.format, settings.zero_suppression))
if self.i is not None:
- ret += 'I{0}'.format(write_gerber_value(self.i, self.format, self.zero_suppression))
+ ret += 'I{0}'.format(write_gerber_value(self.i, settings.format, settings.zero_suppression))
if self.j is not None:
- ret += 'J{0}'.format(write_gerber_value(self.j, self.format, self.zero_suppression))
+ ret += 'J{0}'.format(write_gerber_value(self.j, settings.format, settings.zero_suppression))
if self.op:
ret += self.op
return ret + '*'
@@ -600,7 +760,7 @@ class ApertureStmt(Statement):
self.d = int(d)
self.deprecated = True if deprecated is not None else False
- def to_gerber(self):
+ def to_gerber(self, settings=None):
if self.deprecated:
return 'G54D{0}*'.format(self.d)
else:
@@ -618,7 +778,7 @@ class CommentStmt(Statement):
Statement.__init__(self, "COMMENT")
self.comment = comment
- def to_gerber(self):
+ def to_gerber(self, settings=None):
return 'G04{0}*'.format(self.comment)
def __str__(self):
@@ -631,7 +791,7 @@ class EofStmt(Statement):
def __init__(self):
Statement.__init__(self, "EOF")
- def to_gerber(self):
+ def to_gerber(self, settings=None):
return 'M02*'
def __str__(self):
@@ -656,7 +816,7 @@ class QuadrantModeStmt(Statement):
or "multi-quadrant"')
self.mode = mode
- def to_gerber(self):
+ def to_gerber(self, settings=None):
return 'G74*' if self.mode == 'single-quadrant' else 'G75*'
@@ -675,7 +835,7 @@ class RegionModeStmt(Statement):
raise ValueError('Valid modes are "on" or "off"')
self.mode = mode
- def to_gerber(self):
+ def to_gerber(self, settings=None):
return 'G36*' if self.mode == 'on' else 'G37*'
@@ -686,5 +846,5 @@ class UnknownStmt(Statement):
Statement.__init__(self, "UNKNOWN")
self.line = line
- def to_gerber(self):
+ def to_gerber(self, settings=None):
return self.line
diff --git a/gerber/rs274x.py b/gerber/rs274x.py
index 6dbcc63..8f4a171 100644
--- a/gerber/rs274x.py
+++ b/gerber/rs274x.py
@@ -104,12 +104,13 @@ class GerberFile(CamFile):
return (xbounds, ybounds)
- def write(self, filename):
+ def write(self, filename, settings=None):
""" Write data out to a gerber file
"""
with open(filename, 'w') as f:
for statement in self.statements:
- f.write(statement.to_gerber() + "\n")
+ f.write(statement.to_gerber(settings or self.settings))
+ f.write("\n")
class GerberParser(object):
@@ -125,7 +126,6 @@ class GerberParser(object):
FS = r"(?P<param>FS)(?P<zero>(L|T))?(?P<notation>(A|I))X(?P<x>[0-7][0-7])Y(?P<y>[0-7][0-7])"
MO = r"(?P<param>MO)(?P<mo>(MM|IN))"
- IP = r"(?P<param>IP)(?P<ip>(POS|NEG))"
LP = r"(?P<param>LP)(?P<lp>(D|C))"
AD_CIRCLE = r"(?P<param>AD)D(?P<d>\d+)(?P<shape>C)[,]?(?P<modifiers>[^,]*)?"
AD_RECT = r"(?P<param>AD)D(?P<d>\d+)(?P<shape>R)[,](?P<modifiers>[^,]*)"
@@ -135,13 +135,18 @@ class GerberParser(object):
AM = r"(?P<param>AM)(?P<name>{name})\*(?P<macro>.*)".format(name=NAME)
# begin deprecated
- OF = r"(?P<param>OF)(A(?P<a>{decimal}))?(B(?P<b>{decimal}))?".format(decimal=DECIMAL)
+ AS = r"(?P<param>AS)(?P<mode>(AXBY)|(AYBX))"
IN = r"(?P<param>IN)(?P<name>.*)"
+ IP = r"(?P<param>IP)(?P<ip>(POS|NEG))"
+ IR = r"(?P<param>IR)(?P<angle>{number})".format(number=NUMBER)
+ MI = r"(?P<param>MI)(A(?P<a>0|1))?(B(?P<b>0|1))?"
+ OF = r"(?P<param>OF)(A(?P<a>{decimal}))?(B(?P<b>{decimal}))?".format(decimal=DECIMAL)
+ SF = r"(?P<param>SF)(?P<discarded>.*)"
LN = r"(?P<param>LN)(?P<name>.*)"
# end deprecated
- PARAMS = (FS, MO, IP, LP, AD_CIRCLE, AD_RECT, AD_OBROUND, AD_POLY, AD_MACRO, AM, OF, IN, LN)
- PARAM_STMT = [re.compile(r"%{0}\*%".format(p)) for p in PARAMS]
+ PARAMS = (FS, MO, LP, AD_CIRCLE, AD_RECT, AD_OBROUND, AD_POLY, AD_MACRO, AM, AS, IN, IP, IR, MI, OF, SF, LN)
+ PARAM_STMT = [re.compile(r"%?{0}\*%?".format(p)) for p in PARAMS]
COORD_STMT = re.compile((
r"(?P<function>{function})?"
@@ -149,7 +154,7 @@ class GerberParser(object):
r"(I(?P<i>{number}))?(J(?P<j>{number}))?"
r"(?P<op>{op})?\*".format(number=NUMBER, function=FUNCTION, op=COORD_OP)))
- APERTURE_STMT = re.compile(r"(?P<deprecated>G54)?D(?P<d>\d+)\*")
+ APERTURE_STMT = re.compile(r"(?P<deprecated>(G54)|G55)?D(?P<d>\d+)\*")
COMMENT_STMT = re.compile(r"G04(?P<comment>[^*]*)(\*)?")
@@ -270,8 +275,6 @@ class GerberParser(object):
stmt = MOParamStmt.from_dict(param)
self.settings.units = stmt.mode
yield stmt
- elif param["param"] == "IP":
- yield IPParamStmt.from_dict(param)
elif param["param"] == "LP":
yield LPParamStmt.from_dict(param)
elif param["param"] == "AD":
@@ -284,8 +287,26 @@ class GerberParser(object):
yield INParamStmt.from_dict(param)
elif param["param"] == "LN":
yield LNParamStmt.from_dict(param)
+ # deprecated commands AS, IN, IP, IR, MI, OF, SF, LN
+ elif param["param"] == "AS":
+ yield ASParamStmt.from_dict(param)
+ elif param["param"] == "IN":
+ yield INParamStmt.from_dict(param)
+ elif param["param"] == "IP":
+ yield IPParamStmt.from_dict(param)
+ elif param["param"] == "IR":
+ yield IRParamStmt.from_dict(param)
+ elif param["param"] == "MI":
+ yield MIParamStmt.from_dict(param)
+ elif param["param"] == "OF":
+ yield OFParamStmt.from_dict(param)
+ elif param["param"] == "SF":
+ yield SFParamStmt.from_dict(param)
+ elif param["param"] == "LN":
+ yield LNParamStmt.from_dict(param)
else:
yield UnknownStmt(line)
+
did_something = True
line = r
continue
@@ -298,14 +319,6 @@ class GerberParser(object):
line = r
continue
- if False:
- print self.COORD_STMT.pattern
- print self.APERTURE_STMT.pattern
- print self.COMMENT_STMT.pattern
- print self.EOF_STMT.pattern
- for i in self.PARAM_STMT:
- print i.pattern
-
if line.find('*') > 0:
yield UnknownStmt(line)
did_something = True