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 _placeDrillIndex(self): """ Adds a drill index """ # Get the drills sheet / SVG layer drill_layer = self._layers['drills']['layer'] ns = {'pcbmode':config.cfg['ns']['pcbmode'], 'svg':config.cfg['ns']['svg']} drills = drill_layer.findall(".//*[@pcbmode:diameter]", namespaces=ns) drills_dict = {} longest_text = 0 largest_drill = 0 drill_count = 0 for drill in drills: diameter = drill.get('{'+config.cfg['ns']['pcbmode']+'}diameter') diameter = round(float(diameter), 2) if diameter not in drills_dict: drills_dict[diameter] = 1 else: drills_dict[diameter] += 1 if diameter > largest_drill: largest_drill = diameter drill_count += 1 if len(str(diameter)) > longest_text: longest_text = len(str(diameter)) # Get location, or generate one try: location = config.brd['drill-index']['location'] except: # If not location is specified, put the drill index at the # bottom left of the board. The 'gap' defines the extra # spcae between the top of the largest drill and the # board's edge gap = 2 location = [-self._width/2, -(self._height/2+gap)] location = utils.toPoint(location) # Create group for placing index transform = "translate(%s,%s)" % (location.x, config.cfg['invert-y']*location.y) group = et.SubElement(drill_layer, 'g', transform=transform) group.set('{'+config.cfg['ns']['pcbmode']+'}type', 'drill-index') text_style_dict = config.stl['layout']['drill-index'].get('text') text_style = utils.dictToStyleText(text_style_dict) count_style_dict = config.stl['layout']['drill-index'].get('count-text') count_style = utils.dictToStyleText(count_style_dict) count_style_dict['font-size'] /= 2 drill_size_style = utils.dictToStyleText(count_style_dict) if drill_count == 0: text = 'No drills' elif drill_count == 1: text = '1 drill: ' else: text = '%s drills: ' % drill_count t = et.SubElement(group, 'text', x=str(0), y=str(0), style=text_style) t.text = text # "new line" location.y = -(largest_drill/2 + 1.5) # TODO: this hack'ish thing for aligning the text isn't going # to work when the font is changed in the stylesheet if float(longest_text*0.5) > largest_drill: location.x = longest_text*0.3 else: location.x = largest_drill/2 gap = 2 for diameter in reversed(sorted(drills_dict)): path = svg.drillPath(diameter) transform = "translate(%s,%s)" % (location.x, config.cfg['invert-y']*location.y) element = et.SubElement(group, 'path', d=path, transform=transform) element.set("fill-rule", "evenodd") t = et.SubElement(group, 'text', x=str(location.x), y=str(-location.y), dy="%s" % (config.cfg['invert-y']*0.25), style=count_style) t.text = str(drills_dict[diameter]) t = et.SubElement(group, 'text', x=str(location.x), y=str(-location.y), dy="%s" % (config.cfg['invert-y']*-0.5), style=drill_size_style) t.text = "%s mm" % diameter location.x += max(diameter, 2.5)
def _placeDrillIndex(self): """ Adds a drill index """ # Get the drills sheet / SVG layer drill_layer = self._layers['drills']['layer'] ns = { 'pcbmode': config.cfg['ns']['pcbmode'], 'svg': config.cfg['ns']['svg'] } drills = drill_layer.findall(".//*[@pcbmode:diameter]", namespaces=ns) drills_dict = {} largest_drill = 0 drill_count = 0 for drill in drills: diameter = drill.get('{' + config.cfg['ns']['pcbmode'] + '}diameter') diameter = round(float(diameter), 2) if diameter not in drills_dict: drills_dict[diameter] = 1 else: drills_dict[diameter] += 1 if diameter > largest_drill: largest_drill = diameter drill_count += 1 # Get location, or generate one try: location = config.brd['drill-index']['location'] except: # If not location is specified, put the drill index at the # bottom left of the board. The 'gap' defines the extra # spcae between the top of the largest drill and the # board's edge gap = 2 location = [-self._width / 2, -(self._height / 2 + gap)] location = utils.toPoint(location) # Create group for placing index transform = "translate(%s,%s)" % (location.x, config.cfg['invert-y'] * location.y) group = et.SubElement(drill_layer, 'g', transform=transform) group.set('{' + config.cfg['ns']['pcbmode'] + '}type', 'drill-index') text_style_dict = config.stl['layout']['drill-index'].get('text') text_style = utils.dict_to_style(text_style_dict) count_style_dict = config.stl['layout']['drill-index'].get( 'count-text') count_style = utils.dict_to_style(count_style_dict) count_style_dict['font-size'] /= 2 drill_size_style = utils.dict_to_style(count_style_dict) if drill_count == 0: text = 'No drills' elif drill_count == 1: text = '1 drill: ' else: text = '%s drills: ' % drill_count t = et.SubElement(group, 'text', x=str(0), y=str(0), style=text_style) t.text = text # "new line" location.y = -(largest_drill / 2 + 0.5) location.x = largest_drill / 2 gap = 2 for diameter in reversed(sorted(drills_dict)): path = svg.drillPath(diameter) transform = "translate(%s,%s)" % ( location.x, config.cfg['invert-y'] * location.y) element = et.SubElement(group, 'path', d=path, transform=transform) element.set("fill-rule", "evenodd") t = et.SubElement(group, 'text', x=str(location.x), y=str(-location.y), dy="%s" % (config.cfg['invert-y'] * 0.25), style=count_style) t.text = str(drills_dict[diameter]) t = et.SubElement(group, 'text', x=str(location.x), y=str(-location.y), dy="%s" % (config.cfg['invert-y'] * -0.5), style=drill_size_style) t.text = "%s mm" % diameter location.x += max(diameter, 2.5)
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]))