index 02d049364baf7459e9ba32a99bc25d1f9a316f46..5b16ea2f5a119e335be52832ceef787a484e6139 100755 (executable)
from webslicer_effect import *
import inkex
import gettext
-import os.path
+import os
+import sys
import tempfile
-import commands
_ = gettext.gettext
+
class WebSlicer_Export(WebSlicer_Effect):
def __init__(self):
WebSlicer_Effect.__init__(self)
+ self.OptionParser.add_option("--tab")
self.OptionParser.add_option("--dir",
action="store", type="string",
dest="dir",
dest="with_code",
help="")
- def effect(self):
+
+ svgNS = '{http://www.w3.org/2000/svg}'
+
+
+ def validate_inputs(self):
# The user must supply a directory to export:
if is_empty( self.options.dir ):
inkex.errormsg(_('You must to give a directory to export the slices.'))
# Try to create it:
try:
os.makedirs( self.options.dir )
- except Exception as e:
+ except Exception, e:
inkex.errormsg( _('Can\'t create "%s".') % self.options.dir )
inkex.errormsg( _('Error: %s') % e )
return {'error':'Can\'t create the directory to export.'}
else:
inkex.errormsg(_('The directory "%s" does not exists.') % self.options.dir)
return
- # Create HTML and CSS files, if the user wants:
- if self.options.with_code:
- try:
- self.html = open(os.path.join(self.options.dir,'layout.html'), 'w')
- self.css = open(os.path.join(self.options.dir,'style.css'), 'w')
- self.html.write('Only a test yet\n\n')
- self.css.write('Only a test yet\n\n')
- except Exception as e:
- inkex.errormsg( _('Can\'t create code files.') )
- inkex.errormsg( _('Error: %s') % e )
- return {'error':'Can\'t create code files.'}
+ self.unique_html_id( self.get_slicer_layer() )
+ return None
+
+
+ def get_cmd_output(self, cmd):
+ # This solution comes from Andrew Reedick <jr9445 at ATT.COM>
+ # http://mail.python.org/pipermail/python-win32/2008-January/006606.html
+ # This method replaces the commands.getstatusoutput() usage, with the
+ # hope to correct the windows exporting bug:
+ # https://bugs.launchpad.net/inkscape/+bug/563722
+ if sys.platform != "win32": cmd = '{ '+ cmd +'; }'
+ pipe = os.popen(cmd +' 2>&1', 'r')
+ text = pipe.read()
+ sts = pipe.close()
+ if sts is None: sts = 0
+ if text[-1:] == '\n': text = text[:-1]
+ return sts, text
+
+
+ _html_ids = []
+ def unique_html_id(self, el):
+ for child in el.getchildren():
+ if child.tag in [ self.svgNS+'rect', self.svgNS+'path',
+ self.svgNS+'circle', self.svgNS+'g' ]:
+ conf = self.get_el_conf(child)
+ if conf['html-id'] in self._html_ids:
+ inkex.errormsg(
+ _('You have more than one element with "%s" html-id.') %
+ conf['html-id'] )
+ n = 2
+ while (conf['html-id']+"-"+str(n)) in self._html_ids: n += 1
+ conf['html-id'] += "-"+str(n)
+ self._html_ids.append( conf['html-id'] )
+ self.save_conf( conf, child )
+ self.unique_html_id( child )
+
+
+ def test_if_has_imagemagick(self):
+ (status, output) = self.get_cmd_output('convert --version')
+ self.has_magick = ( status == 0 and 'ImageMagick' in output )
+
+
+ def effect(self):
+ self.test_if_has_imagemagick()
+ error = self.validate_inputs()
+ if error: return error
+ # Register the basic CSS code:
+ self.reg_css( 'body', 'text-align', 'center' )
# Create the temporary SVG with invisible Slicer layer to export image pieces
self.create_the_temporary_svg()
# Start what we really want!
self.export_chids_of( self.get_slicer_layer() )
- # Close the HTML and CSS files:
+ # Write the HTML and CSS files if asked for:
if self.options.with_code:
- self.html.close()
- self.css.close()
+ self.make_html_file()
+ self.make_css_file()
# Delete the temporary SVG with invisible Slicer layer
self.delete_the_temporary_svg()
+ #TODO: prevent inkex to return svg code to update Inkscape
+
+
+ def make_html_file(self):
+ f = open(os.path.join(self.options.dir,'layout.html'), 'w')
+ f.write(
+ '<html>\n<head>\n' +\
+ ' <title>Web Layout Testing</title>\n' +\
+ ' <style type="text/css" media="screen">\n' +\
+ ' @import url("style.css")\n' +\
+ ' </style>\n' +\
+ '</head>\n<body>\n' +\
+ self.html_code() +\
+ ' <p style="position:absolute; bottom:10px">\n' +\
+ ' This HTML code is not done to the web. <br/>\n' +\
+ ' The automatic HTML and CSS code are only a helper.' +\
+ '</p>\n</body>\n</html>' )
+ f.close()
+
+
+ def make_css_file(self):
+ f = open(os.path.join(self.options.dir,'style.css'), 'w')
+ f.write(
+ '/*\n' +\
+ '** This CSS code is not done to the web.\n' +\
+ '** The automatic HTML and CSS code are only a helper.\n' +\
+ '*/\n' +\
+ self.css_code() )
+ f.close()
+
def create_the_temporary_svg(self):
(ref, self.tmp_svg) = tempfile.mkstemp('.svg')
layer = self.get_slicer_layer()
- current_style = layer.attrib['style']
+ current_style = ('style' in layer.attrib) and layer.attrib['style'] or ''
layer.attrib['style'] = 'display:none'
self.document.write( self.tmp_svg );
layer.attrib['style'] = current_style
+
def delete_the_temporary_svg(self):
os.remove( self.tmp_svg )
+
+ noid_element_count = 0
def get_el_conf(self, el):
- desc = el.find('{http://www.w3.org/2000/svg}desc')
+ desc = el.find(self.svgNS+'desc')
conf = {}
- if desc is not None:
- #desc = desc.text.split("\n")
- for line in desc.text.split("\n"):
- if line.find(':') > 0:
- line = line.split(':')
- conf[line[0].strip()] = line[1].strip()
+ if desc is None:
+ desc = inkex.etree.SubElement(el, 'desc')
+ if desc.text is None:
+ desc.text = ''
+ for line in desc.text.split("\n"):
+ if line.find(':') > 0:
+ line = line.split(':')
+ conf[line[0].strip()] = line[1].strip()
+ if not 'html-id' in conf:
+ if el == self.get_slicer_layer():
+ return {'html-id':'#body#'}
+ else:
+ self.noid_element_count += 1
+ conf['html-id'] = 'element-'+str(self.noid_element_count)
+ desc.text += "\nhtml-id:"+conf['html-id']
return conf
+ def save_conf(self, conf, el):
+ desc = el.find('{http://www.w3.org/2000/svg}desc')
+ if desc is not None:
+ conf_a = []
+ for k in conf:
+ conf_a.append( k+' : '+conf[k] )
+ desc.text = "\n".join(conf_a)
+
+
def export_chids_of(self, parent):
- nmspc = '{http://www.w3.org/2000/svg}'
+ parent_id = self.get_el_conf( parent )['html-id']
for el in parent.getchildren():
el_conf = self.get_el_conf( el )
- if el.tag == nmspc+'g':
+ if el.tag == self.svgNS+'g':
if self.options.with_code:
self.register_group_code( el, el_conf )
else:
self.export_chids_of( el )
- if el.tag in [ nmspc+'rect', nmspc+'path', nmspc+'circle' ]:
+ if el.tag in [ self.svgNS+'rect', self.svgNS+'path', self.svgNS+'circle' ]:
if self.options.with_code:
- self.register_unity_code( el, el_conf )
+ self.register_unity_code( el, el_conf, parent_id )
self.export_img( el, el_conf )
def register_group_code(self, group, conf):
- #inkex.errormsg( 'group CSS and HTML' )
- self.html.write( '<div id="G">\n' )
- for att in conf:
- self.html.write( ' <!-- {att} : {val} -->\n'.format(att=att, val=conf[att]) )
+ self.reg_html('div', group)
+ selec = '#'+conf['html-id']
+ self.reg_css( selec, 'position', 'absolute' )
+ geometry = self.get_relative_el_geometry(group)
+ self.reg_css( selec, 'top', str(int(geometry['y']))+'px' )
+ self.reg_css( selec, 'left', str(int(geometry['x']))+'px' )
+ self.reg_css( selec, 'width', str(int(geometry['w']))+'px' )
+ self.reg_css( selec, 'height', str(int(geometry['h']))+'px' )
self.export_chids_of( group )
- self.html.write( '</div><!-- end id="G" -->\n' )
- def register_unity_code(self, el, conf):
- #inkex.errormsg( 'unity CSS and HTML' )
- self.html.write( '<div id="image">\n' )
- for att in conf:
- self.html.write( ' <!-- {att} : {val} -->\n'.format(att=att, val=conf[att]) )
- self.html.write( '</div><!-- end id="image" -->\n' )
+ def __validate_slice_conf(self, conf):
+ if not 'layout-disposition' in conf:
+ conf['layout-disposition'] = 'bg-el-norepeat'
+ if not 'layout-position-anchor' in conf:
+ conf['layout-position-anchor'] = 'mc'
+ return conf
+
+
+ def register_unity_code(self, el, conf, parent_id):
+ conf = self.__validate_slice_conf(conf)
+ css_selector = '#'+conf['html-id']
+ bg_repeat = 'no-repeat'
+ img_name = self.img_name(el, conf)
+ if conf['layout-disposition'][0:2] == 'bg':
+ if conf['layout-disposition'][0:9] == 'bg-parent':
+ if parent_id == '#body#':
+ css_selector = 'body'
+ else:
+ css_selector = '#'+parent_id
+ if conf['layout-disposition'] == 'bg-parent-repeat':
+ bg_repeat = 'repeat'
+ if conf['layout-disposition'] == 'bg-parent-repeat-x':
+ bg_repeat = 'repeat-x'
+ if conf['layout-disposition'] == 'bg-parent-repeat-y':
+ bg_repeat = 'repeat-y'
+ lay_anchor = conf['layout-position-anchor']
+ if lay_anchor == 'tl': lay_anchor = 'top left'
+ if lay_anchor == 'tc': lay_anchor = 'top center'
+ if lay_anchor == 'tr': lay_anchor = 'top right'
+ if lay_anchor == 'ml': lay_anchor = 'middle left'
+ if lay_anchor == 'mc': lay_anchor = 'middle center'
+ if lay_anchor == 'mr': lay_anchor = 'middle right'
+ if lay_anchor == 'bl': lay_anchor = 'bottom left'
+ if lay_anchor == 'bc': lay_anchor = 'bottom center'
+ if lay_anchor == 'br': lay_anchor = 'bottom right'
+ self.reg_css( css_selector, 'background',
+ 'url("%s") %s %s' % (img_name, bg_repeat, lay_anchor) )
+ else: # conf['layout-disposition'][0:9] == 'bg-el...'
+ self.reg_html('div', el)
+ self.reg_css( css_selector, 'background',
+ 'url("%s") %s' % (img_name, 'no-repeat') )
+ self.reg_css( css_selector, 'position', 'absolute' )
+ geo = self.get_relative_el_geometry(el,True)
+ self.reg_css( css_selector, 'top', geo['y'] )
+ self.reg_css( css_selector, 'left', geo['x'] )
+ self.reg_css( css_selector, 'width', geo['w'] )
+ self.reg_css( css_selector, 'height', geo['h'] )
+ else: # conf['layout-disposition'] == 'img...'
+ self.reg_html('img', el)
+ if conf['layout-disposition'] == 'img-pos':
+ self.reg_css( css_selector, 'position', 'absolute' )
+ geo = self.get_relative_el_geometry(el)
+ self.reg_css( css_selector, 'left', str(geo['x'])+'px' )
+ self.reg_css( css_selector, 'top', str(geo['y'])+'px' )
+ if conf['layout-disposition'] == 'img-float-left':
+ self.reg_css( css_selector, 'float', 'right' )
+ if conf['layout-disposition'] == 'img-float-right':
+ self.reg_css( css_selector, 'float', 'right' )
+
+
+ el_geo = { }
+ def register_all_els_geometry(self):
+ ink_cmm = 'inkscape --query-all '+self.tmp_svg
+ (status, output) = self.get_cmd_output( ink_cmm )
+ self.el_geo = { }
+ if status == 0:
+ for el in output.split('\n'):
+ el = el.split(',')
+ if len(el) == 5:
+ self.el_geo[el[0]] = { 'x':float(el[1]), 'y':float(el[2]),
+ 'w':float(el[3]), 'h':float(el[4]) }
+ doc_w = inkex.unittouu( self.document.getroot().get('width') )
+ doc_h = inkex.unittouu( self.document.getroot().get('height') )
+ self.el_geo['webslicer-layer'] = { 'x':0, 'y':0, 'w':doc_w, 'h':doc_h }
+
+
+ def get_relative_el_geometry(self, el, value_to_css=False):
+ # This method return a dictionary with x, y, w and h keys.
+ # All values are float, if value_to_css is False, otherwise
+ # that is a string ended with "px". The x and y values are
+ # relative to parent position.
+ if not self.el_geo: self.register_all_els_geometry()
+ parent = self.getParentNode(el)
+ geometry = self.el_geo[el.attrib['id']]
+ geometry['x'] -= self.el_geo[parent.attrib['id']]['x']
+ geometry['y'] -= self.el_geo[parent.attrib['id']]['y']
+ if value_to_css:
+ for k in geometry: geometry[k] = str(int(geometry[k]))+'px'
+ return geometry
+
+
+ def img_name(self, el, conf):
+ return el.attrib['id']+'.'+conf['format']
def export_img(self, el, conf):
- (status, output) = commands.getstatusoutput(
- "inkscape -i '%s' -e '%s' '%s'" % (
- el.attrib['id'],
- os.path.join( self.options.dir, el.attrib['id']+'.png' ),
- self.tmp_svg
+ if not self.has_magick:
+ inkex.errormsg(_('You must install the ImageMagick to get JPG and GIF.'))
+ conf['format'] = 'png'
+ img_name = os.path.join( self.options.dir, self.img_name(el, conf) )
+ img_name_png = img_name
+ if conf['format'] != 'png':
+ img_name_png = img_name+'.png'
+ opts = ''
+ if 'bg-color' in conf: opts += ' -b "'+conf['bg-color']+'" -y 1'
+ if 'dpi' in conf: opts += ' -d '+conf['dpi']
+ if 'dimension' in conf:
+ dim = conf['dimension'].split('x')
+ opts += ' -w '+dim[0]+' -h '+dim[1]
+ (status, output) = self.get_cmd_output(
+ 'inkscape %s -i "%s" -e "%s" "%s"' % (
+ opts, el.attrib['id'], img_name_png, self.tmp_svg
)
)
- #inkex.errormsg( status )
- #inkex.errormsg( output )
+ if conf['format'] != 'png':
+ opts = ''
+ if conf['format'] == 'jpg':
+ opts += ' -quality '+str(conf['quality'])
+ if conf['format'] == 'gif':
+ if conf['gif-type'] == 'grayscale':
+ opts += ' -type Grayscale'
+ else:
+ opts += ' -type Palette'
+ if conf['palette-size'] < 256:
+ opts += ' -colors '+str(conf['palette-size'])
+ (status, output) = self.get_cmd_output(
+ 'convert "%s" %s "%s"' % ( img_name_png, opts, img_name )
+ )
+ if status != 0:
+ inkex.errormsg('Upss... ImageMagick error: '+output)
+ os.remove(img_name_png)
+
+
+ _html = {}
+ def reg_html(self, el_tag, el):
+ parent = self.getParentNode( el )
+ parent_id = self.get_el_conf( parent )['html-id']
+ if parent == self.get_slicer_layer(): parent_id = 'body'
+ conf = self.get_el_conf( el )
+ el_id = conf['html-id']
+ if 'html-class' in conf:
+ el_class = conf['html-class']
+ else:
+ el_class = ''
+ if not parent_id in self._html: self._html[parent_id] = []
+ self._html[parent_id].append({ 'tag':el_tag, 'id':el_id, 'class':el_class })
+
+
+ def html_code(self, parent='body', ident=' '):
+ #inkex.errormsg( self._html )
+ if not parent in self._html: return ''
+ code = ''
+ for el in self._html[parent]:
+ child_code = self.html_code(el['id'], ident+' ')
+ tag_class = ''
+ if el['class'] != '': tag_class = ' class="'+el['class']+'"'
+ if el['tag'] == 'img':
+ code += ident+'<img id="'+el['id']+'"'+tag_class+\
+ ' src="'+self.img_name(el, self.get_el_conf(el))+'"/>\n'
+ else:
+ code += ident+'<'+el['tag']+' id="'+el['id']+'"'+tag_class+'>\n'
+ if child_code:
+ code += child_code
+ else:
+ code += ident+' Element '+el['id']+'\n'
+ code += ident+'</'+el['tag']+'><!-- id="'+el['id']+'" -->\n'
+ return code
+
+
+ _css = []
+ def reg_css(self, selector, att, val):
+ pos = i = -1
+ for s in self._css:
+ i += 1
+ if s['selector'] == selector: pos = i
+ if pos == -1:
+ pos = i + 1
+ self._css.append({ 'selector':selector, 'atts':{} })
+ if not att in self._css[pos]['atts']: self._css[pos]['atts'][att] = []
+ self._css[pos]['atts'][att].append( val )
+
+
+ def css_code(self):
+ code = ''
+ for s in self._css:
+ code += '\n'+s['selector']+' {\n'
+ for att in s['atts']:
+ val = s['atts'][att]
+ if att == 'background' and len(val) > 1:
+ code += ' /* the next attribute needs a CSS3 enabled browser */\n'
+ code += ' '+ att +': '+ (', '.join(val)) +';\n'
+ code += '}\n'
+ return code
+
+
+ def output(self):
+ # Cancel document serialization to stdout
+ pass
if __name__ == '__main__':