Add postscript and other stuff

This commit is contained in:
John Beard 2018-06-02 15:51:24 +01:00
parent 1519ff2e08
commit 5aada8a3cd
4 changed files with 497 additions and 108 deletions

View File

@ -9,19 +9,23 @@ outputs:
type: gerber
dir: gerberdir
options:
subtract_mask_from_silk: true
use_protel_extensions: false
line_width: 0.15
# generic layer options
exclude_edge_layer: false
exclude_pads_from_silkscreen: false
use_aux_axis_as_origin: false
plot_sheet_reference: true
plot_sheet_reference: false
plot_footprint_refs: true
plot_footprint_values: true
force_plot_invisible_refs_vals: false
tent_vias: true
check_zone_fills: true
# gerber options
line_width: 0.15
subtract_mask_from_silk: true
use_protel_extensions: false
gerber_precision: 4.5
create_gerber_job_file: true
layers:
- layer: F.Cu
suffix: F_Cu
@ -43,4 +47,37 @@ outputs:
report:
filename: 'drill_report.rpt'
map:
type: 'pdf'
type: 'pdf'
- name: postscript
comment: "Postscript files"
type: ps
dir: gerberdir
options:
exclude_edge_layer: false
exclude_pads_from_silkscreen: false
use_aux_axis_as_origin: false
plot_sheet_reference: false
plot_footprint_refs: true
plot_footprint_values: true
force_plot_invisible_refs_vals: false
tent_vias: true
check_zone_fills: true
# PS options
line_width: 0.15
drill_marks: full
scaling: 2
sketch_plot: true
scale_adjust_x: 1.0
scale_adjust_y: 1.0
width_adjust: 0
mirror_plot: false
negative_plot: false
layers:
- layer: F.Cu
suffix: F_Cu
- layer: F.SilkS
suffix: F_SilkS
# - layer: Inner.1
# suffix: Inner_1

View File

@ -88,111 +88,219 @@ class CfgYamlReader(CfgReader):
Map a config dict onto a target object given a mapping list
"""
for map_type in mapping_list:
for mapping in mapping_list:
# if this output type matches the mapping specification:
if otype in map_type['types']:
if otype in mapping['types']:
# for each mapping:
for key, mapping in map_type['options'].items():
key = mapping['key']
# set the internal option as needed
if mapping['required'](cfg_options):
# set the internal option as needed
if mapping['required'](cfg_options):
cfg_val = self._get_required(cfg_options, key)
elif key in cfg_options:
# not required but given anyway
cfg_val = cfg_options[key]
else:
continue
cfg_val = self._get_required(cfg_options, key)
elif key in cfg_options:
# not required but given anyway
cfg_val = cfg_options[key]
else:
continue
# transform the value if needed
if 'transform' in mapping:
cfg_val = mapping['transform'](cfg_val)
# transform the value if needed
if 'transform' in mapping:
cfg_val = mapping['transform'](cfg_val)
setattr(target, mapping['to'], cfg_val)
setattr(target, mapping['to'], cfg_val)
def _parse_out_opts(self, otype, options):
# note - type IDs are strings form the _config_, not the internal
# strings used as enums (in plot_config)
ANY_LAYER = ['gerber', 'ps', 'svg', 'hpgl', 'pdf', 'dxf']
ANY_DRILL = ['excellon', 'gerb_drill']
# mappings from YAML keys to type_option keys
MAPPINGS = [
{
# Options for a general layer type
'key': 'use_aux_axis_as_origin',
'types': ['gerber', 'dxf'],
'to': 'use_aux_axis_as_origin',
'required': lambda opts: True,
},
{
'key': 'exclude_edge_layer',
'types': ANY_LAYER,
'to': 'exclude_edge_layer',
'required': lambda opts: True,
},
{
'key': 'exclude_pads_from_silkscreen',
'types': ANY_LAYER,
'to': 'exclude_pads_from_silkscreen',
'required': lambda opts: True,
},
{
'key': 'plot_sheet_reference',
'types': ANY_LAYER,
'to': 'plot_sheet_reference',
'required': lambda opts: True,
},
{
'key': 'plot_footprint_refs',
'types': ANY_LAYER,
'to': 'plot_footprint_refs',
'required': lambda opts: True,
},
{
'key': 'plot_footprint_values',
'types': ANY_LAYER,
'to': 'plot_footprint_values',
'required': lambda opts: True,
},
{
'key': 'force_plot_invisible_refs_vals',
'types': ANY_LAYER,
'to': 'force_plot_invisible_refs_vals',
'required': lambda opts: True,
},
{
'key': 'tent_vias',
'types': ANY_LAYER,
'to': 'tent_vias',
'required': lambda opts: True,
},
{
'key': 'check_zone_fills',
'types': ANY_LAYER,
'to': 'check_zone_fills',
'required': lambda opts: True,
},
{
'key': 'line_width',
'types': ['gerber', 'ps', 'svg', 'pdf'],
'to': 'line_width',
'required': lambda opts: True,
},
{
'key': 'subtract_mask_from_silk',
'types': ['gerber'],
'options': {
'exclude_edge_layer': {
'to': 'exclude_edge_layer',
'required': lambda opts: True,
},
'exclude_pads_from_silkscreen': {
'to': 'exclude_pads_from_silkscreen',
'required': lambda opts: True,
},
'use_aux_axis_as_origin': {
'to': 'use_aux_axis_as_origin',
'required': lambda opts: True,
},
'line_width': {
'to': 'line_width',
'required': lambda opts: True,
},
},
'to': 'subtract_mask_from_silk',
'required': lambda opts: True,
},
{
# Gerber only
'key': 'mirror_plot',
'types': ['ps', 'svg', 'hpgl', 'pdf'],
'to': 'mirror_plot',
'required': lambda opts: True,
},
{
'key': 'negative_plot',
'types': ['ps', 'svg', 'pdf'],
'to': 'negative_plot',
'required': lambda opts: True,
},
{
'key': 'sketch_plot',
'types': ['ps', 'hpgl'],
'to': 'sketch_plot',
'required': lambda opts: True,
},
{
'key': 'scaling',
'types': ['ps', 'hpgl'],
'to': 'scaling',
'required': lambda opts: True,
},
{
'key': 'drill_marks',
'types': ['ps', 'svg', 'dxf', 'hpgl', 'pdf'],
'to': 'drill_marks',
'required': lambda opts: True,
},
{
'key': 'use_protel_extensions',
'types': ['gerber'],
'options': {
'subtract_mask_from_silk': {
'to': 'subtract_mask_from_silk',
'required': lambda opts: True,
},
'use_protel_extensions': {
'to': 'use_protel_extensions',
'required': lambda opts: True,
},
},
'to': 'use_protel_extensions',
'required': lambda opts: True,
},
{
# Drill files
'types': ['excellon'],
'options': {
'use_aux_axis_as_origin': {
'to': 'use_aux_axis_as_origin',
'required': lambda opts: True,
},
'map': {
'to': 'map_options',
'required': lambda opts: False,
'transform': self._parse_drill_map
},
'report': {
'to': 'report_options',
'required': lambda opts: False,
'transform': self._parse_drill_report
},
},
'key': 'gerber_precision',
'types': ['gerber'],
'to': 'gerber_precision',
'required': lambda opts: True,
},
{
# Excellon drill files
'key': 'create_gerber_job_file',
'types': ['gerber'],
'to': 'create_gerber_job_file',
'required': lambda opts: True,
},
{
'key': 'scale_adjust_x',
'types': ['ps'],
'to': 'scale_adjust_x',
'required': lambda opts: True,
},
{
'key': 'scale_adjust_y',
'types': ['ps'],
'to': 'scale_adjust_y',
'required': lambda opts: True,
},
{
'key': 'width_adjust',
'types': ['ps'],
'to': 'width_adjust',
'required': lambda opts: True,
},
{
'key': 'pen_width',
'types': ['hpgl'],
'to': 'pen_width',
'required': lambda opts: True,
},
{
'key': 'use_aux_axis_as_origin',
'types': ANY_DRILL,
'to': 'use_aux_axis_as_origin',
'required': lambda opts: True,
},
{
'key': 'map',
'types': ANY_DRILL,
'to': 'map_options',
'required': lambda opts: False,
'transform': self._parse_drill_map
},
{
'key': 'report',
'types': ANY_DRILL,
'to': 'report_options',
'required': lambda opts: False,
'transform': self._parse_drill_report
},
{
'key': 'metric_units',
'types': ['excellon'],
'options': {
'metric_units': {
'to': 'metric_units',
'required': lambda opts: True,
},
'pth_and_npth_single_file': {
'to': 'pth_and_npth_single_file',
'required': lambda opts: True,
},
'minimal_header': {
'to': 'minimal_header',
'required': lambda opts: True,
},
'mirror_y_axis': {
'to': 'mirror_y_axis',
'required': lambda opts: True,
},
},
'to': 'metric_units',
'required': lambda opts: True,
},
{
'key': 'pth_and_npth_single_file',
'types': ['excellon'],
'to': 'pth_and_npth_single_file',
'required': lambda opts: True,
},
{
'key': 'minimal_header',
'types': ['excellon'],
'to': 'minimal_header',
'required': lambda opts: True,
},
{
'key': 'mirror_y_axis',
'types': ['excellon'],
'to': 'mirror_y_axis',
'required': lambda opts: True,
},
]
@ -279,7 +387,7 @@ class CfgYamlReader(CfgReader):
except KeyError:
raise YamlError("Output needs a type")
if otype not in ['gerber', 'excellon']:
if otype not in ['gerber', 'ps', 'excellon']:
raise YamlError("Unknown output type: {}".format(otype))
try:

View File

@ -56,27 +56,41 @@ class Plotter(object):
def _output_is_layer(self, output):
return output.options.type in [PCfg.OutputOptions.GERBER]
return output.options.type in [
PCfg.OutputOptions.GERBER,
PCfg.OutputOptions.POSTSCRIPT
]
def _output_is_drill(self, output):
return output.options.type in [PCfg.OutputOptions.EXCELLON]
return output.options.type in [
PCfg.OutputOptions.EXCELLON,
]
def _get_layer_plot_format(self, output):
"""
Gets the Pcbnew plot format for a given KiPlot output type
"""
if output.options.type == PCfg.OutputOptions.GERBER:
return pcbnew.PLOT_FORMAT_GERBER
mapping = {
PCfg.OutputOptions.GERBER: pcbnew.PLOT_FORMAT_GERBER,
PCfg.OutputOptions.POSTSCRIPT: pcbnew.PLOT_FORMAT_POST
}
try:
return mapping[output.options.type]
except KeyError:
pass
raise ValueError("Don't know how to translate plot type: {}"
.format(output.options.type))
def _do_layer_plot(self, board, plot_ctrl, output):
# set up plot options for the whole output
self._configure_plot_ctrl(plot_ctrl, output)
po = plot_ctrl.GetPlotOptions()
layer_cnt = board.GetCopperLayerCount()
# plot every layer in the output
@ -97,9 +111,18 @@ class Plotter(object):
# Set current layer
plot_ctrl.SetLayer(layer.layer)
# Plot single layer to file
# Skipping NPTH is controlled by whether or not this is
# a copper layer
is_cu = pcbnew.IsCopperLayer(layer.layer)
po.SetSkipPlotNPTH_Pads(is_cu)
plot_format = self._get_layer_plot_format(output)
# Plot single layer to file
logging.debug("Opening plot file for layer {} ({})"
.format(layer.layer, suffix))
plot_ctrl.OpenPlotfile(suffix, plot_format, desc)
logging.debug("Plotting layer {} to {}".format(
layer.layer, plot_ctrl.GetPlotFileName()))
plot_ctrl.PlotLayer()
@ -160,6 +183,24 @@ class Plotter(object):
po.SetSubtractMaskFromSilk(gerb_opts.subtract_mask_from_silk)
po.SetUseGerberProtelExtensions(gerb_opts.use_protel_extensions)
po.SetGerberPrecision(gerb_opts.gerber_precision)
po.SetCreateGerberJobFile(gerb_opts.create_gerber_job_file)
def _configure_hpgl_opts(self, po, output):
assert(output.options.type == PCfg.OutputOptions.HPGL)
hpgl_opts = output.options.type_options
po.SetHPGLPenDiameter(hpgl_opts.pen_width)
def _configure_ps_opts(self, po, output):
assert(output.options.type == PCfg.OutputOptions.POSTSCRIPT)
ps_opts = output.options.type_options
po.SetWidthAdjust(ps_opts.width_adjust)
po.SetFineScaleAdjustX(ps_opts.scale_adjust_x)
po.SetFineScaleAdjustX(ps_opts.scale_adjust_y)
def _configure_output_dir(self, plot_ctrl, output):
@ -180,27 +221,34 @@ class Plotter(object):
opts = output.options.type_options
# Set some important plot options:
po.SetPlotFrameRef(False)
# Line width for items without one defined
po.SetLineWidth(opts.line_width)
po.SetAutoScale(False) # do not change it
po.SetScale(1) # do not change it
po.SetMirror(False)
po.SetAutoScale(opts.auto_scale)
po.SetScale(opts.scaling)
po.SetMirror(opts.mirror_plot)
po.SetNegative(opts.negative_plot)
po.SetPlotFrameRef(opts.plot_sheet_reference)
po.SetPlotReference(opts.plot_footprint_refs)
po.SetPlotValue(opts.plot_footprint_values)
po.SetPlotInvisibleText(opts.force_plot_invisible_refs_vals)
po.SetExcludeEdgeLayer(opts.exclude_edge_layer)
po.SetPlotPadsOnSilkLayer(not opts.exclude_pads_from_silkscreen)
po.SetUseAuxOrigin(opts.use_aux_axis_as_origin)
po.SetPlotViaOnMaskLayer(not opts.tent_vias)
# in general, false, but gerber will set it back later
po.SetUseGerberAttributes(False)
if output.options.type == PCfg.OutputOptions.GERBER:
self._configure_gerber_opts(po, output)
elif output.options.type == PCfg.OutputOptions.POSTSCRIPT:
self._configure_ps_opts(po, output)
# Disable plot pad holes
po.SetDrillMarksType(pcbnew.PCB_PLOT_PARAMS.NO_DRILL_SHAPE)
# Skip plot pad NPTH when possible: when drill size and shape == pad size
# and shape
# usually sel to True for copper layers
po.SetDrillMarksType(opts.drill_marks)
# We'll come back to this on a per-layer basis
po.SetSkipPlotNPTH_Pads(False)

View File

@ -1,15 +1,46 @@
import pcbnew
from . import error
class KiPlotConfigurationError(error.KiPlotError):
pass
class LayerOptions(object):
"""
Common options that all layer outputs have
"""
AUTO_SCALE = 0
def __init__(self):
self._line_width = None
self.exclude_edge_layer = False
self.exclude_pads_from_silkscreen = False
self.plot_sheet_reference = False
self._supports_line_width = False
self._line_width = None
self._supports_aux_axis_origin = False
self._use_aux_axis_as_origin = False
# override for scalable formats
self._supports_scaling = False
self._auto_scale = False
self._scaling = 1
self._supports_mirror = False
self._mirror_plot = False
self._supports_negative = False
self._negative_plot = False
self._supports_drill_marks = False
self._drill_marks = pcbnew.PCB_PLOT_PARAMS.NO_DRILL_SHAPE
@property
def line_width(self):
@ -20,7 +51,102 @@ class LayerOptions(object):
"""
Set the line width, in mm
"""
self._line_width = pcbnew.FromMM(value)
if self._supports_line_width:
self._line_width = pcbnew.FromMM(value)
print("Set LW %d" % self._line_width)
else:
raise KiPlotConfigurationError(
"This output doesn't support setting line width")
@property
def auto_scale(self):
return self._auto_scale
@property
def scaling(self):
return self._scaling
@scaling.setter
def scaling(self, val):
"""
Set scaling, if possible. AUTO_SCALE to set auto scaling
"""
if self._supports_scaling:
if val == self.AUTO_SCALE:
self._scaling = 1
self._auto_scale = True
else:
self._scaling = val
self._auto_scale = False
else:
raise KiPlotConfigurationError(
"This Layer output does not support scaling")
@property
def mirror_plot(self):
return self._mirror_plot
@mirror_plot.setter
def mirror_plot(self, val):
if self._supports_mirror:
self._mirror_plot = val
else:
raise KiPlotConfigurationError(
"This Layer output does not support mirror plotting")
@property
def negative_plot(self):
return self._mirror_plot
@negative_plot.setter
def negative_plot(self, val):
if self._supports_mirror:
self._mirror_plot = val
else:
raise KiPlotConfigurationError(
"This Layer output does not support negative plotting")
@property
def drill_marks(self):
return self._drill_marks
@drill_marks.setter
def drill_marks(self, val):
if self._supports_drill_marks:
try:
drill_mark = {
'none': pcbnew.PCB_PLOT_PARAMS.NO_DRILL_SHAPE,
'small': pcbnew.PCB_PLOT_PARAMS.SMALL_DRILL_SHAPE,
'full': pcbnew.PCB_PLOT_PARAMS.FULL_DRILL_SHAPE,
}[val]
except KeyError:
raise KiPlotConfigurationError(
"Unknown drill mark type: {}".format(val))
self._drill_marks = drill_mark
else:
raise KiPlotConfigurationError(
"This Layer output does not support drill marks")
@property
def use_aux_axis_as_origin(self):
return self._use_aux_axis_as_origin
@use_aux_axis_as_origin.setter
def use_aux_axis_as_origin(self, val):
if self._supports_aux_axis_origin:
self._use_aux_axis_as_origin = val
else:
raise KiPlotConfigurationError(
"This Layer output does not support using the auxiliary"
" axis as the origin")
class GerberOptions(LayerOptions):
@ -29,8 +155,75 @@ class GerberOptions(LayerOptions):
super(GerberOptions, self).__init__()
self._supports_line_width = True
self._supports_aux_axis_origin = True
self.subtract_mask_from_silk = False
self.use_protel_extensions = False
self.create_gerber_job_file = False
# either 5 or 6
self._gerber_precision = None
@property
def gerber_precision(self):
return self._gerber_precision
@gerber_precision.setter
def gerber_precision(self, val):
"""
Set gerber precision: either 4.5 or 4.6
"""
if val == 4.5:
self._gerber_precision = 5
elif val == 4.6:
self._gerber_precision = 6
else:
raise KiPlotConfigurationError(
"Bad Gerber precision : {}".format(val))
class HpglOptions(LayerOptions):
def __init__(self):
super(HpglOptions, self).__init__()
self._pen_width = None
@property
def pen_width(self):
return self._pen_width
@pen_width.setter
def pen_width(self, pw_mm):
self._pen_width = pcbnew.FromMM(pw_mm)
class PsOptions(LayerOptions):
def __init__(self):
super(PsOptions, self).__init__()
self._supports_mirror = True
self._supports_negative = True
self._supports_scaling = True
self._supports_drill_marks = True
self._supports_line_width = True
self.scale_adjust_x = 1.0
self.scale_adjust_y = 1.0
self._width_adjust = 0
@property
def width_adjust(self):
return self._width_adjust
@width_adjust.setter
def width_adjust(self, width_adjust_mm):
self._width_adjust = pcbnew.FromMM(width_adjust_mm)
class DrillOptions(object):
@ -74,6 +267,7 @@ class DrillMapOptions(object):
class OutputOptions(object):
GERBER = 'gerber'
POSTSCRIPT = 'ps'
EXCELLON = 'excellon'
def __init__(self, otype):
@ -81,6 +275,8 @@ class OutputOptions(object):
if otype == self.GERBER:
self.type_options = GerberOptions()
elif otype == self.POSTSCRIPT:
self.type_options = PsOptions()
elif otype == self.EXCELLON:
self.type_options = ExcellonOptions()
else: