Example #1
0
    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]))
Example #2
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) 
Example #3
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 = {}
        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)
Example #4
0
    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]))