diff options
-rw-r--r-- | .github/workflows/pcb-tools.yml | 2 | ||||
-rw-r--r-- | gerber/__main__.py | 127 | ||||
-rw-r--r-- | gerber/layers.py | 33 | ||||
-rw-r--r-- | gerber/pcb.py | 11 | ||||
-rw-r--r-- | gerber/render/__init__.py | 5 | ||||
-rw-r--r-- | setup.py | 13 |
6 files changed, 151 insertions, 40 deletions
diff --git a/.github/workflows/pcb-tools.yml b/.github/workflows/pcb-tools.yml index 59abe6c..0575212 100644 --- a/.github/workflows/pcb-tools.yml +++ b/.github/workflows/pcb-tools.yml @@ -6,7 +6,7 @@ jobs: test: strategy: matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8] + python-version: [3.5, 3.6, 3.7, 3.8] runs-on: ubuntu-latest diff --git a/gerber/__main__.py b/gerber/__main__.py index 50c5a42..988adff 100644 --- a/gerber/__main__.py +++ b/gerber/__main__.py @@ -15,27 +15,108 @@ # License for the specific language governing permissions and limitations under # the License. +import os +import argparse +from .render import available_renderers +from .render import theme +from .pcb import PCB +from . import load_layer + + +def main(): + parser = argparse.ArgumentParser( + description='Render gerber files to image', + prog='gerber-render' + ) + parser.add_argument( + 'filenames', metavar='FILENAME', type=str, nargs='+', + help='Gerber files to render. If a directory is provided, it should ' + 'be provided alone and should contain the gerber files for a ' + 'single PCB.' + ) + parser.add_argument( + '--outfile', '-o', type=str, nargs='?', default='out', + help="Output Filename (extension will be added automatically)" + ) + parser.add_argument( + '--backend', '-b', choices=available_renderers.keys(), default='cairo', + help='Choose the backend to use to generate the output.' + ) + parser.add_argument( + '--theme', '-t', choices=theme.THEMES.keys(), default='default', + help='Select render theme.' + ) + parser.add_argument( + '--width', type=int, default=1920, help='Maximum width.' + ) + parser.add_argument( + '--height', type=int, default=1080, help='Maximum height.' + ) + parser.add_argument( + '--verbose', '-v', action='store_true', default=False, + help='Increase verbosity of the output.' + ) + # parser.add_argument( + # '--quick', '-q', action='store_true', default=False, + # help='Skip longer running rendering steps to produce lower quality' + # ' output faster. This only has an effect for the freecad backend.' + # ) + # parser.add_argument( + # '--nox', action='store_true', default=False, + # help='Run without using any GUI elements. This may produce suboptimal' + # 'output. For the freecad backend, colors, transparancy, and ' + # 'visibility cannot be set without a GUI instance.' + # ) + + args = parser.parse_args() + + renderer = available_renderers[args.backend]() + + if args.backend in ['cairo', ]: + outext = 'png' + else: + outext = None + + if os.path.exists(args.filenames[0]) and os.path.isdir(args.filenames[0]): + directory = args.filenames[0] + pcb = PCB.from_directory(directory) + + if args.backend in ['cairo', ]: + top = pcb.top_layers + bottom = pcb.bottom_layers + copper = pcb.copper_layers + + outline = pcb.outline_layer + if outline: + top = [outline] + top + bottom = [outline] + bottom + copper = [outline] + copper + pcb.drill_layers + + renderer.render_layers( + layers=top, theme=theme.THEMES[args.theme], + max_height=args.height, max_width=args.width, + filename='{0}.top.{1}'.format(args.outfile, outext) + ) + renderer.render_layers( + layers=bottom, theme=theme.THEMES[args.theme], + max_height=args.height, max_width=args.width, + filename='{0}.bottom.{1}'.format(args.outfile, outext) + ) + renderer.render_layers( + layers=copper, theme=theme.THEMES['Transparent Multilayer'], + max_height=args.height, max_width=args.width, + filename='{0}.copper.{1}'.format(args.outfile, outext)) + else: + pass + else: + filenames = args.filenames + for filename in filenames: + layer = load_layer(filename) + settings = theme.THEMES[args.theme].get(layer.layer_class, None) + renderer.render_layer(layer, settings=settings) + renderer.dump(filename='{0}.{1}'.format(args.outfile, outext)) + + if __name__ == '__main__': - from gerber.common import read - from gerber.render.cairo_backend import GerberCairoContext - import sys - - if len(sys.argv) < 2: - sys.stderr.write("Usage: python -m gerber <filename> <filename>...\n") - sys.exit(1) - - ctx = GerberCairoContext() - ctx.alpha = 0.95 - for filename in sys.argv[1:]: - print("parsing %s" % filename) - if 'GTO' in filename or 'GBO' in filename: - ctx.color = (1, 1, 1) - ctx.alpha = 0.8 - elif 'GTS' in filename or 'GBS' in filename: - ctx.color = (0.2, 0.2, 0.75) - ctx.alpha = 0.8 - gerberfile = read(filename) - gerberfile.render(ctx) - - print('Saving image to test.svg') - ctx.dump('test.svg') + main() + diff --git a/gerber/layers.py b/gerber/layers.py index 5c26412..69e1c0d 100644 --- a/gerber/layers.py +++ b/gerber/layers.py @@ -40,10 +40,12 @@ hints = [ content=[]
),
Hint(layer='internal',
- ext=['in', 'gt1', 'gt2', 'gt3', 'gt4', 'gt5', 'gt6', 'g1',
- 'g2', 'g3', 'g4', 'g5', 'g6', ],
- name=['art', 'internal', 'pgp', 'pwr', 'gp1', 'gp2', 'gp3', 'gp4',
- 'gt5', 'gp6', 'gnd', 'ground', 'In1.Cu', 'In2.Cu', 'In3.Cu', 'In4.Cu'],
+ ext=['in', 'gt1', 'gt2', 'gt3', 'gt4', 'gt5', 'gt6',
+ 'g1', 'g2', 'g3', 'g4', 'g5', 'g6', ],
+ name=['art', 'internal', 'pgp', 'pwr', 'gnd', 'ground',
+ 'gp1', 'gp2', 'gp3', 'gp4', 'gt5', 'gp6',
+ 'In1.Cu', 'In2.Cu', 'In3.Cu', 'In4.Cu',
+ 'group3', 'group4', 'group5', 'group6', 'group7', 'group8', ],
regex='',
content=[]
),
@@ -54,21 +56,22 @@ hints = [ content=[]
),
Hint(layer='bottomsilk',
- ext=['gbo', 'ssb', 'pls', 'bs', 'skb', 'bottomsilk',],
- name=['bsilk', 'ssb', 'botsilk', 'B.SilkS'],
+ ext=['gbo', 'ssb', 'pls', 'bs', 'skb', 'bottomsilk', ],
+ name=['bsilk', 'ssb', 'botsilk', 'bottomsilk', 'B.SilkS'],
regex='',
content=[]
),
Hint(layer='topmask',
ext=['gts', 'stc', 'tmk', 'smt', 'tr', 'topmask', ],
name=['sm01', 'cmask', 'tmask', 'mask1', 'maskcom', 'topmask',
- 'mst', 'F.Mask',],
+ 'mst', 'F.Mask', ],
regex='',
content=[]
),
Hint(layer='bottommask',
ext=['gbs', 'sts', 'bmk', 'smb', 'br', 'bottommask', ],
- name=['sm', 'bmask', 'mask2', 'masksold', 'botmask', 'msb', 'B.Mask',],
+ name=['sm', 'bmask', 'mask2', 'masksold', 'botmask', 'bottommask',
+ 'msb', 'B.Mask', ],
regex='',
content=[]
),
@@ -80,13 +83,13 @@ hints = [ ),
Hint(layer='bottompaste',
ext=['gbp', 'bm', 'bottompaste', ],
- name=['sp02', 'botpaste', 'psb', 'B.Paste', ],
+ name=['sp02', 'botpaste', 'bottompaste', 'psb', 'B.Paste', ],
regex='',
content=[]
),
Hint(layer='outline',
ext=['gko', 'outline', ],
- name=['BDR', 'border', 'out', 'Edge.Cuts', ],
+ name=['BDR', 'border', 'out', 'outline', 'Edge.Cuts', ],
regex='',
content=[]
),
@@ -98,13 +101,21 @@ hints = [ ),
Hint(layer='drawing',
ext=['fab'],
- name=['assembly drawing', 'assembly', 'fabrication', 'fab drawing'],
+ name=['assembly drawing', 'assembly', 'fabrication',
+ 'fab drawing', 'fab'],
regex='',
content=[]
),
]
+def layer_signatures(layer_class):
+ for hint in hints:
+ if hint.layer == layer_class:
+ return hint.ext + hint.name
+ return []
+
+
def load_layer(filename):
return PCBLayer.from_cam(common.read(filename))
diff --git a/gerber/pcb.py b/gerber/pcb.py index 56deaa3..1d22e74 100644 --- a/gerber/pcb.py +++ b/gerber/pcb.py @@ -18,7 +18,7 @@ import os from .exceptions import ParseError -from .layers import PCBLayer, sort_layers +from .layers import PCBLayer, sort_layers, layer_signatures from .common import read as gerber_read from .utils import listdir @@ -41,7 +41,14 @@ class PCB(object): camfile = gerber_read(os.path.join(directory, filename)) layer = PCBLayer.from_cam(camfile) layers.append(layer) - names.add(os.path.splitext(filename)[0]) + name = os.path.splitext(filename)[0] + if len(os.path.splitext(filename)) > 1: + _name, ext = os.path.splitext(name) + if ext[1:] in layer_signatures(layer.layer_class): + name = _name + if layer.layer_class == 'drill' and 'drill' in ext: + name = _name + names.add(name) if verbose: print('[PCB]: Added {} layer <{}>'.format(layer.layer_class, filename)) diff --git a/gerber/render/__init__.py b/gerber/render/__init__.py index fe08d50..c7dbdd5 100644 --- a/gerber/render/__init__.py +++ b/gerber/render/__init__.py @@ -24,3 +24,8 @@ SVG is the only supported format. """ from .render import RenderSettings +from .cairo_backend import GerberCairoContext + +available_renderers = { + 'cairo': GerberCairoContext, +} @@ -16,6 +16,7 @@ # limitations under the License. import os + def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read() @@ -25,13 +26,13 @@ METADATA = { 'version': 0.1, 'author': 'Paulo Henrique Silva <ph.silva@gmail.com>, Hamilton Kibbe <ham@hamiltonkib.be>', 'author_email': "ph.silva@gmail.com, ham@hamiltonkib.be", - 'description': ("Utilities to handle Gerber (RS-274X) files."), + 'description': "Utilities to handle Gerber (RS-274X) files.", 'license': "Apache", 'keywords': "pcb gerber tools", 'url': "http://github.com/curtacircuitos/pcb-tools", 'packages': ['gerber', 'gerber.render'], 'long_description': read('README.md'), - 'classifiers':[ + 'classifiers': [ "Development Status :: 3 - Alpha", "Topic :: Utilities", "License :: OSI Approved :: Apple Public Source License", @@ -40,6 +41,11 @@ METADATA = { SETUPTOOLS_METADATA = { 'install_requires': ['cairocffi==0.6'], + 'entry_points': { + 'console_scripts': [ + 'gerber-render = gerber.__main__:main', + ], + }, } @@ -53,9 +59,10 @@ def install(): except ImportError: from sys import stderr stderr.write('Could not import setuptools, using distutils') - stderr.write('NOTE: You will need to install dependencies manualy') + stderr.write('NOTE: You will need to install dependencies manually') from distutils.core import setup setup(**METADATA) + if __name__ == '__main__': install() |