summaryrefslogtreecommitdiff
path: root/support/dxf_export
diff options
context:
space:
mode:
Diffstat (limited to 'support/dxf_export')
-rw-r--r--support/dxf_export/__main__.py6
-rwxr-xr-xsupport/dxf_export/better_dxf_outlines.py123
-rw-r--r--support/dxf_export/dxf_footer.txt62
-rw-r--r--support/dxf_export/dxf_header.txt580
-rwxr-xr-xsupport/dxf_export/dxf_templates.py645
-rw-r--r--support/dxf_export/effect.py81
6 files changed, 726 insertions, 771 deletions
diff --git a/support/dxf_export/__main__.py b/support/dxf_export/__main__.py
index 76c2121..676935e 100644
--- a/support/dxf_export/__main__.py
+++ b/support/dxf_export/__main__.py
@@ -1,14 +1,14 @@
import sys, os, xml.etree.ElementTree, shutil
from lib import util
-from . import better_dxf_outlines
+from . import effect
def _export_dxf(in_path, out_path):
- dxf_export = better_dxf_outlines.MyEffect()
+ dxf_export = effect.DXFExportEffect()
dxf_export.affect(args = [in_path], output = False)
with open(out_path, 'w') as file:
- file.write(dxf_export.dxf)
+ dxf_export.write(file)
def _get_inkscape_layer_count(svg_path):
diff --git a/support/dxf_export/better_dxf_outlines.py b/support/dxf_export/better_dxf_outlines.py
deleted file mode 100755
index 2757fa9..0000000
--- a/support/dxf_export/better_dxf_outlines.py
+++ /dev/null
@@ -1,123 +0,0 @@
-#!/usr/bin/env python
-'''
-Copyright (C) 2005,2007 Aaron Spike, aaron@ekips.org
-- template dxf_outlines.dxf added Feb 2008 by Alvin Penner, penner@vaxxine.com
-- layers, transformation, flattening added April 2008 by Bob Cook, bob@bobcookdev.com
-- bug fix for xpath() calls added February 2009 by Bob Cook, bob@bobcookdev.com
-- max value of 10 on path flattening, August 2011 by Bob Cook, bob@bobcookdev.com
-
-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
-'''
-import inkex, simplepath, simpletransform, cubicsuperpath, cspsubdiv, dxf_templates, re
-
-class MyEffect(inkex.Effect):
-
- def __init__(self):
-
- inkex.Effect.__init__(self)
- self.dxf = ''
- self.handle = 255
- self.flatness = 0.1
-
- def output(self):
- print self.dxf
-
- def dxf_add(self, str):
- self.dxf += str
-
- def dxf_insert_code(self, code, value):
- self.dxf += code + "\n" + value + "\n"
-
- def dxf_line(self,layer,csp):
- self.dxf_insert_code( '0', 'LINE' )
- self.dxf_insert_code( '8', layer )
- self.dxf_insert_code( '62', '4' )
- self.dxf_insert_code( '5', '%x' % self.handle )
- self.dxf_insert_code( '100', 'AcDbEntity' )
- self.dxf_insert_code( '100', 'AcDbLine' )
- self.dxf_insert_code( '10', '%f' % csp[0][0] )
- self.dxf_insert_code( '20', '%f' % csp[0][1] )
- self.dxf_insert_code( '30', '0.0' )
- self.dxf_insert_code( '11', '%f' % csp[1][0] )
- self.dxf_insert_code( '21', '%f' % csp[1][1] )
- self.dxf_insert_code( '31', '0.0' )
-
- def dxf_point(self,layer,x,y):
- self.dxf_insert_code( '0', 'POINT' )
- self.dxf_insert_code( '8', layer )
- self.dxf_insert_code( '62', '4' )
- self.dxf_insert_code( '5', '%x' % self.handle )
- self.dxf_insert_code( '100', 'AcDbEntity' )
- self.dxf_insert_code( '100', 'AcDbPoint' )
- self.dxf_insert_code( '10', '%f' % x )
- self.dxf_insert_code( '20', '%f' % y )
- self.dxf_insert_code( '30', '0.0' )
-
- def dxf_path_to_lines(self,layer,p):
- f = self.flatness
- is_flat = 0
- while is_flat < 1:
- if f > 10:
- break
- try:
- cspsubdiv.cspsubdiv(p, f)
- is_flat = 1
- except:
- f += 0.1
-
- for sub in p:
- for i in range(len(sub)-1):
- self.handle += 1
- s = sub[i]
- e = sub[i+1]
- self.dxf_line(layer,[s[1],e[1]])
-
- def dxf_path_to_point(self,layer,p):
- bbox = simpletransform.roughBBox(p)
- x = (bbox[0] + bbox[1]) / 2
- y = (bbox[2] + bbox[3]) / 2
- self.dxf_point(layer,x,y)
-
- def effect(self):
- self.dxf_insert_code( '999', 'Inkscape export via "Better DXF Output" (bob@bobcookdev.com)' )
- self.dxf_add( dxf_templates.r14_header )
-
- scale = 25.4/90.0
- h = self.unittouu(self.document.getroot().xpath('@height',namespaces=inkex.NSS)[0])
-
- path = '//svg:path'
- for node in self.document.getroot().xpath(path,namespaces=inkex.NSS):
-
- layer = node.getparent().get(inkex.addNS('label','inkscape'))
- if layer == None:
- layer = 'Layer 1'
-
- d = node.get('d')
- p = cubicsuperpath.parsePath(d)
-
- t = node.get('transform')
- if t != None:
- m = simpletransform.parseTransform(t)
- simpletransform.applyTransformToPath(m,p)
-
- m = [[scale,0,0],[0,-scale,h*scale]]
- simpletransform.applyTransformToPath(m,p)
-
- if re.search('drill$',layer,re.I) == None:
- self.dxf_path_to_lines(layer,p)
- else:
- self.dxf_path_to_point(layer,p)
-
- self.dxf_add( dxf_templates.r14_footer )
diff --git a/support/dxf_export/dxf_footer.txt b/support/dxf_export/dxf_footer.txt
new file mode 100644
index 0000000..a225dd7
--- /dev/null
+++ b/support/dxf_export/dxf_footer.txt
@@ -0,0 +1,62 @@
+0
+ENDSEC
+0
+SECTION
+2
+OBJECTS
+0
+DICTIONARY
+5
+C
+330
+0
+100
+AcDbDictionary
+3
+ACAD_GROUP
+350
+D
+3
+ACAD_MLINESTYLE
+350
+17
+0
+DICTIONARY
+5
+D
+330
+C
+100
+AcDbDictionary
+0
+DICTIONARY
+5
+1A
+330
+C
+100
+AcDbDictionary
+0
+DICTIONARY
+5
+17
+330
+C
+100
+AcDbDictionary
+3
+STANDARD
+350
+18
+0
+DICTIONARY
+5
+19
+330
+C
+100
+AcDbDictionary
+0
+ENDSEC
+0
+EOF
diff --git a/support/dxf_export/dxf_header.txt b/support/dxf_export/dxf_header.txt
new file mode 100644
index 0000000..341cb1b
--- /dev/null
+++ b/support/dxf_export/dxf_header.txt
@@ -0,0 +1,580 @@
+0
+SECTION
+2
+HEADER
+9
+$ACADVER
+1
+AC1014
+9
+$HANDSEED
+5
+FFFF
+0
+ENDSEC
+0
+SECTION
+2
+TABLES
+0
+TABLE
+2
+VPORT
+5
+8
+330
+0
+100
+AcDbSymbolTable
+70
+4
+0
+VPORT
+5
+2E
+330
+8
+100
+AcDbSymbolTableRecord
+100
+AcDbViewportTableRecord
+2
+*ACTIVE
+70
+0
+10
+0.0
+20
+0.0
+11
+1.0
+21
+1.0
+12
+4.25
+22
+5.5
+13
+0.0
+23
+0.0
+14
+10.0
+24
+10.0
+15
+10.0
+25
+10.0
+16
+0.0
+26
+0.0
+36
+1.0
+17
+0.0
+27
+0.0
+37
+0.0
+40
+11
+41
+1.24
+42
+50.0
+43
+0.0
+44
+0.0
+50
+0.0
+51
+0.0
+71
+0
+72
+100
+73
+1
+74
+3
+75
+0
+76
+0
+77
+0
+78
+0
+0
+ENDTAB
+0
+TABLE
+2
+LTYPE
+5
+5
+330
+0
+100
+AcDbSymbolTable
+70
+1
+0
+LTYPE
+5
+14
+330
+5
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+2
+BYBLOCK
+70
+0
+3
+
+72
+65
+73
+0
+40
+0.0
+0
+LTYPE
+5
+15
+330
+5
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+2
+BYLAYER
+70
+0
+3
+
+72
+65
+73
+0
+40
+0.0
+0
+LTYPE
+5
+16
+330
+5
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+2
+CONTINUOUS
+70
+0
+3
+Solid line
+72
+65
+73
+0
+40
+0.0
+0
+ENDTAB
+0
+TABLE
+2
+LAYER
+5
+2
+330
+0
+100
+AcDbSymbolTable
+70
+1
+0
+LAYER
+5
+10
+330
+2
+100
+AcDbSymbolTableRecord
+100
+AcDbLayerTableRecord
+2
+0
+70
+0
+62
+7
+6
+CONTINUOUS
+0
+ENDTAB
+0
+TABLE
+2
+STYLE
+5
+3
+330
+0
+100
+AcDbSymbolTable
+70
+1
+0
+STYLE
+5
+11
+330
+3
+100
+AcDbSymbolTableRecord
+100
+AcDbTextStyleTableRecord
+2
+STANDARD
+70
+0
+40
+0.0
+41
+1.0
+50
+0.0
+71
+0
+42
+2.5
+3
+txt
+4
+
+0
+ENDTAB
+0
+TABLE
+2
+VIEW
+5
+6
+330
+0
+100
+AcDbSymbolTable
+70
+0
+0
+ENDTAB
+0
+TABLE
+2
+UCS
+5
+7
+330
+0
+100
+AcDbSymbolTable
+70
+0
+0
+ENDTAB
+0
+TABLE
+2
+APPID
+5
+9
+330
+0
+100
+AcDbSymbolTable
+70
+2
+0
+APPID
+5
+12
+330
+9
+100
+AcDbSymbolTableRecord
+100
+AcDbRegAppTableRecord
+2
+ACAD
+70
+0
+0
+ENDTAB
+0
+TABLE
+2
+DIMSTYLE
+5
+A
+330
+0
+100
+AcDbSymbolTable
+70
+1
+0
+DIMSTYLE
+105
+27
+330
+A
+100
+AcDbSymbolTableRecord
+100
+AcDbDimStyleTableRecord
+2
+ISO-25
+70
+0
+3
+
+4
+
+5
+
+6
+
+7
+
+40
+1.0
+41
+2.5
+42
+0.625
+43
+3.75
+44
+1.25
+45
+0.0
+46
+0.0
+47
+0.0
+48
+0.0
+140
+2.5
+141
+2.5
+142
+0.0
+143
+0.03937007874016
+144
+1.0
+145
+0.0
+146
+1.0
+147
+0.625
+71
+0
+72
+0
+73
+0
+74
+0
+75
+0
+76
+0
+77
+1
+78
+8
+170
+0
+171
+3
+172
+1
+173
+0
+174
+0
+175
+0
+176
+0
+177
+0
+178
+0
+270
+2
+271
+2
+272
+2
+273
+2
+274
+3
+340
+11
+275
+0
+280
+0
+281
+0
+282
+0
+283
+0
+284
+8
+285
+0
+286
+0
+287
+3
+288
+0
+0
+ENDTAB
+0
+TABLE
+2
+BLOCK_RECORD
+5
+1
+330
+0
+100
+AcDbSymbolTable
+70
+1
+0
+BLOCK_RECORD
+5
+1F
+330
+1
+100
+AcDbSymbolTableRecord
+100
+AcDbBlockTableRecord
+2
+*MODEL_SPACE
+0
+BLOCK_RECORD
+5
+1B
+330
+1
+100
+AcDbSymbolTableRecord
+100
+AcDbBlockTableRecord
+2
+*PAPER_SPACE
+0
+ENDTAB
+0
+ENDSEC
+0
+SECTION
+2
+BLOCKS
+0
+BLOCK
+5
+20
+330
+1F
+100
+AcDbEntity
+8
+0
+100
+AcDbBlockBegin
+2
+*MODEL_SPACE
+70
+0
+10
+0.0
+20
+0.0
+30
+0.0
+3
+*MODEL_SPACE
+1
+
+0
+ENDBLK
+5
+21
+330
+1F
+100
+AcDbEntity
+8
+0
+100
+AcDbBlockEnd
+0
+BLOCK
+5
+1C
+330
+1B
+100
+AcDbEntity
+67
+1
+8
+0
+100
+AcDbBlockBegin
+2
+*PAPER_SPACE
+1
+
+0
+ENDBLK
+5
+1D
+330
+1B
+100
+AcDbEntity
+67
+1
+8
+0
+100
+AcDbBlockEnd
+0
+ENDSEC
+0
+SECTION
+2
+ENTITIES
diff --git a/support/dxf_export/dxf_templates.py b/support/dxf_export/dxf_templates.py
deleted file mode 100755
index fb26632..0000000
--- a/support/dxf_export/dxf_templates.py
+++ /dev/null
@@ -1,645 +0,0 @@
-r14_header = ''' 0
-SECTION
- 2
-HEADER
- 9
-$ACADVER
- 1
-AC1014
- 9
-$HANDSEED
- 5
-FFFF
- 0
-ENDSEC
- 0
-SECTION
- 2
-TABLES
- 0
-TABLE
- 2
-VPORT
- 5
-8
-330
-0
-100
-AcDbSymbolTable
- 70
- 4
- 0
-VPORT
- 5
-2E
-330
-8
-100
-AcDbSymbolTableRecord
-100
-AcDbViewportTableRecord
- 2
-*ACTIVE
- 70
- 0
- 10
-0.0
- 20
-0.0
- 11
-1.0
- 21
-1.0
- 12
-4.25
- 22
-5.5
- 13
-0.0
- 23
-0.0
- 14
-10.0
- 24
-10.0
- 15
-10.0
- 25
-10.0
- 16
-0.0
- 26
-0.0
- 36
-1.0
- 17
-0.0
- 27
-0.0
- 37
-0.0
- 40
-11
- 41
-1.24
- 42
-50.0
- 43
-0.0
- 44
-0.0
- 50
-0.0
- 51
-0.0
- 71
- 0
- 72
- 100
- 73
- 1
- 74
- 3
- 75
- 0
- 76
- 0
- 77
- 0
- 78
- 0
- 0
-ENDTAB
- 0
-TABLE
- 2
-LTYPE
- 5
-5
-330
-0
-100
-AcDbSymbolTable
- 70
- 1
- 0
-LTYPE
- 5
-14
-330
-5
-100
-AcDbSymbolTableRecord
-100
-AcDbLinetypeTableRecord
- 2
-BYBLOCK
- 70
- 0
- 3
-
- 72
- 65
- 73
- 0
- 40
-0.0
- 0
-LTYPE
- 5
-15
-330
-5
-100
-AcDbSymbolTableRecord
-100
-AcDbLinetypeTableRecord
- 2
-BYLAYER
- 70
- 0
- 3
-
- 72
- 65
- 73
- 0
- 40
-0.0
- 0
-LTYPE
- 5
-16
-330
-5
-100
-AcDbSymbolTableRecord
-100
-AcDbLinetypeTableRecord
- 2
-CONTINUOUS
- 70
- 0
- 3
-Solid line
- 72
- 65
- 73
- 0
- 40
-0.0
- 0
-ENDTAB
- 0
-TABLE
- 2
-LAYER
- 5
-2
-330
-0
-100
-AcDbSymbolTable
- 70
-1
- 0
-LAYER
- 5
-10
-330
-2
-100
-AcDbSymbolTableRecord
-100
-AcDbLayerTableRecord
- 2
-0
- 70
- 0
- 62
- 7
- 6
-CONTINUOUS
- 0
-ENDTAB
- 0
-TABLE
- 2
-STYLE
- 5
-3
-330
-0
-100
-AcDbSymbolTable
- 70
- 1
- 0
-STYLE
- 5
-11
-330
-3
-100
-AcDbSymbolTableRecord
-100
-AcDbTextStyleTableRecord
- 2
-STANDARD
- 70
- 0
- 40
-0.0
- 41
-1.0
- 50
-0.0
- 71
- 0
- 42
-2.5
- 3
-txt
- 4
-
- 0
-ENDTAB
- 0
-TABLE
- 2
-VIEW
- 5
-6
-330
-0
-100
-AcDbSymbolTable
- 70
- 0
- 0
-ENDTAB
- 0
-TABLE
- 2
-UCS
- 5
-7
-330
-0
-100
-AcDbSymbolTable
- 70
- 0
- 0
-ENDTAB
- 0
-TABLE
- 2
-APPID
- 5
-9
-330
-0
-100
-AcDbSymbolTable
- 70
- 2
- 0
-APPID
- 5
-12
-330
-9
-100
-AcDbSymbolTableRecord
-100
-AcDbRegAppTableRecord
- 2
-ACAD
- 70
- 0
- 0
-ENDTAB
- 0
-TABLE
- 2
-DIMSTYLE
- 5
-A
-330
-0
-100
-AcDbSymbolTable
- 70
- 1
- 0
-DIMSTYLE
-105
-27
-330
-A
-100
-AcDbSymbolTableRecord
-100
-AcDbDimStyleTableRecord
- 2
-ISO-25
- 70
- 0
- 3
-
- 4
-
- 5
-
- 6
-
- 7
-
- 40
-1.0
- 41
-2.5
- 42
-0.625
- 43
-3.75
- 44
-1.25
- 45
-0.0
- 46
-0.0
- 47
-0.0
- 48
-0.0
-140
-2.5
-141
-2.5
-142
-0.0
-143
-0.03937007874016
-144
-1.0
-145
-0.0
-146
-1.0
-147
-0.625
- 71
- 0
- 72
- 0
- 73
- 0
- 74
- 0
- 75
- 0
- 76
- 0
- 77
- 1
- 78
- 8
-170
- 0
-171
- 3
-172
- 1
-173
- 0
-174
- 0
-175
- 0
-176
- 0
-177
- 0
-178
- 0
-270
- 2
-271
- 2
-272
- 2
-273
- 2
-274
- 3
-340
-11
-275
- 0
-280
- 0
-281
- 0
-282
- 0
-283
- 0
-284
- 8
-285
- 0
-286
- 0
-287
- 3
-288
- 0
- 0
-ENDTAB
- 0
-TABLE
- 2
-BLOCK_RECORD
- 5
-1
-330
-0
-100
-AcDbSymbolTable
- 70
- 1
- 0
-BLOCK_RECORD
- 5
-1F
-330
-1
-100
-AcDbSymbolTableRecord
-100
-AcDbBlockTableRecord
- 2
-*MODEL_SPACE
- 0
-BLOCK_RECORD
- 5
-1B
-330
-1
-100
-AcDbSymbolTableRecord
-100
-AcDbBlockTableRecord
- 2
-*PAPER_SPACE
- 0
-ENDTAB
- 0
-ENDSEC
- 0
-SECTION
- 2
-BLOCKS
- 0
-BLOCK
- 5
-20
-330
-1F
-100
-AcDbEntity
- 8
-0
-100
-AcDbBlockBegin
- 2
-*MODEL_SPACE
- 70
- 0
- 10
-0.0
- 20
-0.0
- 30
-0.0
- 3
-*MODEL_SPACE
- 1
-
- 0
-ENDBLK
- 5
-21
-330
-1F
-100
-AcDbEntity
- 8
-0
-100
-AcDbBlockEnd
- 0
-BLOCK
- 5
-1C
-330
-1B
-100
-AcDbEntity
- 67
- 1
- 8
-0
-100
-AcDbBlockBegin
- 2
-*PAPER_SPACE
- 1
-
- 0
-ENDBLK
- 5
-1D
-330
-1B
-100
-AcDbEntity
- 67
- 1
- 8
-0
-100
-AcDbBlockEnd
- 0
-ENDSEC
- 0
-SECTION
- 2
-ENTITIES
-'''
-
-
-r14_footer = ''' 0
-ENDSEC
- 0
-SECTION
- 2
-OBJECTS
- 0
-DICTIONARY
- 5
-C
-330
-0
-100
-AcDbDictionary
- 3
-ACAD_GROUP
-350
-D
- 3
-ACAD_MLINESTYLE
-350
-17
- 0
-DICTIONARY
- 5
-D
-330
-C
-100
-AcDbDictionary
- 0
-DICTIONARY
- 5
-1A
-330
-C
-100
-AcDbDictionary
- 0
-DICTIONARY
- 5
-17
-330
-C
-100
-AcDbDictionary
- 3
-STANDARD
-350
-18
- 0
-DICTIONARY
- 5
-19
-330
-C
-100
-AcDbDictionary
- 0
-ENDSEC
- 0
-EOF'''
diff --git a/support/dxf_export/effect.py b/support/dxf_export/effect.py
new file mode 100644
index 0000000..09699b0
--- /dev/null
+++ b/support/dxf_export/effect.py
@@ -0,0 +1,81 @@
+"""
+Based on code from Aaron Spike. See http://www.bobcookdev.com/inkscape/inkscape-dxf.html
+"""
+
+import pkgutil
+from . import inkex, simpletransform, cubicsuperpath, cspsubdiv
+
+
+class DXFExportEffect(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self._dxf_instructions = []
+ self._handle = 255
+ self._flatness = 0.1
+
+ def _add_instruction(self, code, value):
+ self._dxf_instructions.append((code, str(value)))
+
+ def _add_dxf_line(self, layer, csp):
+ self._add_instruction(0, 'LINE')
+ self._add_instruction(8, layer)
+ self._add_instruction(62, 4)
+ self._add_instruction(5, '{:x}'.format(self._handle))
+ self._add_instruction(100, 'AcDbEntity')
+ self._add_instruction(100, 'AcDbLine')
+ self._add_instruction(10, repr(csp[0][0]))
+ self._add_instruction(20, repr(csp[0][1]))
+ self._add_instruction(30, 0.0)
+ self._add_instruction(11, repr(csp[1][0]))
+ self._add_instruction(21, repr(csp[1][1]))
+ self._add_instruction(31, 0.0)
+
+ def _add_dxf_path(self, layer, path):
+ cspsubdiv.cspsubdiv(path, self._flatness)
+
+ for sub in path:
+ for i in range(len(sub) - 1):
+ self._handle += 1
+ s = sub[i]
+ e = sub[i + 1]
+ self._add_dxf_line(layer, [s[1], e[1]])
+
+ def _add_dxf_shape(self, node, document_transformation):
+ layer = self._get_inkscape_layer(node)
+ path = cubicsuperpath.parsePath(node.get('d'))
+
+ transformation = simpletransform.composeTransform(
+ document_transformation,
+ simpletransform.composeParents(node, [[1, 0, 0], [0, 1, 0]]))
+
+ simpletransform.applyTransformToPath(transformation, path)
+
+ self._add_dxf_path(layer, path)
+
+ def effect(self):
+ height = self.unittouu(self.document.getroot().xpath('@height', namespaces = inkex.NSS)[0])
+ document_transformation = [[1, 0, 0], [0, -1, height]]
+
+ for node in self.document.getroot().xpath('//svg:path', namespaces = inkex.NSS):
+ self._add_dxf_shape(node, document_transformation)
+
+ def write(self, file):
+ file.write(pkgutil.get_data(__name__, 'dxf_header.txt'))
+
+ for code, value in self._dxf_instructions:
+ print >> file, code
+ print >> file, value
+
+ file.write(pkgutil.get_data(__name__, 'dxf_footer.txt'))
+
+ @classmethod
+ def _get_inkscape_layer(cls, node):
+ while node is not None:
+ layer = node.get(inkex.addNS('label', 'inkscape'))
+
+ if layer is not None:
+ return layer
+
+ node = node.getparent()
+
+ return ''