#!/usr/local/bin/python3.12
'''
Copyright (C) 2017-2021 Scorch www.scorchworks.com
Derived from dxf_outlines.py by Aaron Spike and Alvin Penner

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
'''

# standard library
import math
import tempfile, os, sys, shutil

import zipfile
import re
# local library
import inkex
import simplestyle
import simpletransform
import cubicsuperpath
import cspsubdiv
import traceback

from PIL import Image
Image.MAX_IMAGE_PIXELS = None

from lxml import etree

try:
    inkex.localize()
except:
    pass

#### Subprocess timout stuff ######
from subprocess import Popen, PIPE
from threading import Timer
def run_external(cmd, timeout_sec):
    stdout=None
    stderr=None
    FLAG=[True]
    try:
        proc = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE, stdin=PIPE, startupinfo=None)
    except Exception as e:
        raise Exception("\n%s\n\nExecutable Path:\n%s" %(e,cmd[0]))
    if timeout_sec > 0:
        kill_proc = lambda p: kill_sub_process(p,timeout_sec, FLAG)

        timer = Timer(timeout_sec, kill_proc, [proc])
        try:
            timer.start()
            stdout,stderr = proc.communicate()
        finally:
            timer.cancel()
        if not FLAG[0]:
            raise Exception("\nInkscape sub-process terminated after %d seconds." %(timeout_sec))
        return (stdout,stderr)

def kill_sub_process(p,timeout_sec, FLAG):
    FLAG[0]=False
    p.kill()

##################################
class SVG_TEXT_EXCEPTION(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

class SVG_ENCODING_EXCEPTION(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

class SVG_PXPI_EXCEPTION(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

class CSS_value_class():
    def __init__(self,type_name,value):
        type_name_list = type_name.split('.')
        try:
            self.object_type = type_name_list[0]
        except:
            self.object_type = ""

        try:
            self.value_name  = type_name_list[1]
        except:
            self.value_name  = ""
        self.data_string = value


class CSS_values_class():
    def __init__(self):
        self.CSS_value_list = []

    def add(self,type_name,value):
        self.CSS_value_list.append(CSS_value_class(type_name,value))

    def get_css_value(self,tag_type,class_val):
        value = ""
        for entry in self.CSS_value_list:
            if entry.object_type == "":
                if entry.value_name  == class_val:
                    value = value + entry.data_string
            if entry.object_type == tag_type:
                if entry.value_name  == class_val:
                    value = entry.data_string
                    break
        return value


class SVG_READER(inkex.Effect):
    def __init__(self):
        inkex.Effect.__init__(self)
        self.flatness = 0.01
        self.image_dpi = 1000
        self.inkscape_exe_list = []
        self.inkscape_exe_list.append("C:\\Program Files\\Inkscape\\bin\\inkscape.exe")
        self.inkscape_exe_list.append("C:\\Program Files (x86)\\Inkscape\\bin\\inkscape.exe")
        self.inkscape_exe_list.append("C:\\Program Files\\Inkscape\\inkscape.exe")
        self.inkscape_exe_list.append("C:\\Program Files (x86)\\Inkscape\\inkscape.exe")
        self.inkscape_exe_list.append("/usr/bin/inkscape")
        self.inkscape_exe_list.append("/usr/local/bin/inkscape")
        self.inkscape_exe_list.append("/Applications/Inkscape.app/Contents/Resources/bin/inkscape")
        self.inkscape_exe_list.append("/Applications/Inkscape.app/Contents/MacOS/Inkscape")
        self.inkscape_exe = None
        self.lines =[]
        self.Cut_Type = {}
        self.Xsize=40
        self.Ysize=40
        self.raster   = True
        self.SVG_dpi = None

        self.SVG_inkscape_version = None
        self.SVG_size_mm = None
        self.SVG_size_px = None
        self.SVG_ViewBox = None

        self.raster_PIL = None
        self.cut_lines = []
        self.eng_lines = []
        self.id_cnt = 0

        self.png_area = "--export-area-page"
        self.timout = 180 #timeout time for external calls to Inkscape in seconds

        self.layers = ['0']
        self.layer = '0'
        self.layernames = []
        self.txt2paths = False
        self.CSS_values = CSS_values_class()

    def parse_svg(self,filename):
        try:
            self.parse(filename)
            #self.parse(filename, encoding='utf-8')
        except Exception as e:
            exception_msg = "%s" %(e)
            if exception_msg.find("encoding"):
                self.parse(filename,encoding="ISO-8859-1")
            else:
                raise Exception(e)

    def set_inkscape_path(self,PATH):
        if PATH!=None:
            self.inkscape_exe_list.insert(0,PATH)
        for location in self.inkscape_exe_list:
            if ( os.path.isfile( location ) ):
                self.inkscape_exe=location
                break

    def colmod(self,r,g,b,path_id):
        changed=False
        k40_action = 'raster'
        delta = 10
        # Check if the color is Red (or close to it)
        if (r >= 255-delta) and (g <= delta) and (b <= delta):
            k40_action = "cut"
            self.Cut_Type[path_id]=k40_action
            (r,g,b) = (255,255,255)
            changed=True
        # Check if the color is Blue (or close to it)
        elif (r <= delta) and (g <= delta) and (b >= 255-delta):
            k40_action = "engrave"
            self.Cut_Type[path_id]=k40_action
            (r,g,b) = (255,255,255)
            changed=True
        else:
            k40_action = "raster"
            self.Cut_Type[path_id]=k40_action
            changed=False
        color_out = '#%02x%02x%02x' %(r,g,b)
        return (color_out, changed, k40_action)


    def process_shape(self, node, mat, group_stroke = None):
        #################################
        ### Determine the shape type  ###
        #################################
        try:
            i = node.tag.find('}')
            if i >= 0:
                tag_type = node.tag[i+1:]
        except:
            tag_type=""

        ##############################################
        ### Set a unique identifier for each shape ###
        ##############################################
        self.id_cnt=self.id_cnt+1
        path_id = "ID%d"%(self.id_cnt)
        sw_flag = False
        changed = False
        #######################################
        ### Handle references to CSS data   ###
        #######################################
        class_val = node.get('class')
        if class_val:
            css_data = ""
            for cv in class_val.split(' '):
                if css_data!="":
                    css_data = self.CSS_values.get_css_value(tag_type,cv)+";"+css_data
                else:
                    css_data = self.CSS_values.get_css_value(tag_type,cv)

            # Remove the reference to the CSS data
            del node.attrib['class']

            # Check if a style entry already exists. If it does
            # append the the existing style data to the CSS data
            # otherwise create a new style entry.
            if node.get('style'):
                if css_data!="":
                    css_data = css_data + ";" + node.get('style')
                    node.set('style', css_data)
            else:
                node.set('style', css_data)

        style   = node.get('style')
        self.Cut_Type[path_id]="raster" # Set default type to raster

        text_message_warning = "SVG File with Color Coded Text Outlines Found: (i.e. Blue: engrave/ Red: cut)"
        line1 = "SVG File with color coded text outlines found (i.e. Blue: engrave/ Red: cut)."
        line2 = "Automatic conversion to paths failed: Try upgrading to Inkscape .90 or later"
        line3 = "To convert manually in Inkscape: select the text then select \"Path\"-\"Object to Path\" in the menu bar."
        text_message_fatal  = "%s\n\n%s\n\n%s" %(line1,line2,line3)

        ##############################################
        ### Handle 'style' data outside of style   ###
        ##############################################
        stroke_outside = node.get('stroke')
        if not stroke_outside:
            stroke_outside = group_stroke
        if stroke_outside:
            stroke_width_outside = node.get('stroke-width')

            col = stroke_outside
            col= col.strip()
            if simplestyle.isColor(col):
                c=simplestyle.parseColor(col)
                (new_val,changed,k40_action)=self.colmod(c[0],c[1],c[2],path_id)
            else:
                new_val = col
            if changed:
                node.set('stroke',new_val)
                node.set('stroke-width',"0.0")
                node.set('k40_action', k40_action)
                sw_flag = True

            if sw_flag == True:
                if node.tag == inkex.addNS('text','svg') or node.tag == inkex.addNS('flowRoot','svg'):
                    if (self.txt2paths==False):
                        raise SVG_TEXT_EXCEPTION(text_message_warning)
                    else:
                        raise Exception(text_message_fatal)

        ###################################################
        ### Handle 'k40_action' data outside of style   ###
        ###################################################
        if node.get('k40_action'):
            k40_action = node.get('k40_action')
            changed=True
            self.Cut_Type[path_id]=k40_action

        ##############################################
        ### Handle 'style' data                    ###
        ##############################################
        if style:
            declarations = style.split(';')
            i_sw = -1

            sw_prop = 'stroke-width'
            for i,decl in enumerate(declarations):
                parts = decl.split(':', 2)
                if len(parts) == 2:
                    (prop, col) = parts
                    prop = prop.strip().lower()

                    if prop == 'display' and col == "none":
                        # display is 'none' return without processing group
                        return

                    if prop == 'k40_action':
                        changed = True
                        self.Cut_Type[path_id]=col

                    #if prop in color_props:
                    if prop == sw_prop:
                        i_sw = i
                    if prop == 'stroke':
                        col= col.strip()
                        if simplestyle.isColor(col):
                            c=simplestyle.parseColor(col)
                            (new_val,changed,k40_action)=self.colmod(c[0],c[1],c[2],path_id)
                        else:
                            new_val = col
                        if changed:
                            declarations[i] = prop + ':' + new_val
                            declarations.append('k40_action' + ':' + k40_action)
                            sw_flag = True
            if sw_flag == True:
                if node.tag == inkex.addNS('text','svg') or node.tag == inkex.addNS('flowRoot','svg'):
                    if (self.txt2paths==False):
                        raise SVG_TEXT_EXCEPTION(text_message_warning)
                    else:
                        # Check if there is actually any text
                        text_length = 0
                        text_length = text_length + len(self.strip_text(node.tail))
                        text_length = text_length + len(self.strip_text(node.text))
                        for child in node:
                            text_length = text_length + len(self.strip_text(child.tail))
                            text_length = text_length + len(self.strip_text(child.text))
                        #print("text_length",text_length)
                        if (text_length!=0):
                            raise Exception(text_message_fatal)

                if i_sw != -1:
                    declarations[i_sw] = sw_prop + ':' + "0.0"
                else:
                    declarations.append(sw_prop + ':' + "0.0")
            node.set('style', ';'.join(declarations))
        ##############################################

        #####################################################
        ### If vector data was found save the path data   ###
        #####################################################
        if changed:
            if node.get('display')=='none':
                return
            if node.tag == inkex.addNS('path','svg'):
                d = node.get('d')
                if not d:
                    return
                p = cubicsuperpath.parsePath(d)
            elif node.tag == inkex.addNS('rect','svg'):
                x = 0.0
                y = 0.0
                if node.get('x'):
                    x=float(node.get('x'))
                if node.get('y'):
                    y=float(node.get('y'))

                width = float(node.get('width'))
                height = float(node.get('height'))
                rx = 0.0
                ry = 0.0
                if node.get('rx'):
                    rx=float(node.get('rx'))
                if node.get('ry'):
                    ry=float(node.get('ry'))

                if max(rx,ry) > 0.0:
                    if rx==0.0 or ry==0.0:
                        rx = max(rx,ry)
                        ry = rx
                    Rxmax = abs(width)/2.0
                    Rymax = abs(height)/2.0
                    rx = min(rx,Rxmax)
                    ry = min(ry,Rymax)
                    L1 = "M %f,%f %f,%f "      %(x+rx       , y          , x+width-rx , y          )
                    C1 = "A %f,%f 0 0 1 %f,%f" %(rx         , ry         , x+width    , y+ry       )
                    L2 = "M %f,%f %f,%f "      %(x+width    , y+ry       , x+width    , y+height-ry)
                    C2 = "A %f,%f 0 0 1 %f,%f" %(rx         , ry         , x+width-rx , y+height   )
                    L3 = "M %f,%f %f,%f "      %(x+width-rx , y+height   , x+rx       , y+height   )
                    C3 = "A %f,%f 0 0 1 %f,%f" %(rx         , ry         , x          , y+height-ry)
                    L4 = "M %f,%f %f,%f "      %(x          , y+height-ry, x          , y+ry       )
                    C4 = "A %f,%f 0 0 1 %f,%f" %(rx         , ry         , x+rx       , y          )
                    d =  L1 + C1 + L2 + C2 + L3 + C3 + L4 + C4
                else:
                    d = "M %f,%f %f,%f %f,%f %f,%f Z" %(x,y, x+width,y,  x+width,y+height, x,y+height)
                p = cubicsuperpath.parsePath(d)

            elif node.tag == inkex.addNS('circle','svg'):
                cx = 0.0
                cy = 0.0
                if node.get('cx'):
                    cx=float(node.get('cx'))
                if node.get('cy'):
                    cy=float(node.get('cy'))
                if node.get('r'):
                    r  = float(node.get('r'))
                    d  = "M %f,%f A   %f,%f 0 0 1 %f,%f   %f,%f 0 0 1 %f,%f   %f,%f 0 0 1 %f,%f   %f,%f 0 0 1 %f,%f Z" %(cx+r,cy, r,r,cx,cy+r,  r,r,cx-r,cy,  r,r,cx,cy-r, r,r,cx+r,cy)
                else: #if there is no radius assume it is a path
                    if node.get('d'):
                        d = node.get('d')
                        p = cubicsuperpath.parsePath(d)
                    else:
                        raise Exception("Radius of SVG circle is not defined.")
                p = cubicsuperpath.parsePath(d)

            elif node.tag == inkex.addNS('ellipse','svg'):
                cx = 0.0
                cy = 0.0
                if node.get('cx'):
                    cx=float(node.get('cx'))
                if node.get('cy'):
                    cy=float(node.get('cy'))
                if node.get('r'):
                    r = float(node.get('r'))
                    rx = r
                    ry = r
                if node.get('rx'):
                    rx = float(node.get('rx'))
                if node.get('ry'):
                    ry = float(node.get('ry'))

                d  = "M %f,%f A   %f,%f 0 0 1 %f,%f   %f,%f 0 0 1 %f,%f   %f,%f 0 0 1 %f,%f   %f,%f 0 0 1 %f,%f Z" %(cx+rx,cy, rx,ry,cx,cy+ry,  rx,ry,cx-rx,cy,  rx,ry,cx,cy-ry, rx,ry,cx+rx,cy)
                p = cubicsuperpath.parsePath(d)

            elif (node.tag == inkex.addNS('polygon','svg')) or (node.tag == inkex.addNS('polyline','svg')):
                points = node.get('points')
                if not points:
                    return

                points = points.replace(',', ' ')
                while points.find('  ') > -1:
                    points = points.replace('  ', ' ')

                points = points.strip().split(" ")
                d = "M "
                for i in range(0,len(points),2):
                    x = float(points[i])
                    y = float(points[i+1])
                    d = d + "%f,%f " %(x,y)

                #Close the loop if it is a ploygon
                if node.tag == inkex.addNS('polygon','svg'):
                    d = d + "Z"
                p = cubicsuperpath.parsePath(d)

            elif node.tag == inkex.addNS('line','svg'):
                x1 = float(node.get('x1'))
                y1 = float(node.get('y1'))
                x2 = float(node.get('x2'))
                y2 = float(node.get('y2'))
                d = "M "
                d = "M %f,%f %f,%f" %(x1,y1,x2,y2)
                p = cubicsuperpath.parsePath(d)
            else:
                #print("something was ignored")
                #print(node.tag)
                return

            trans = node.get('transform')
            if trans:
                mat = simpletransform.composeTransform(mat, simpletransform.parseTransform(trans))
            simpletransform.applyTransformToPath(mat, p)

            ##########################################
            ## Break Curves down into small lines  ###
            ##########################################
            f = self.flatness
            is_flat = 0
            while is_flat < 1:
                try:
                    cspsubdiv.cspsubdiv(p, f)
                    is_flat = 1
                except IndexError:
                    break
                except:
                    f += 0.1
                    if f>2 :
                      break
                      #something has gone very wrong.
            ##########################################
            rgb=(0,0,0)
            for sub in p:
                for i in range(len(sub)-1):
                    x1 = sub[i][1][0]
                    y1 = sub[i][1][1]
                    x2 = sub[i+1][1][0]
                    y2 = sub[i+1][1][1]
                    self.lines.append([x1,y1,x2,y2,rgb,path_id])
        #####################################################
        ### End of saving the vector path data            ###
        #####################################################


    def process_clone(self, node):
        trans = node.get('transform')
        x = node.get('x')
        y = node.get('y')
        mat = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
        if trans:
            mat = simpletransform.composeTransform(mat, simpletransform.parseTransform(trans))
        if x:
            mat = simpletransform.composeTransform(mat, [[1.0, 0.0, float(x)], [0.0, 1.0, 0.0]])
        if y:
            mat = simpletransform.composeTransform(mat, [[1.0, 0.0, 0.0], [0.0, 1.0, float(y)]])
        # push transform
        if trans or x or y:
            self.groupmat.append(simpletransform.composeTransform(self.groupmat[-1], mat))
        # get referenced node
        refid = node.get(inkex.addNS('href','xlink'))
        #print(refid,node.get('id'),node.get('layer'))
        refnode = self.getElementById(refid[1:])
        if refnode is not None:
            if refnode.tag == inkex.addNS('g','svg') or refnode.tag == inkex.addNS('switch','svg'):
                self.process_group(refnode)
            elif refnode.tag == inkex.addNS('use', 'svg'):
                #print(refnode,'1')
                self.process_clone(refnode)
            else:
                self.process_shape(refnode, self.groupmat[-1])
        # pop transform
        if trans or x or y:
            self.groupmat.pop()

    def process_group(self, group):
        ##############################################
        ### Get color set at group level
        stroke_group = group.get('stroke')
        if group.get('display')=='none':
            return
        ##############################################
        ### Handle 'style' data
        style = group.get('style')
        if style:
            declarations = style.split(';')
            for i,decl in enumerate(declarations):
                parts = decl.split(':', 2)
                if len(parts) == 2:
                    (prop, val) = parts
                    prop = prop.strip().lower()
                    if prop == 'stroke':
                        stroke_group = val.strip()
                    if prop == 'display' and val == "none":
                        #group display is 'none' return without processing group
                        return
        ##############################################
        if group.get(inkex.addNS('groupmode', 'inkscape')) == 'layer':
            style = group.get('style')
            if style:
                style = simplestyle.parseStyle(style)
                if 'display' in style:
                    if style['display'] == 'none':
                        #layer display is 'none' return without processing layer
                        return
            layer = group.get(inkex.addNS('label', 'inkscape'))

            layer = layer.replace(' ', '_')
            if layer in self.layers:
                self.layer = layer
        trans = group.get('transform')
        if trans:
            self.groupmat.append(simpletransform.composeTransform(self.groupmat[-1], simpletransform.parseTransform(trans)))
        for node in group:
            if node.tag == inkex.addNS('g','svg') or  node.tag == inkex.addNS('switch','svg'):
                self.process_group(node)
            elif node.tag == inkex.addNS('use', 'svg'):
                #print(node.get('id'),'2',node.get('href'))
                self.process_clone(node)

            elif node.tag == inkex.addNS('style', 'svg'):
                if node.get('type')=="text/css":
                    self.parse_css(node.text)

            elif node.tag == inkex.addNS('defs', 'svg'):
                for sub in node:
                    if sub.tag == inkex.addNS('style','svg'):
                        self.parse_css(sub.text)
            else:
                self.process_shape(node, self.groupmat[-1], group_stroke = stroke_group)
        if trans:
            self.groupmat.pop()

    def parse_css(self,css_string):
        if css_string == None:
            return
        name_list=[]
        value_list=[]
        name=""
        value=""
        i=0
        while i < len(css_string):
            c=css_string[i]
            if c==",":
                i=i+1
                name_list.append(name)
                value_list.append(value)
                name=""
                value=""
                continue
            if c=="{":
                i=i+1
                while i < len(css_string):
                    c=css_string[i]
                    i=i+1
                    if c=="}":
                        break
                    else:
                        value = value+c

                if len(value_list)>0:
                    len_value_list = len(value_list)
                    k=-1
                    while abs(k) <= len_value_list and value_list[k]=="":
                        value_list[k]=value
                        k=k-1
                name_list.append(name)
                value_list.append(value)
                name=""
                value=""
                continue
            name=name+c
            i=i+1
        for i in range(len(name_list)):
            name_list[i]=" ".join(name_list[i].split())
            self.CSS_values.add(name_list[i],value_list[i])


    def unit2mm(self, string): #,dpi=None):
        if string==None:
            return None
        # Returns mm given a string representation of units in another system
        # a dictionary of unit to user unit conversion factors
        uuconv = {'in': 25.4,
                  'pt': 25.4/72.0,
                  'mm': 1.0,
                  'cm': 10.0,
                  'm' : 1000.0,
                  'km': 1000.0*1000.0,
                  'pc': 25.4/6.0,
                  'yd': 25.4*12*3,
                  'ft': 25.4*12}

        unit = re.compile('(%s)$' % '|'.join(uuconv.keys()))
        param = re.compile(r'(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)')

        string = string.replace(' ','')
        p = param.match(string)
        u = unit.search(string)

        if p:
            retval = float(p.string[p.start():p.end()])
        else:
            return None
        if u:
            retunit = u.string[u.start():u.end()]
        else:
            return None
        try:
            return retval * uuconv[retunit]
        except KeyError:
            return None

    def unit2px(self, string):
        if string==None:
            return None
        string = string.replace(' ','')
        string = string.replace('px','')
        try:
            retval = float(string)
        except:
            retval = None
        return retval


    def Make_PNG(self):
        #create OS temp folder
        tmp_dir = tempfile.mkdtemp()
        #tmp_dir = self.tempDir()
        if self.inkscape_exe != None:
            try:
                svg_temp_file = os.path.join(tmp_dir, "k40w_temp.svg")
                png_temp_file = os.path.join(tmp_dir, "k40w_image.png")
                dpi = "%d" %(self.image_dpi)
                #print(dpi)
                self.document.write(svg_temp_file)
                #self.document.write("svg_temp_file.svg", encoding='utf-8')

                # Check Version of Inkscape
                cmd = [ self.inkscape_exe, "-V"]
                (stdout,stderr)=run_external(cmd, self.timout)
                #print(stdout)
                if stdout.find(b'Inkscape 1.')==-1:
                    cmd = [ self.inkscape_exe, self.png_area, "--export-dpi", dpi, \
                            "--export-background","rgb(255, 255, 255)","--export-background-opacity", \
                            "255" ,"--export-png", png_temp_file, svg_temp_file ]
                else:
                    cmd = [ self.inkscape_exe, self.png_area, "--export-dpi", dpi, \
                            "--export-background","rgb(255, 255, 255)","--export-background-opacity", \
                            "255" ,"--export-type=png", "--export-filename=%s" %(png_temp_file), svg_temp_file ]

                run_external(cmd, self.timout)
                self.raster_PIL = Image.open(png_temp_file)
                self.raster_PIL = self.raster_PIL.convert("L")
            except Exception as e:
                try:
                    shutil.rmtree(tmp_dir)
                except:
                    pass
                error_text = "%s" %(e)
                raise Exception("Inkscape Execution Failed (while making raster data).\n%s" %(error_text))
        else:
            raise Exception("Inkscape Not found.")
        try:
            shutil.rmtree(tmp_dir)
        except:
            raise Exception("Temp dir failed to delete:\n%s" %(tmp_dir) )


##    def open_cdr_file(self,filename):
##        #create OS temp folder
##        svg_temp_file=filename
##        #tmp_dir = tempfile.mkdtemp()
##        tmp_dir = self.tempDir()
##        if self.inkscape_exe != None:
##            try:
##                #svg_temp_file = os.path.join(tmp_dir, "k40w_temp.svg")
##                txt2path_file = os.path.join(tmp_dir, "txt2path.svg")
##                #self.document.write(svg_temp_file)
##                cmd = [ self.inkscape_exe, "--export-text-to-path","--export-plain-svg",txt2path_file, svg_temp_file ]
##                run_external(cmd, self.timout)
##                self.parse_svg(txt2path_file)
##            except Exception as e:
##                raise Exception("Inkscape Execution Failed (while converting text to paths).\n\n"+str(e))
##        else:
##            raise Exception("Inkscape Not found.")
##        try:
##            shutil.rmtree(tmp_dir)
##        except:
##            raise Exception("Temp dir failed to delete:\n%s" %(tmp_dir) )


##    def tempDir(self):
##        tmpdir = tempfile.mkdtemp()
##        if os.path.isdir(tmpdir):
##            print("first tmp dir exists")
##            print(tmpdir)
##            #raw_input("press any key...")
##        else:
##            print("try again")
##            tmpdir_base = tempfile.gettempdir()
##            print(tmpdir_base)
##            tmpdir = tmpdir_base+"/k40whisperer"
##            os.mkdir(tmpdir)
##            if not os.path.isdir(tmpdir):
##                print("still didn't work")
##            #creatte folder intempdir
##            #test if new dir exists
##
##        return tmpdir


    def convert_text2paths(self):
        #create OS temp folder
        tmp_dir = tempfile.mkdtemp()
        #tmp_dir = self.tempDir()
        if self.inkscape_exe != None:
            try:
                svg_temp_file = os.path.join(tmp_dir, "k40w_temp.svg")
                txt2path_file = os.path.join(tmp_dir, "txt2path.svg")
                self.document.write(svg_temp_file)

                # Check Version of Inkscape
                cmd = [ self.inkscape_exe, "-V"]
                (stdout,stderr)=run_external(cmd, self.timout)
                if stdout.find(b'Inkscape 1.')==-1:
                    cmd = [ self.inkscape_exe, "--export-text-to-path","--export-plain-svg", \
                            txt2path_file, svg_temp_file,  ]
                else:
                    cmd = [ self.inkscape_exe, "--export-text-to-path","--export-plain-svg", \
                            "--export-filename=%s" %(txt2path_file), svg_temp_file,  ]

                (stdout,stderr)=run_external(cmd, self.timout)
                self.parse_svg(txt2path_file)
            except Exception as e:
                raise Exception("Inkscape Execution Failed (while converting text to paths).\n\n"+str(e))
        else:
            raise Exception("Inkscape Not found.")
        try:
            shutil.rmtree(tmp_dir)
        except:
            raise Exception("Temp dir failed to delete:\n%s" %(tmp_dir) )


    def set_size(self,pxpi,viewbox):
        width_mm = viewbox[2]/pxpi*25.4
        height_mm = viewbox[3]/pxpi*25.4
        self.document.getroot().set('width', '%fmm' %(width_mm))
        self.document.getroot().set('height','%fmm' %(height_mm))
        self.document.getroot().set('viewBox', '%f %f %f %f' %(viewbox[0],viewbox[1],viewbox[2],viewbox[3]))


    def make_paths(self, txt2paths=False ):
        self.txt2paths = txt2paths
        msg               = ""

        if (self.txt2paths):
            self.convert_text2paths()

        #################
        ## GET VIEWBOX ##
        #################
        view_box_array = self.document.getroot().xpath('@viewBox', namespaces=inkex.NSS) #[0]
        if view_box_array == []:
            view_box_str = None
        else:
            view_box_str=view_box_array[0]
        #################
        ##  GET SIZE   ##
        #################
        h_array = self.document.getroot().xpath('@height', namespaces=inkex.NSS)
        w_array = self.document.getroot().xpath('@width' , namespaces=inkex.NSS)
        if h_array == []:
            h_string = None
        else:
            h_string = h_array[0]
        if w_array == []:
            w_string = None
        else:
            w_string = w_array[0]
        #################
        w_mm = self.unit2mm(w_string)
        h_mm = self.unit2mm(h_string)
        w_px = self.unit2px(w_string)
        h_px = self.unit2px(h_string)
        self.SVG_Size = [w_mm, h_mm, w_px, h_px]

        if view_box_str!=None:
            view_box_list = view_box_str.split(' ')
            DXpix= float(view_box_list[0])
            DYpix= float(view_box_list[1])
            Wpix = float(view_box_list[2])
            Hpix = float(view_box_list[3])
            self.SVG_ViewBox = [DXpix, DYpix, Wpix, Hpix]
        else:
            SVG_ViewBox = None

        if h_mm==None or w_mm==None or self.SVG_ViewBox==None:
            line1  = "Cannot determine SVG size. Viewbox missing or Units not set."
            raise SVG_PXPI_EXCEPTION("%s" %(line1))

        scale_h = h_mm/Hpix
        scale_w = w_mm/Wpix
        Dx = DXpix * scale_w
        Dy = DYpix * scale_h

        if abs(1.0-scale_h/scale_w) > .01:
            line1 ="SVG Files with different scales in X and Y are not supported.\n"
            line2 ="In Inkscape (v0.92): 'File'-'Document Properties'"
            line3 ="on the 'Page' tab adjust 'Scale x:' in the 'Scale' section"
            raise Exception("%s\n%s\n%s" %(line1,line2,line3))

        for node in self.document.getroot().xpath('//svg:g', namespaces=inkex.NSS):
            if node.get(inkex.addNS('groupmode', 'inkscape')) == 'layer':
                layer = node.get(inkex.addNS('label', 'inkscape'))
                self.layernames.append(layer.lower())
                layer = layer.replace(' ', '_')
                if layer and not layer in self.layers:
                    self.layers.append(layer)

        self.groupmat = [[[scale_w,    0.0,  0.0-Dx],
                          [0.0  , -scale_h, h_mm+Dy]]]

        self.process_group(self.document.getroot())


        #################################################
        xmin= 0.0
        xmax=  w_mm
        ymin= -h_mm
        ymax= 0.0
        self.Make_PNG()

        self.Xsize=xmax-xmin
        self.Ysize=ymax-ymin
        Xcorner=xmin
        Ycorner=ymax
        for ii in range(len(self.lines)):
            self.lines[ii][0] = self.lines[ii][0]-Xcorner
            self.lines[ii][1] = self.lines[ii][1]-Ycorner
            self.lines[ii][2] = self.lines[ii][2]-Xcorner
            self.lines[ii][3] = self.lines[ii][3]-Ycorner

        self.cut_lines = []
        self.eng_lines = []
        for line in self.lines:
            ID=line[5]
            if (self.Cut_Type[ID]=="engrave"):
                self.eng_lines.append([line[0],line[1],line[2],line[3]])
            elif (self.Cut_Type[ID]=="cut"):
                self.cut_lines.append([line[0],line[1],line[2],line[3]])
            else:
                pass

    def strip_text(self,input):
        try:
            return input.strip()
        except:
            return ""


if __name__ == '__main__':
    svg_reader =  SVG_READER()
    #svg_reader.parse("test.svg")
    #svg_reader.make_paths()
    tests=["100 mm ",".1 m ","4 in ","100 px ", "100  "]
    for line in tests:
        print(svg_reader.unit2mm(line),svg_reader.unit2px(line))
