def __init__(self, shape): gerber_lp = None mirror = False # Invert rotation so it's clock-wise. Inkscape is counter-clockwise and # it's unclear to ma what's the "right" direction. clockwise makse more # sense to me. This should be the only place to make the change. self._inv_rotate = -1 self._rotate = shape.get('rotate') or 0 self._rotate *= self._inv_rotate self._rotate_point = shape.get('rotate-point') or Point(0,0) self._scale = shape.get('scale') or 1 self._pour_buffer = shape.get('buffer-to-pour') try: self._type = shape.get('type') except: msg.error("Shapes must have a 'type' defined") if self._type in ['rect', 'rectangle']: path = svg.width_and_height_to_path(shape['width'], shape['height'], shape.get('radii')) elif self._type in ['circ', 'circle', 'round']: path = svg.circle_diameter_to_path(shape['diameter']) elif self._type in ['drill']: self._diameter = shape['diameter'] path = svg.drillPath(self._diameter) elif self._type in ['text', 'string']: try: self._text = shape['value'] except KeyError: msg.error("Could not find the text to display. The text to be displayed should be defined in the 'value' field, for example, 'value': 'DEADBEEF\\nhar\\nhar'") # Get the fon'ts name font = shape.get('font-family') or config.stl['layout']['defaults']['font-family'] # Search for the font SVG in these paths paths = [os.path.join(config.cfg['base-dir']), os.path.join(os.path.dirname(os.path.realpath(__file__)), '..')] font_filename = "%s.svg" % font filenames = '' font_data = None for path in paths: filename = os.path.join(path, config.cfg['locations']['fonts'], font_filename) filenames += " %s \n" % filename if os.path.isfile(filename): font_data = et.ElementTree(file=filename) break if font_data == None: msg.error("Couldn't find style file %s. Looked for it here:\n%s" % (font_filename, filenames)) try: fs = shape['font-size'] except: msg.error("A 'font-size' attribute must be specified for a 'text' type") ls = shape.get('letter-spacing') or '0mm' lh = shape.get('line-height') or fs font_size, letter_spacing, line_height = utils.getTextParams(fs, ls, lh) # With the units-per-em we can figure out the scale factor # to use for the desired font size units_per_em = float(font_data.find("//n:font-face", namespaces={'n': config.cfg['namespace']['svg']}).get('units-per-em')) or 1000 self._scale = font_size/units_per_em # Get the path to use. This returns the path without # scaling, which will be applied later, in the same manner # as to the other shape types path, gerber_lp = utils.textToPath(font_data, self._text, letter_spacing, line_height, self._scale) # In the case where the text is an outline/stroke instead # of a fill we get rid of the gerber_lp if shape.get('style') == 'stroke': gerber_lp = None self._rotate += 180 elif self._type in ['path']: path = shape.get('value') else: msg.error("'%s' is not a recongnised shape type" % self._type) self._path = SvgPath(path, gerber_lp) self._path.transform(self._scale, self._rotate, self._rotate_point, True) self._gerber_lp = (shape.get('gerber-lp') or shape.get('gerber_lp') or gerber_lp or None) self._location = utils.toPoint(shape.get('location', [0, 0]))
def placeDrill(drill, layer, location, scale, soldermask_layers={}, mask_groups={}): """ Places the drilling point """ diameter = drill.get('diameter') offset = utils.to_Point(drill.get('offset') or [0, 0]) path = svg.drill_diameter_to_path(diameter) mask_path = svg.circle_diameter_to_path(diameter) sig_dig = config.cfg['significant-digits'] transform = 'translate(%s %s)' % (round( (location.x + offset.x) * scale, sig_dig), round((-location.y - offset.y) * scale, sig_dig)) drill_element = et.SubElement(layer, 'path', transform=transform, d=path, id='pad_drill', diameter=str(diameter)) pour_buffer = 1.0 try: pour_buffer = board_cfg['distances']['buffer_from_pour_to'].get( 'drill') or 1.0 except: pass # add a mask buffer between pour and board outline if mask_groups != {}: for pcb_layer in surface_layers: mask_group = et.SubElement(mask_groups[pcb_layer], 'g', id="drill_masks") pour_mask = et.SubElement(mask_group, 'path', transform=transform, style=MASK_STYLE % str(pour_buffer * 2), gerber_lp="c", d=mask_path) # place the size of the drill; id the drill element has a # "show_diameter": "no", then this can be suppressed # default to 'yes' show_diameter = drill.get('show_diameter') or 'yes' if show_diameter.lower() != 'no': text = "%s mm" % (str(diameter)) text_style = config.stl['layout']['drills'].get('text') or None if text_style is not None: text_style['font-size'] = str(diameter / 10.0) + 'px' text_style = utils.dict_to_style(text_style) t = et.SubElement( layer, 'text', x=str(location.x), # TODO: get rid of this hack y=str(-location.y - (diameter / 4)), style=text_style) t.text = text # place soldermask unless specified otherwise # default is 'yes' add_soldermask = drill.get('add_soldermask') or 'yes' style = utils.dict_to_style(config.stl['layout']['soldermask'].get('fill')) possible_answers = [ 'yes', 'top', 'top only', 'bottom', 'bottom only', 'top and bottom' ] if (add_soldermask.lower() in possible_answers) and (soldermask_layers != {}): # TODO: get this into a configuration parameter drill_soldermask_scale_factors = drill.get( 'soldermask_scale_factors') or { 'top': 1.2, 'bottom': 1.2 } path_top = svg.circle_diameter_to_path( diameter * drill_soldermask_scale_factors['top']) path_bottom = svg.circle_diameter_to_path( diameter * drill_soldermask_scale_factors['bottom']) if add_soldermask.lower() == 'yes' or add_soldermask.lower( ) == 'top and bottom': drill_element = et.SubElement(soldermask_layers['top'], 'path', transform=transform, style=style, d=path_top) drill_element = et.SubElement(soldermask_layers['bottom'], 'path', transform=transform, style=style, d=path_bottom) elif add_soldermask.lower() == 'top only' or add_soldermask.lower( ) == 'top': drill_element = et.SubElement(soldermask_layers['top'], 'path', transform=transform, style=style, d=path_top) elif add_soldermask.lower() == 'bottom only' or add_soldermask.lower( ) == 'bottom': drill_element = et.SubElement(soldermask_layers['bottom'], 'path', transform=transform, style=style, d=path_bottom) else: print "ERROR: unrecognised drills soldermask option" return
def __init__(self, shape): gerber_lp = None mirror = False self._shape_dict = shape # Invert rotation so it's clock-wise. Inkscape is counter-clockwise and # it's unclear to ma what's the "right" direction. clockwise makse more # sense to me. This should be the only place to make the change. self._inv_rotate = -1 try: self._type = shape.get('type') except: msg.error("Shapes must have a 'type' defined") # A 'layer' type is a copy of the outline. Here we copy the # outline shape and override the type if self._type in ['layer']: self._shape_dict = config.brd['outline'].get('shape').copy() self._type = self._shape_dict.get('type') self._place_mirrored = shape.get('mirror') or False self._rotate = shape.get('rotate') or 0 self._rotate *= self._inv_rotate self._rotate_point = shape.get('rotate-point') or Point(0, 0) self._scale = shape.get('scale') or 1 self._pour_buffer = shape.get('buffer-to-pour') # A general purpose label field; intended for use for pad # labels self._label = None if self._type in ['rect', 'rectangle']: path = svg.width_and_height_to_path(self._shape_dict['width'], self._shape_dict['height'], self._shape_dict.get('radii')) elif self._type in ['circ', 'circle', 'round']: path = svg.circle_diameter_to_path(self._shape_dict['diameter']) elif self._type in ['drill']: self._diameter = self._shape_dict['diameter'] path = svg.drillPath(self._diameter) elif self._type in ['text', 'string']: try: self._text = self._shape_dict['value'] except KeyError: msg.error( "Could not find the text to display. The text to be displayed should be defined in the 'value' field, for example, 'value': 'DEADBEEF\\nhar\\nhar'" ) # Get the font's name font = self._shape_dict.get('font-family') or config.stl['layout'][ 'defaults']['font-family'] # Search for the font SVG in these paths paths = [ os.path.join(config.cfg['base-dir']), os.path.join(os.path.dirname(os.path.realpath(__file__)), '..') ] font_filename = "%s.svg" % font filenames = '' font_data = None for path in paths: filename = os.path.join(path, config.cfg['locations']['fonts'], font_filename) filenames += " %s \n" % filename if os.path.isfile(filename): font_data = et.ElementTree(file=filename) break if font_data == None: msg.error( "Couldn't find style file %s. Looked for it here:\n%s" % (font_filename, filenames)) try: fs = self._shape_dict['font-size'] except: msg.error( "A 'font-size' attribute must be specified for a 'text' type" ) ls = self._shape_dict.get('letter-spacing') or '0mm' lh = self._shape_dict.get('line-height') or fs font_size, letter_spacing, line_height = utils.getTextParams( fs, ls, lh) # With the units-per-em we can figure out the scale factor # to use for the desired font size units_per_em = float( font_data.find("//n:font-face", namespaces={ 'n': config.cfg['namespace']['svg'] }).get('units-per-em')) or 1000 self._scale = font_size / units_per_em # Get the path to use. This returns the path without # scaling, which will be applied later, in the same manner # as to the other shape types path, gerber_lp = utils.textToPath(font_data, self._text, letter_spacing, line_height, self._scale) # In the case where the text is an outline/stroke instead # of a fill we get rid of the gerber_lp if self._shape_dict.get('style') == 'stroke': gerber_lp = None self._rotate += 180 elif self._type in ['path']: path = self._shape_dict.get('value') else: msg.error("'%s' is not a recongnised shape type" % self._type) self._path = SvgPath(path, gerber_lp) self._path.transform(scale=self._scale, rotate_angle=self._rotate, rotate_point=self._rotate_point, mirror=self._place_mirrored) self._gerber_lp = (shape.get('gerber-lp') or shape.get('gerber_lp') or gerber_lp or None) self._location = utils.toPoint(shape.get('location', [0, 0]))
def placeDrill(drill, layer, location, scale, soldermask_layers={}, mask_groups={}): """ Places the drilling point """ diameter = drill.get('diameter') offset = utils.to_Point(drill.get('offset') or [0, 0]) path = svg.drill_diameter_to_path(diameter) mask_path = svg.circle_diameter_to_path(diameter) sig_dig = config.cfg['significant-digits'] #translate = str(round((location.x + offset.x)*scale, sig_dig))+' '+str(round((-location.y - offset.y)*scale, sig_dig)) transform = 'translate(%s %s)' % (round((location.x + offset.x)*scale, sig_dig), round((-location.y - offset.y)*scale, sig_dig)) drill_element = et.SubElement(layer, 'path', transform=transform, d=path, id='pad_drill', diameter=str(diameter)) pour_buffer = 1.0 try: pour_buffer = board_cfg['distances']['buffer_from_pour_to'].get('drill') or 1.0 except: pass # add a mask buffer between pour and board outline if mask_groups != {}: for pcb_layer in surface_layers: mask_group = et.SubElement(mask_groups[pcb_layer], 'g', id="drill_masks") pour_mask = et.SubElement(mask_group, 'path', transform=transform, style=MASK_STYLE % str(pour_buffer*2), gerber_lp="c", d=mask_path) # place the size of the drill; id the drill element has a # "show_diameter": "no", then this can be suppressed # default to 'yes' show_diameter = drill.get('show_diameter') or 'yes' if show_diameter.lower() != 'no': text = "%s mm" % (str(diameter)) text_style = config.stl['layout']['drills'].get('text') or None if text_style is not None: text_style['font-size'] = str(diameter/10.0)+'px' text_style = utils.dict_to_style(text_style) t = et.SubElement(layer, 'text', x=str(location.x), # TODO: get rid of this hack y=str(-location.y-(diameter/4)), style=text_style) t.text = text # place soldermask unless specified otherwise # default is 'yes' add_soldermask = drill.get('add_soldermask') or 'yes' style = utils.dict_to_style(config.stl['layout']['soldermask'].get('fill')) possible_answers = ['yes', 'top', 'top only', 'bottom', 'bottom only', 'top and bottom'] if (add_soldermask.lower() in possible_answers) and (soldermask_layers != {}): # TODO: get this into a configuration parameter drill_soldermask_scale_factors = drill.get('soldermask_scale_factors') or {'top':1.2, 'bottom':1.2} path_top = svg.circle_diameter_to_path(diameter * drill_soldermask_scale_factors['top']) path_bottom = svg.circle_diameter_to_path(diameter * drill_soldermask_scale_factors['bottom']) if add_soldermask.lower() == 'yes' or add_soldermask.lower() == 'top and bottom': drill_element = et.SubElement(soldermask_layers['top'], 'path', transform=transform, style=style, d=path_top) drill_element = et.SubElement(soldermask_layers['bottom'], 'path', transform=transform, style=style, d=path_bottom) elif add_soldermask.lower() == 'top only' or add_soldermask.lower() == 'top': drill_element = et.SubElement(soldermask_layers['top'], 'path', transform=transform, style=style, d=path_top) elif add_soldermask.lower() == 'bottom only' or add_soldermask.lower() == 'bottom': drill_element = et.SubElement(soldermask_layers['bottom'], 'path', transform=transform, style=style, d=path_bottom) else: print "ERROR: unrecognised drills soldermask option" return