#! /usr/bin/env python

import inkex
import os
import subprocess
import tempfile
import shutil
import copy
import platform
import simplepath
import simpletransform
from simplestyle import *
import cubicsuperpath
import cspsubdiv
import webbrowser
import hashlib
import xml.etree.ElementTree as ET
import pickle
from copy import deepcopy


EXPORT_PNG_MAX_PROCESSES = 3
EXPORT_KICAD_MAX_PROCESSES = 2

PCB_HEADER = '''
(kicad_pcb (version 4) (host pcbnew 4.0.7)

	(general
		(links 0)
		(no_connects 0)
		(area 77.052499 41.877835 92.193313 53.630501)
		(thickness 1.6)
		(drawings 8)
		(tracks 0)
		(zones 0)
		(modules 1)
		(nets 1)
	)

	(page A4)
	(layers
		(0 F.Cu signal)
		(31 B.Cu signal)
		(32 B.Adhes user)
		(33 F.Adhes user)
		(34 B.Paste user)
		(35 F.Paste user)
		(36 B.SilkS user)
		(37 F.SilkS user)
		(38 B.Mask user)
		(39 F.Mask user)
		(40 Dwgs.User user)
		(41 Cmts.User user)
		(42 Eco1.User user)
		(43 Eco2.User user)
		(44 Edge.Cuts user)
		(45 Margin user)
		(46 B.CrtYd user)
		(47 F.CrtYd user)
		(48 B.Fab user)
		(49 F.Fab user)
	)

	(setup
		(last_trace_width 0.25)
		(trace_clearance 0.2)
		(zone_clearance 0.508)
		(zone_45_only no)
		(trace_min 0.2)
		(segment_width 0.2)
		(edge_width 0.15)
		(via_size 0.6)
		(via_drill 0.4)
		(via_min_size 0.4)
		(via_min_drill 0.3)
		(uvia_size 0.3)
		(uvia_drill 0.1)
		(uvias_allowed no)
		(uvia_min_size 0.2)
		(uvia_min_drill 0.1)
		(pcb_text_width 0.3)
		(pcb_text_size 1.5 1.5)
		(mod_edge_width 0.15)
		(mod_text_size 1 1)
		(mod_text_width 0.15)
		(pad_size 1.524 1.524)
		(pad_drill 0.762)
		(pad_to_mask_clearance 0.2)
		(aux_axis_origin 0 0)
		(visible_elements FFFFFF7F)
		(pcbplotparams
			(layerselection 0x010f0_80000001)
			(usegerberextensions false)
			(excludeedgelayer true)
			(linewidth 0.100000)
			(plotframeref false)
			(viasonmask false)
			(mode 1)
			(useauxorigin false)
			(hpglpennumber 1)
			(hpglpenspeed 20)
			(hpglpendiameter 15)
			(hpglpenoverlay 2)
			(psnegative false)
			(psa4output false)
			(plotreference true)
			(plotvalue true)
			(plotinvisibletext false)
			(padsonsilk false)
			(subtractmaskfromsilk false)
			(outputformat 1)
			(mirror false)
			(drillshape 1)
			(scaleselection 1)
			(outputdirectory gerbers/))
	)

	(net 0 "")

	(net_class Default "This is the default net class."
		(clearance 0.2)
		(trace_width 0.25)
		(via_dia 0.6)
		(via_drill 0.4)
		(uvia_dia 0.3)
		(uvia_drill 0.1)
	)
'''

PCB_FOOTER = '''
)
'''

PCB_LIB_TABLE = '''
(fp_lib_table
  (lib (name "{name}")(type KiCad)(uri "$(KIPRJMOD)/{folder}")(options "")(descr ""))
)
'''

PCB_PROJECT_FILE = '''
update=2018 March 15, Thursday 14:41:19
version=1
last_client=kicad
[pcbnew]
version=1
LastNetListRead=
UseCmpFile=1
PadDrill=0.600000000000
PadDrillOvalY=0.600000000000
PadSizeH=1.500000000000
PadSizeV=1.500000000000
PcbTextSizeV=1.500000000000
PcbTextSizeH=1.500000000000
PcbTextThickness=0.300000000000
ModuleTextSizeV=1.000000000000
ModuleTextSizeH=1.000000000000
ModuleTextSizeThickness=0.150000000000
SolderMaskClearance=0.000000000000
SolderMaskMinWidth=0.000000000000
DrawSegmentWidth=0.200000000000
BoardOutlineThickness=0.100000000000
ModuleOutlineThickness=0.150000000000
[cvpcb]
version=1
NetIExt=net
[general]
version=1
[eeschema]
version=1
LibDir=
[eeschema/libraries]
LibName1=power
LibName2=device
LibName3=transistors
LibName4=conn
LibName5=linear
LibName6=regul
LibName7=74xx
LibName8=cmos4000
LibName9=adc-dac
LibName10=memory
LibName11=xilinx
LibName12=microcontrollers
LibName13=dsp
LibName14=microchip
LibName15=analog_switches
LibName16=motorola
LibName17=texas
LibName18=intel
LibName19=audio
LibName20=interface
LibName21=digital-audio
LibName22=philips
LibName23=display
LibName24=cypress
LibName25=siliconi
LibName26=opto
LibName27=atmel
LibName28=contrib
LibName29=valves
'''

MODULE_INVIS_REF_HEADER = '''
(fp_text reference Ref** (at 0 0) (layer F.SilkS) hide
(effects (font (size 1.27 1.27) (thickness 0.15)))
)
(fp_text value Val** (at 0 0) (layer F.SilkS) hide
(effects (font (size 1.27 1.27) (thickness 0.15)))
)
'''

IDENTITY_MATRIX = [[1.0,0.0,0.0],[0.0,1.0,0.0]]


LIBRARY_TABLE_FILE = "fp-lib-table"
EXPORT_IMAGE_FOLDER = "images"
EXPORT_CACHE_FOLDER = ".svg2shenzhen-cache"


class Svg2ShenzhenExport(inkex.Effect):
    def __init__(self):
        """init the effect library and get options from gui"""
        inkex.Effect.__init__(self)
        self.OptionParser.add_option("--path", action="store", type="string", dest="path", default="~/", help="")
        self.OptionParser.add_option('-f', '--filetype', action='store', type='string', dest='filetype', default='jpeg', help='Exported file type')
        self.OptionParser.add_option("--crop", action="store", type="inkbool", dest="crop", default=False)
        self.OptionParser.add_option("--dpi", action="store", type="float", dest="dpi", default=600)
        self.OptionParser.add_option("--threshold", action="store", type="float", dest="threshold", default=128.0)
        self.OptionParser.add_option("--openfactory", action="store", type="inkbool", dest="openfactory", default="true")
        self.OptionParser.add_option("--openkicad", action="store", type="inkbool", dest="openkicad", default="true")
        self.OptionParser.add_option("--autoflatten", action="store", type="inkbool", dest="autoflatten", default="true")
        self.OptionParser.add_option("--debug", action="store", type="inkbool", dest="debug", default=False)


        self.doc_width = 0
        self.doc_height = 0

        self.bb_width_center = 0
        self.bb_height_center = 0
        self.bb_scaling_w = 0
        self.bb_scaling_h = 0

        self.layer_map = {
            #'inkscape-name' : 'kicad-name',
            'F.Cu'      : 'F.Cu',
            'B.Cu'      : 'B.Cu',
            'B.Adhes'   : 'B.Adhes',
            'F.Adhes'   : 'F.Adhes',
            'B.Paste'   : 'B.Paste',
            'F.Paste'   : 'F.Paste',
            'B.SilkS'   : 'B.SilkS',
            'F.SilkS'   : 'F.SilkS',
            'B.Mask'    : 'B.Mask',
            'F.Mask'    : 'F.Mask',
            'Dwgs.User' : 'Dwgs.User',
            'Cmts.User' : 'Cmts.User',
            'Eco1.User' : 'Eco1.User',
            'Eco2.User' : 'Eco2.User',
            'Margin'    : 'Margin',
            'B.CrtYd'   : 'B.CrtYd',
            'F.CrtYd'   : 'F.CrtYd',
            'B.Fab'     : 'B.Fab',
            'F.Fab'     : 'F.Fab',
            # The following layers are here for backward compatibility:
            'B.Silk'    : 'B.SilkS',
            'F.Silk'    : 'F.SilkS',
            # 'Edge.Cuts' : "Edge.Cuts"
        }


    def coordToKicad(self,XYCoord):
        return [
            (XYCoord[0]-self.bb_width_center)/self.bb_scaling_w,
            (XYCoord[1]-self.bb_height_center)/self.bb_scaling_h,
        ]

    def setInkscapeScaling(self):

        root = self.document.getroot()
        height = float(root.get('height').replace("mm", ""))
        width = float(root.get('width').replace("mm", ""))

        self.doc_width = width
        self.doc_height = height

        viewbox = root.attrib['viewBox'].split(' ')
        viewbox_h = float(viewbox[-1])
        viewbox_w = float(viewbox[-2])

        self.bb_width_center = viewbox_w/2
        self.bb_height_center = viewbox_h/2
        self.bb_scaling_w = viewbox_w/width
        self.bb_scaling_h = viewbox_h/height

    def setDocumentSquare(self):
        root = self.document.getroot()
        height = float(root.attrib['height'].replace("mm", ""))
        width =  float(root.attrib['width'].replace("mm", ""))

        if (width > height):
            root.attrib['height'] = str(width) + "mm"
            root.attrib['viewBox'] = "0 0 %f %f" % (width, width)
        else:
            root.attrib['width'] = str(height) + "mm"
            root.attrib['viewBox'] = "0 0 %f %f" % (height, height)

    def findLayer(self, layerName, contains=False):
        svg_layers = self.document.xpath('//svg:g[@inkscape:groupmode="layer"]', namespaces=inkex.NSS)
        for layer in svg_layers:
            label_attrib_name = "{%s}label" % layer.nsmap['inkscape']
            if label_attrib_name not in layer.attrib:
                continue
            if ((layer.attrib[label_attrib_name] == layerName) and (contains == False)):
                return layer
            elif ((layerName in layer.attrib[label_attrib_name]) and (contains == True)):
                return layer

        return False

    def effect(self):
        # self.setDocumentSquare()
        self.setInkscapeScaling()
        self.processAutoMasks()
        self.processExportLayer()
        if (self.options.openfactory):
            webbrowser.open("https://www.pcbway.com/setinvite.aspx?inviteid=54747", new = 2)

    def processAutoMasks(self):
        self.processAutoMaskFromTo("F.Cu", "F.Mask-auto")
        self.processAutoMaskFromTo("B.Cu", "B.Mask-auto")

    def processAutoMaskFromTo(self, from_layer, to_layer):
        copper_layer = self.findLayer(from_layer, False)
        cpmask_layer = self.findLayer(to_layer, True)
        # copper_layer = cpmask_layer
        if (copper_layer != False and cpmask_layer != False):
            for node in cpmask_layer.xpath("*", namespaces=inkex.NSS):
                cpmask_layer.remove(node)
            for node in copper_layer.xpath("*", namespaces=inkex.NSS):
                cpmask_layer.append(deepcopy(node))


    def processExportLayer(self):
        options = self.options

        output_path = os.path.expanduser(options.path)
        curfile = self.args[-1]
        layers = self.get_layers(curfile)
        name = self.get_name()
        kicad_pcb_file = "{}.kicad_pcb".format(name)
        library_folder = "{}.pretty".format(name)
        kicad_project_file = "{}.pro".format(name)
        kicad_mod_file = "{}.kicad_mod".format(name)
        kicad_mod_files = []

        cache_folder_path = os.path.join(output_path, EXPORT_CACHE_FOLDER)

        if options.filetype == "png":
            image_folder_path = output_path
        else:
            image_folder_path = os.path.join(cache_folder_path, EXPORT_IMAGE_FOLDER)

        if options.filetype == "kicad_pcb":
            library_folder_path = os.path.join(output_path, library_folder)
        else:
            library_folder_path = os.path.join(cache_folder_path, library_folder)

        if not os.path.exists(output_path):
            os.makedirs(output_path)
        if not os.path.exists(library_folder_path):
            os.makedirs(library_folder_path)
        if not os.path.exists(image_folder_path):
            os.makedirs(image_folder_path)
        if not os.path.exists(cache_folder_path):
            os.makedirs(cache_folder_path)

        kicad_pcb_path = os.path.join(output_path, kicad_pcb_file )
        kicad_lib_path = os.path.join(output_path, LIBRARY_TABLE_FILE)
        kicad_pro_path = os.path.join(output_path, kicad_project_file )
        kicad_mod_path = os.path.join(output_path, kicad_mod_file)

        options_path = os.path.join(cache_folder_path, 'options.pickle')

        if os.path.exists(options_path):
            with open(options_path, 'r') as f:
                prev_options = pickle.load(f)
            dpi_equal = prev_options.dpi == options.dpi
            path_equal = prev_options.path == options.path
            crop_equal = prev_options.crop == options.crop
            filetype_equal = prev_options.filetype == options.filetype
            threshold_equal = prev_options.threshold == options.threshold
            ignore_hashes = not dpi_equal or not path_equal or not crop_equal or not filetype_equal or not threshold_equal
        else:
            ignore_hashes = True

        with open(options_path, 'w') as f:
            pickle.dump(options, f)

        layer_arguments = []
        temp_svg_paths = []
        for (layer_id, layer_label, layer_type) in layers:
            if layer_type == "fixed":
                continue
            show_layer_ids = [layer[0] for layer in layers if layer[2] == "fixed" or layer[0] == layer_id]
            invert = "true"

            if ("-auto" in layer_label):
                layer_label = layer_label.replace("-auto", "")

            if ("-invert" in layer_label):
                layer_label = layer_label.replace("-invert", "")
                invert = "false"
            hash_sum_path = os.path.join(cache_folder_path, '{}-{}-{}-{}.hash'.format(layer_id, layer_label, layer_type, invert))

            prev_hash_sum = None
            if os.path.exists(hash_sum_path):
              with open(hash_sum_path, 'r') as f:
                  prev_hash_sum = f.read()

            # generate unique filename each layer
            temp_name = next(tempfile._get_candidate_names())
            layer_dest_svg_path = os.path.join(cache_folder_path, temp_name)
            hash_sum = self.export_layers(layer_dest_svg_path, show_layer_ids)
            temp_svg_paths.append(layer_dest_svg_path)

            layer_dest_png_path = os.path.join(image_folder_path,  "%s_%s.png" % (layer_label, layer_id))
            layer_dest_kicad_path = os.path.join(library_folder_path, "%s_%s.kicad_mod" % (layer_label, layer_id))
            kicad_mod_files.append(layer_dest_kicad_path)


            if ignore_hashes or hash_sum != prev_hash_sum or not os.path.exists(layer_dest_kicad_path):
                with open(hash_sum_path, 'w') as f:
                    f.write(hash_sum)
                layer_arguments.append((layer_dest_svg_path, layer_dest_png_path, layer_dest_kicad_path, layer_label, invert))


        for i in range(0, len(layer_arguments), EXPORT_PNG_MAX_PROCESSES):
            processes = []
            for layer_dest_svg_path, layer_dest_png_path, _, _, _ in layer_arguments[i:i+EXPORT_PNG_MAX_PROCESSES]:
                #export layer to png
                p = self.exportToPng(layer_dest_svg_path, layer_dest_png_path)
                processes.append(p)
            for p in processes:
                p.wait()

        for layer_dest_svg_path in temp_svg_paths:
            os.remove(layer_dest_svg_path)

        if options.filetype == "kicad_pcb" or options.filetype == "kicad_module":
            for i in range(0, len(layer_arguments), EXPORT_KICAD_MAX_PROCESSES):
                processes = []
                for _, layer_dest_png_path, layer_dest_kicad_path, layer_label, invert in layer_arguments[i:i+EXPORT_KICAD_MAX_PROCESSES]:
                    #export layer png to kicad
                    p = self.exportToKicad(layer_dest_png_path, layer_dest_kicad_path, layer_label, invert)
                    processes.append(p)
                for p in processes:
                    p.wait()
        else:
            return

        kicad_edgecut_string = self.exportEdgeCut(kicad_mod = options.filetype == "kicad_module")
        kicad_drill_string = self.exportDrill(kicad_mod = options.filetype == "kicad_module")

        if options.filetype == "kicad_pcb":
            kicad_modules_string = ""
            for kicad_file in kicad_mod_files:
                with open(kicad_file, 'r') as f:
                    kicad_modules_string += f.read()

            with open(kicad_pcb_path, 'w') as f:
                f.write(PCB_HEADER)
                f.write(kicad_modules_string)
                f.write(kicad_edgecut_string)
                f.write(kicad_drill_string)
                f.write(PCB_FOOTER)

            with open(kicad_lib_path, 'w') as f:
                f.write(PCB_LIB_TABLE.format(name=name, folder=library_folder))

            with open(kicad_pro_path, 'w') as f:
                f.write(PCB_PROJECT_FILE)

            if (options.openkicad):
                self.openKicad(kicad_pcb_path)

        elif options.filetype == "kicad_module":
            kicad_modules_string = '(module "{}" (layer F.Cu)'.format(name)
            kicad_modules_string += MODULE_INVIS_REF_HEADER
            for kicad_file in kicad_mod_files:
                with open(kicad_file, 'r') as f:
                    mod = f.readlines()[8:-1]
                    kicad_modules_string += "".join(mod)
            kicad_modules_string += kicad_edgecut_string
            kicad_modules_string += kicad_drill_string
            kicad_modules_string += ")"
            with open(kicad_mod_path, 'w') as f:
                f.write(kicad_modules_string)


    def export_layers(self, dest, show):
        """
        Export selected layers of SVG to the file `dest`.
        :arg  str   dest:  path to export SVG file.
        :arg  list  hide:  layers to hide. each element is a string.
        :arg  list  show:  layers to show. each element is a string.
        """
        doc = copy.deepcopy(self.document)
        root = doc.getroot()
        for layer in doc.xpath('//svg:g[@inkscape:groupmode="layer"]', namespaces=inkex.NSS):
            id = layer.attrib["id"]
            if id in show:
                layer.attrib['style'] = 'display:inline'
            else:
                root.remove(layer)

        # remove the namedview for the hash as it changes based on user zoom/scroll
        namedview = doc.find('sodipodi:namedview', namespaces=inkex.NSS)
        root.remove(namedview)

        doc.write(dest)

        # returns a hash of the exported layer contents which can be used to
        # detect changes
        return hashlib.md5(ET.tostring(root)).hexdigest()

    def get_name(self):
        root = self.document.getroot()
        docname = root.get('{http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd}docname')
        if docname is None:
            return 'drawing'
        return os.path.splitext(docname)[0]

    def get_layers(self, src):
        svg_layers = self.document.xpath('//svg:g[@inkscape:groupmode="layer"]', namespaces=inkex.NSS)
        layers = []

        for layer in svg_layers:
            label_attrib_name = "{%s}label" % layer.nsmap['inkscape']
            if label_attrib_name not in layer.attrib:
                continue

            layer_id = layer.attrib["id"]
            layer_label = layer.attrib[label_attrib_name]

            layer_label_name = layer_label.replace("-invert", "")
            layer_label_name = layer_label_name.replace("-auto", "")

            if  layer_label_name in self.layer_map.iterkeys():
                layer_type = "export"
                layer_label = layer_label
            elif layer_label.lower().startswith("[fixed] "):
                layer_type = "fixed"
                layer_label = layer_label[8:]
            else:
                continue

            layers.append([layer_id, layer_label, layer_type])

        return layers

    def openKicad(self, kicad_file_path):
        platform_system = platform.system()

        if (platform_system == 'Darwin'):
            command = "open %s" % (kicad_file_path)
        elif (platform_system == 'Linux'):
            command = "xdg-open %s" % (kicad_file_path)
        else:
            command = "start %s" % (kicad_file_path)

        return subprocess.Popen(command.encode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)



    def exportToKicad(self, png_path, output_path, layer_type, invert = "true"):
        plugin_path = os.path.dirname(os.path.abspath(__file__))

        platform_system = platform.system()

        if (platform_system == 'Darwin'):
            bitmap2component_exe = os.path.join(plugin_path, 'bitmap2component_osx')
        elif (platform_system == 'Linux'):
            bitmap2component_exe = os.path.join(plugin_path, 'bitmap2component_linux64')
        else:
            bitmap2component_exe = os.path.join(plugin_path, 'bitmap2component.exe')

        layer_name = self.layer_map[layer_type]
        command =  "\"%s\" \"%s\" \"%s\" %s %s %s %s" % (bitmap2component_exe, png_path, output_path, layer_name, invert , str(int(self.options.dpi)) , str(int(self.options.threshold)))
        if (self.options.debug):
            inkex.debug(command)
        return subprocess.Popen(command.encode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)


    def exportToPng(self, svg_path, output_path):
        area_param = '-D' if self.options.crop else 'C'
        command = "inkscape %s -d %s -e \"%s\" \"%s\"" % (area_param, self.options.dpi, output_path, svg_path)
        if (self.options.debug):
            inkex.debug(command)
        return subprocess.Popen(command.encode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)


    def exportEdgeCut(self, kicad_mod=False):
        x0 = 0
        y0 = 0
        mirror = 1.0

        line_type = "fp_line" if kicad_mod else "gr_line"

        kicad_edgecut_string = ""

        i = 0
        layerPath = '//svg:g[@inkscape:groupmode="layer"]'

        if (self.options.autoflatten):
            self.flatten_bezier()

        for layer in self.document.getroot().xpath(layerPath, namespaces=inkex.NSS):
            i += 1

            label_attrib_name = "{%s}label" % layer.nsmap['inkscape']
            if label_attrib_name not in layer.attrib:
                continue

            layer_name = (layer.attrib[label_attrib_name])

            if layer_name != "Edge.Cuts":
                continue

            layer_trans = layer.get('transform')
            if layer_trans:
                layer_m = simpletransform.parseTransform(layer_trans)
            else:
                layer_m = IDENTITY_MATRIX

            nodePath = ('//svg:g[@inkscape:groupmode="layer"][%d]/descendant::svg:path') % i
            for node in self.document.getroot().xpath(nodePath, namespaces=inkex.NSS):
                d = node.get('d')
                p = simplepath.parsePath(d)

                points = []
                if p:
                    #sanity check
                    if p[0][0] == 'M':
                        t = node.get('transform')
                        if t:
                            m = simpletransform.parseTransform(t)
                            trans = simpletransform.composeTransform(layer_m, m)
                        else:
                            trans = layer_m

                        for path in p:
                            if path[0] != "Z":
                                x = (path[1][0])
                                y = (path[1][1])
                                xy = [x,y]
                                simpletransform.applyTransformToPoint(trans,xy)
                                points.append(self.coordToKicad([(xy[0]-x0), xy[1]*mirror-y0]))

                        points_count = len(points)
                        points.append(points[0])

                        for x in range (0, points_count):
                            kicad_edgecut_string = kicad_edgecut_string + ("(%s (start %f %f) (end %f %f) (layer Edge.Cuts) (width 0.1))\n"  % (line_type, points[x][0],points[x][1],points[x+1][0],points[x+1][1]))

        return kicad_edgecut_string

    def exportDrill(self, kicad_mod=False):
        x0 = 0
        y0 = 0
        mirror = 1.0

        self.setInkscapeScaling()

        kicad_drill_string = ""

        i = 0

        if kicad_mod:
            pad_template = "(pad {n} thru_hole circle (at {x} {y}) (size {d} {d}) (drill {d}) (layers *.Cu *.Mask))\n"
        else:
            pad_template = """
                (module Wire_Pads:SolderWirePad_single_0-8mmDrill (layer F.Cu) (tedit 0) (tstamp 5ABD66D0)
                    (at {x} {y})
                    (pad {n} thru_hole circle (at 0 0) (size {d} {d}) (drill {d}) (layers *.Cu *.Mask))
                )
            """

        layerPath = '//svg:g[@inkscape:groupmode="layer"][@inkscape:label="Drill"]'

        for layer in self.document.getroot().xpath(layerPath, namespaces=inkex.NSS):

            layer_trans = layer.get('transform')
            if layer_trans:
                layer_m = simpletransform.parseTransform(layer_trans)
            else:
                layer_m = IDENTITY_MATRIX

            nodePath = 'descendant::svg:circle'

            count = 0
            for node in layer.xpath(nodePath, namespaces=inkex.NSS):
                count = count + 1
                cx = float(node.get('cx'))
                cy = float(node.get('cy'))

                radius = float(node.get('r'))
                drill_size = radius * 2

                t = node.get('transform')

                pt = [cx, cy]

                if t:
                    m = simpletransform.parseTransform(t)
                    trans = simpletransform.composeTransform(layer_m, m)
                else:
                    trans = layer_m

                simpletransform.applyTransformToPoint(trans,pt)
                padCoord = self.coordToKicad(pt)

                kicad_drill_string += pad_template.format(x=padCoord[0], y=padCoord[1], n=count, d=drill_size)

            return kicad_drill_string

    def flatten_bezier(self):
        layerPath = '//svg:g[@inkscape:groupmode="layer"][@inkscape:label="Edge.Cuts"]'
        for layer in self.document.getroot().xpath(layerPath, namespaces=inkex.NSS):
            nodePath = 'descendant::svg:path'
            for node in layer.xpath(nodePath, namespaces=inkex.NSS):
                if node.tag == inkex.addNS('path','svg'):
                    d = node.get('d')
                    p = cubicsuperpath.parsePath(d)
                    cspsubdiv.cspsubdiv(p, 0.01)
                    np = []
                    for sp in p:
                        first = True
                        for csp in sp:
                            cmd = 'L'
                            if first:
                                cmd = 'M'
                            first = False
                            np.append([cmd, [csp[1][0], csp[1][1]]])
                            node.set('d', simplepath.formatPath(np))

def _main():
    e = Svg2ShenzhenExport()
    e.affect()
    exit()

if __name__ == "__main__":
    _main()
