summaryrefslogtreecommitdiff
path: root/gerbonara/rs274x.py
diff options
context:
space:
mode:
Diffstat (limited to 'gerbonara/rs274x.py')
-rw-r--r--gerbonara/rs274x.py57
1 files changed, 37 insertions, 20 deletions
diff --git a/gerbonara/rs274x.py b/gerbonara/rs274x.py
index dbda955..3dd8bb7 100644
--- a/gerbonara/rs274x.py
+++ b/gerbonara/rs274x.py
@@ -4,7 +4,7 @@
# Modified from parser.py by Paulo Henrique Silva <ph.silva@gmail.com>
# Copyright 2014 Hamilton Kibbe <ham@hamiltonkib.be>
# Copyright 2019 Hiroshi Murayama <opiopan@gmail.com>
-# Copyright 2021 Jan Götte <code@jaseg.de>
+# Copyright 2022 Jan Götte <code@jaseg.de>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,8 +17,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-""" This module provides an RS-274-X class and parser.
-"""
+#
import re
import math
@@ -46,9 +45,7 @@ def points_close(a, b):
return math.isclose(a[0], b[0]) and math.isclose(a[1], b[1])
class GerberFile(CamFile):
- """ A class representing a single gerber file
-
- The GerberFile class represents a single gerber file.
+ """ A single gerber file.
"""
def __init__(self, objects=None, comments=None, import_settings=None, original_path=None, generator_hints=None,
@@ -81,7 +78,6 @@ class GerberFile(CamFile):
return
def merge(self, other):
- """ Merge other GerberFile into this one """
if other is None:
return
@@ -127,6 +123,7 @@ class GerberFile(CamFile):
seen_macro_names.add(new_name)
def dilate(self, offset, unit=MM, polarity_dark=True):
+ # TODO add tests for this
self.apertures = [ aperture.dilated(offset, unit) for aperture in self.apertures ]
offset_circle = CircleAperture(offset, unit=unit)
@@ -154,6 +151,17 @@ class GerberFile(CamFile):
@classmethod
def open(kls, filename, enable_includes=False, enable_include_dir=None):
+ """ Load a Gerber file from the file system. The Gerber standard contains this wonderful and totally not
+ insecure "include file" setting. We disable it by default and do not parse Gerber includes because a) nobody
+ actually uses them, and b) they're a bad idea from a security point of view. In case you actually want these,
+ you can enable them by setting ``enable_includes=True``.
+
+ :param filename: str or :py:class:`pathlib.Path`
+ :param bool enable_includes: Enable Gerber ``IF`` statement includes (default *off*, recommended *off*)
+ :param enable_include_dir: str or :py:class:`pathlib.Path`. Override base dir for include files.
+
+ :rtype: :py:class:`.GerberFile`
+ """
filename = Path(filename)
with open(filename, "r") as f:
if enable_includes and enable_include_dir is None:
@@ -162,12 +170,15 @@ class GerberFile(CamFile):
@classmethod
def from_string(kls, data, enable_include_dir=None, filename=None):
+ """ Parse given string as Gerber file content. For the meaning of the parameters, see
+ :py:meth:`~.GerberFile.open`. """
# filename arg is for error messages
obj = kls()
GerberParser(obj, include_dir=enable_include_dir).parse(data, filename=filename)
return obj
- def generate_statements(self, settings, drop_comments=True):
+ def _generate_statements(self, settings, drop_comments=True):
+ """ Export this file as Gerber code, yields one str per line. """
yield 'G04 Gerber file generated by Gerbonara*'
for name, value in self.file_attrs.items():
attrdef = ','.join([name, *map(str, value)])
@@ -222,17 +233,27 @@ class GerberFile(CamFile):
return f'<GerberFile {name}with {len(self.apertures)} apertures, {len(self.objects)} objects>'
def save(self, filename, settings=None, drop_comments=True):
+ """ Save this Gerber file to the file system. See :py:meth:`~.GerberFile.generate_gerber` for the meaning
+ of the arguments. """
with open(filename, 'w', encoding='utf-8') as f: # Encoding is specified as UTF-8 by spec.
f.write(self.generate_gerber(settings, drop_comments=drop_comments))
def generate_gerber(self, settings=None, drop_comments=True):
- # Use given settings, or use same settings as original file if not given, or use defaults if not imported from a
- # file
+ """ Export to Gerber format. Uses either the file's original settings or sane default settings if you don't give
+ any.
+
+ :param FileSettings settings: override export settings.
+ :param bool drop_comments: If true, do not write comments to output file. This defaults to true because
+ otherwise there is a risk that Gerbonara does not consider some obscure magic comment semantically
+ meaningful while some other Excellon viewer might still parse it.
+
+ :rtype: str
+ """
if settings is None:
settings = self.import_settings.copy() or FileSettings()
settings.zeros = None
settings.number_format = (5,6)
- return '\n'.join(self.generate_statements(settings, drop_comments=drop_comments))
+ return '\n'.join(self._generate_statements(settings, drop_comments=drop_comments))
@property
def is_empty(self):
@@ -250,15 +271,6 @@ class GerberFile(CamFile):
obj.with_offset(dx, dy, unit)
def rotate(self, angle:'radian', center=(0,0), unit=MM):
- """ Rotate file contents around given point.
-
- Arguments:
- angle -- Rotation angle in radian clockwise.
- center -- Center of rotation (default: document origin (0, 0))
-
- Note that when rotating by odd angles other than 0, 90, 180 or 270 degree this method may replace standard
- rect and oblong apertures by macro apertures. Existing macro apertures are re-written.
- """
if math.isclose(angle % (2*math.pi), 0):
return
@@ -271,11 +283,14 @@ class GerberFile(CamFile):
obj.rotate(angle, *center, unit)
def invert_polarity(self):
+ """ Invert the polarity (color) of each object in this file. """
for obj in self.objects:
obj.polarity_dark = not p.polarity_dark
class GraphicsState:
+ """ Internal class used to track Gerber processing state during import and export. """
+
def __init__(self, warn, file_settings=None, aperture_map=None):
self.image_polarity = 'positive' # IP image polarity; deprecated
self.polarity_dark = True
@@ -502,6 +517,8 @@ class GraphicsState:
class GerberParser:
+ """ Internal class that contains all of the actual Gerber parsing magic. """
+
NUMBER = r"[\+-]?\d+"
DECIMAL = r"[\+-]?\d+([.]?\d+)?"
NAME = r"[a-zA-Z_$\.][a-zA-Z_$\.0-9+\-]+"