home *** CD-ROM | disk | FTP | other *** search
/ Chip 2011 November / CHIP_2011_11.iso / Programy / Narzedzia / Inkscape / Inkscape-0.48.2-1-win32.exe / share / extensions / webslicer_export.py < prev    next >
Text File  |  2011-07-08  |  18KB  |  438 lines

  1. #!/usr/bin/env python
  2. '''
  3. Copyright (C) 2010 Aurelio A. Heckert, aurium (a) gmail dot com
  4.  
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18. '''
  19.  
  20. from webslicer_effect import *
  21. import inkex
  22. import gettext
  23. import os
  24. import sys
  25. import tempfile
  26.  
  27. _ = gettext.gettext
  28.  
  29.  
  30. class WebSlicer_Export(WebSlicer_Effect):
  31.  
  32.     def __init__(self):
  33.         WebSlicer_Effect.__init__(self)
  34.         self.OptionParser.add_option("--tab")
  35.         self.OptionParser.add_option("--dir",
  36.                                      action="store", type="string",
  37.                                      dest="dir",
  38.                                      help="")
  39.         self.OptionParser.add_option("--create-dir",
  40.                                      action="store", type="inkbool",
  41.                                      default=False,
  42.                                      dest="create_dir",
  43.                                      help="")
  44.         self.OptionParser.add_option("--with-code",
  45.                                      action="store", type="inkbool",
  46.                                      default=False,
  47.                                      dest="with_code",
  48.                                      help="")
  49.  
  50.  
  51.     svgNS = '{http://www.w3.org/2000/svg}'
  52.  
  53.  
  54.     def validate_inputs(self):
  55.         # The user must supply a directory to export:
  56.         if is_empty( self.options.dir ):
  57.             inkex.errormsg(_('You must to give a directory to export the slices.'))
  58.             return {'error':'You must to give a directory to export the slices.'}
  59.         # No directory separator at the path end:
  60.         if self.options.dir[-1] == '/' or self.options.dir[-1] == '\\':
  61.             self.options.dir = self.options.dir[0:-1]
  62.         # Test if the directory exists:
  63.         if not os.path.exists( self.options.dir ):
  64.             if self.options.create_dir:
  65.                 # Try to create it:
  66.                 try:
  67.                     os.makedirs( self.options.dir )
  68.                 except Exception, e:
  69.                     inkex.errormsg( _('Can\'t create "%s".') % self.options.dir )
  70.                     inkex.errormsg( _('Error: %s') % e )
  71.                     return {'error':'Can\'t create the directory to export.'}
  72.             else:
  73.                 inkex.errormsg(_('The directory "%s" does not exists.') % self.options.dir)
  74.                 return
  75.         self.unique_html_id( self.get_slicer_layer() )
  76.         return None
  77.  
  78.  
  79.     def get_cmd_output(self, cmd):
  80.         # This solution comes from Andrew Reedick <jr9445 at ATT.COM>
  81.         # http://mail.python.org/pipermail/python-win32/2008-January/006606.html
  82.         # This method replaces the commands.getstatusoutput() usage, with the
  83.         # hope to correct the windows exporting bug:
  84.         # https://bugs.launchpad.net/inkscape/+bug/563722
  85.         if sys.platform != "win32": cmd = '{ '+ cmd +'; }'
  86.         pipe = os.popen(cmd +' 2>&1', 'r')
  87.         text = pipe.read()
  88.         sts = pipe.close()
  89.         if sts is None: sts = 0
  90.         if text[-1:] == '\n': text = text[:-1]
  91.         return sts, text
  92.  
  93.  
  94.     _html_ids = []
  95.     def unique_html_id(self, el):
  96.         for child in el.getchildren():
  97.             if child.tag in [ self.svgNS+'rect', self.svgNS+'path',
  98.                               self.svgNS+'circle', self.svgNS+'g' ]:
  99.                 conf = self.get_el_conf(child)
  100.                 if conf['html-id'] in self._html_ids:
  101.                     inkex.errormsg(
  102.                         _('You have more than one element with "%s" html-id.') %
  103.                         conf['html-id'] )
  104.                     n = 2
  105.                     while (conf['html-id']+"-"+str(n)) in self._html_ids: n += 1
  106.                     conf['html-id'] += "-"+str(n)
  107.                 self._html_ids.append( conf['html-id'] )
  108.                 self.save_conf( conf, child )
  109.                 self.unique_html_id( child )
  110.  
  111.  
  112.     def test_if_has_imagemagick(self):
  113.         (status, output) = self.get_cmd_output('convert --version')
  114.         self.has_magick = ( status == 0 and 'ImageMagick' in output )
  115.  
  116.  
  117.     def effect(self):
  118.         self.test_if_has_imagemagick()
  119.         error = self.validate_inputs()
  120.         if error: return error
  121.         # Register the basic CSS code:
  122.         self.reg_css( 'body', 'text-align', 'center' )
  123.         # Create the temporary SVG with invisible Slicer layer to export image pieces
  124.         self.create_the_temporary_svg()
  125.         # Start what we really want!
  126.         self.export_chids_of( self.get_slicer_layer() )
  127.         # Write the HTML and CSS files if asked for:
  128.         if self.options.with_code:
  129.             self.make_html_file()
  130.             self.make_css_file()
  131.         # Delete the temporary SVG with invisible Slicer layer
  132.         self.delete_the_temporary_svg()
  133.         #TODO: prevent inkex to return svg code to update Inkscape
  134.  
  135.  
  136.     def make_html_file(self):
  137.         f = open(os.path.join(self.options.dir,'layout.html'), 'w')
  138.         f.write(
  139.             '<html>\n<head>\n'                                     +\
  140.             '  <title>Web Layout Testing</title>\n'                +\
  141.             '  <style type="text/css" media="screen">\n'           +\
  142.             '    @import url("style.css")\n'                       +\
  143.             '  </style>\n'                                         +\
  144.             '</head>\n<body>\n'                                    +\
  145.             self.html_code()                                       +\
  146.             '  <p style="position:absolute; bottom:10px">\n'       +\
  147.             '  This HTML code is not done to the web. <br/>\n'     +\
  148.             '  The automatic HTML and CSS code are only a helper.' +\
  149.             '</p>\n</body>\n</html>' )
  150.         f.close()
  151.  
  152.  
  153.     def make_css_file(self):
  154.         f = open(os.path.join(self.options.dir,'style.css'), 'w')
  155.         f.write(
  156.             '/*\n'                                                    +\
  157.             '** This CSS code is not done to the web.\n'              +\
  158.             '** The automatic HTML and CSS code are only a helper.\n' +\
  159.             '*/\n'                                                    +\
  160.             self.css_code() )
  161.         f.close()
  162.  
  163.  
  164.     def create_the_temporary_svg(self):
  165.         (ref, self.tmp_svg) = tempfile.mkstemp('.svg')
  166.         layer = self.get_slicer_layer()
  167.         current_style = ('style' in layer.attrib) and layer.attrib['style'] or ''
  168.         layer.attrib['style'] = 'display:none'
  169.         self.document.write( self.tmp_svg );
  170.         layer.attrib['style'] = current_style
  171.  
  172.  
  173.     def delete_the_temporary_svg(self):
  174.         os.remove( self.tmp_svg )
  175.  
  176.  
  177.     noid_element_count = 0
  178.     def get_el_conf(self, el):
  179.         desc = el.find(self.svgNS+'desc')
  180.         conf = {}
  181.         if desc is None:
  182.             desc = inkex.etree.SubElement(el, 'desc')
  183.         if desc.text is None:
  184.             desc.text = ''
  185.         for line in desc.text.split("\n"):
  186.             if line.find(':') > 0:
  187.                 line = line.split(':')
  188.                 conf[line[0].strip()] = line[1].strip()
  189.         if not 'html-id' in conf:
  190.             if el == self.get_slicer_layer():
  191.                 return {'html-id':'#body#'}
  192.             else:
  193.                 self.noid_element_count += 1
  194.                 conf['html-id'] = 'element-'+str(self.noid_element_count)
  195.                 desc.text += "\nhtml-id:"+conf['html-id']
  196.         return conf
  197.  
  198.  
  199.     def save_conf(self, conf, el):
  200.         desc = el.find('{http://www.w3.org/2000/svg}desc')
  201.         if desc is not None:
  202.             conf_a = []
  203.             for k in conf:
  204.                 conf_a.append( k+' : '+conf[k] )
  205.             desc.text = "\n".join(conf_a)
  206.  
  207.  
  208.     def export_chids_of(self, parent):
  209.         parent_id = self.get_el_conf( parent )['html-id']
  210.         for el in parent.getchildren():
  211.             el_conf = self.get_el_conf( el )
  212.             if el.tag == self.svgNS+'g':
  213.                 if self.options.with_code:
  214.                     self.register_group_code( el, el_conf )
  215.                 else:
  216.                     self.export_chids_of( el )
  217.             if el.tag in [ self.svgNS+'rect', self.svgNS+'path', self.svgNS+'circle' ]:
  218.                 if self.options.with_code:
  219.                     self.register_unity_code( el, el_conf, parent_id )
  220.                 self.export_img( el, el_conf )
  221.  
  222.  
  223.     def register_group_code(self, group, conf):
  224.         self.reg_html('div', group)
  225.         selec = '#'+conf['html-id']
  226.         self.reg_css( selec, 'position', 'absolute' )
  227.         geometry = self.get_relative_el_geometry(group)
  228.         self.reg_css( selec, 'top', str(int(geometry['y']))+'px' )
  229.         self.reg_css( selec, 'left', str(int(geometry['x']))+'px' )
  230.         self.reg_css( selec, 'width', str(int(geometry['w']))+'px' )
  231.         self.reg_css( selec, 'height', str(int(geometry['h']))+'px' )
  232.         self.export_chids_of( group )
  233.  
  234.  
  235.     def __validate_slice_conf(self, conf):
  236.         if not 'layout-disposition' in conf:
  237.             conf['layout-disposition'] = 'bg-el-norepeat'
  238.         if not 'layout-position-anchor' in conf:
  239.             conf['layout-position-anchor'] = 'mc'
  240.         return conf
  241.  
  242.  
  243.     def register_unity_code(self, el, conf, parent_id):
  244.         conf = self.__validate_slice_conf(conf)
  245.         css_selector = '#'+conf['html-id']
  246.         bg_repeat = 'no-repeat'
  247.         img_name = self.img_name(el, conf)
  248.         if conf['layout-disposition'][0:2] == 'bg':
  249.             if conf['layout-disposition'][0:9] == 'bg-parent':
  250.                 if parent_id == '#body#':
  251.                     css_selector = 'body'
  252.                 else:
  253.                     css_selector = '#'+parent_id
  254.                 if conf['layout-disposition'] == 'bg-parent-repeat':
  255.                     bg_repeat = 'repeat'
  256.                 if conf['layout-disposition'] == 'bg-parent-repeat-x':
  257.                     bg_repeat = 'repeat-x'
  258.                 if conf['layout-disposition'] == 'bg-parent-repeat-y':
  259.                     bg_repeat = 'repeat-y'
  260.                 lay_anchor = conf['layout-position-anchor']
  261.                 if lay_anchor == 'tl': lay_anchor = 'top left'
  262.                 if lay_anchor == 'tc': lay_anchor = 'top center'
  263.                 if lay_anchor == 'tr': lay_anchor = 'top right'
  264.                 if lay_anchor == 'ml': lay_anchor = 'middle left'
  265.                 if lay_anchor == 'mc': lay_anchor = 'middle center'
  266.                 if lay_anchor == 'mr': lay_anchor = 'middle right'
  267.                 if lay_anchor == 'bl': lay_anchor = 'bottom left'
  268.                 if lay_anchor == 'bc': lay_anchor = 'bottom center'
  269.                 if lay_anchor == 'br': lay_anchor = 'bottom right'
  270.                 self.reg_css( css_selector, 'background',
  271.                               'url("%s") %s %s' % (img_name, bg_repeat, lay_anchor) )
  272.             else: # conf['layout-disposition'][0:9] == 'bg-el...'
  273.                 self.reg_html('div', el)
  274.                 self.reg_css( css_selector, 'background',
  275.                               'url("%s") %s' % (img_name, 'no-repeat') )
  276.                 self.reg_css( css_selector, 'position', 'absolute' )
  277.                 geo = self.get_relative_el_geometry(el,True)
  278.                 self.reg_css( css_selector, 'top', geo['y'] )
  279.                 self.reg_css( css_selector, 'left', geo['x'] )
  280.                 self.reg_css( css_selector, 'width', geo['w'] )
  281.                 self.reg_css( css_selector, 'height', geo['h'] )
  282.         else: # conf['layout-disposition'] == 'img...'
  283.                 self.reg_html('img', el)
  284.                 if conf['layout-disposition'] == 'img-pos':
  285.                     self.reg_css( css_selector, 'position', 'absolute' )
  286.                     geo = self.get_relative_el_geometry(el)
  287.                     self.reg_css( css_selector, 'left', str(geo['x'])+'px' )
  288.                     self.reg_css( css_selector, 'top', str(geo['y'])+'px' )
  289.                 if conf['layout-disposition'] == 'img-float-left':
  290.                     self.reg_css( css_selector, 'float', 'right' )
  291.                 if conf['layout-disposition'] == 'img-float-right':
  292.                     self.reg_css( css_selector, 'float', 'right' )
  293.  
  294.  
  295.     el_geo = { }
  296.     def register_all_els_geometry(self):
  297.         ink_cmm = 'inkscape --query-all '+self.tmp_svg
  298.         (status, output) = self.get_cmd_output( ink_cmm )
  299.         self.el_geo = { }
  300.         if status == 0:
  301.             for el in output.split('\n'):
  302.                 el = el.split(',')
  303.                 if len(el) == 5:
  304.                     self.el_geo[el[0]] = { 'x':float(el[1]), 'y':float(el[2]),
  305.                                            'w':float(el[3]), 'h':float(el[4]) }
  306.         doc_w = inkex.unittouu( self.document.getroot().get('width') )
  307.         doc_h = inkex.unittouu( self.document.getroot().get('height') )
  308.         self.el_geo['webslicer-layer'] = { 'x':0, 'y':0, 'w':doc_w, 'h':doc_h }
  309.  
  310.  
  311.     def get_relative_el_geometry(self, el, value_to_css=False):
  312.         # This method return a dictionary with x, y, w and h keys.
  313.         # All values are float, if value_to_css is False, otherwise
  314.         # that is a string ended with "px". The x and y values are
  315.         # relative to parent position.
  316.         if not self.el_geo: self.register_all_els_geometry()
  317.         parent = self.getParentNode(el)
  318.         geometry = self.el_geo[el.attrib['id']]
  319.         geometry['x'] -= self.el_geo[parent.attrib['id']]['x']
  320.         geometry['y'] -= self.el_geo[parent.attrib['id']]['y']
  321.         if value_to_css:
  322.             for k in geometry: geometry[k] = str(int(geometry[k]))+'px'
  323.         return geometry
  324.  
  325.  
  326.     def img_name(self, el, conf):
  327.         return el.attrib['id']+'.'+conf['format']
  328.  
  329.  
  330.     def export_img(self, el, conf):
  331.         if not self.has_magick:
  332.             inkex.errormsg(_('You must install the ImageMagick to get JPG and GIF.'))
  333.             conf['format'] = 'png'
  334.         img_name = os.path.join( self.options.dir, self.img_name(el, conf) )
  335.         img_name_png = img_name
  336.         if conf['format'] != 'png':
  337.             img_name_png = img_name+'.png'
  338.         opts = ''
  339.         if 'bg-color' in conf: opts += ' -b "'+conf['bg-color']+'" -y 1'
  340.         if 'dpi' in conf: opts += ' -d '+conf['dpi']
  341.         if 'dimension' in conf:
  342.             dim = conf['dimension'].split('x')
  343.             opts += ' -w '+dim[0]+' -h '+dim[1]
  344.         (status, output) = self.get_cmd_output(
  345.             'inkscape %s -i "%s" -e "%s" "%s"' % (
  346.                 opts, el.attrib['id'], img_name_png, self.tmp_svg
  347.             )
  348.         )
  349.         if conf['format'] != 'png':
  350.             opts = ''
  351.             if conf['format'] == 'jpg':
  352.                 opts += ' -quality '+str(conf['quality'])
  353.             if conf['format'] == 'gif':
  354.                 if conf['gif-type'] == 'grayscale':
  355.                     opts += ' -type Grayscale'
  356.                 else:
  357.                     opts += ' -type Palette'
  358.                 if conf['palette-size'] < 256:
  359.                     opts += ' -colors '+str(conf['palette-size'])
  360.             (status, output) = self.get_cmd_output(
  361.                 'convert "%s" %s "%s"' % ( img_name_png, opts, img_name )
  362.             )
  363.             if status != 0:
  364.                 inkex.errormsg('Upss... ImageMagick error: '+output)
  365.             os.remove(img_name_png)
  366.  
  367.  
  368.     _html = {}
  369.     def reg_html(self, el_tag, el):
  370.         parent = self.getParentNode( el )
  371.         parent_id = self.get_el_conf( parent )['html-id']
  372.         if parent == self.get_slicer_layer(): parent_id = 'body'
  373.         conf = self.get_el_conf( el )
  374.         el_id = conf['html-id']
  375.         if 'html-class' in conf:
  376.           el_class = conf['html-class']
  377.         else:
  378.           el_class = ''
  379.         if not parent_id in self._html: self._html[parent_id] = []
  380.         self._html[parent_id].append({ 'tag':el_tag, 'id':el_id, 'class':el_class })
  381.  
  382.  
  383.     def html_code(self, parent='body', ident='  '):
  384.         #inkex.errormsg( self._html )
  385.         if not parent in self._html: return ''
  386.         code = ''
  387.         for el in self._html[parent]:
  388.             child_code = self.html_code(el['id'], ident+'  ')
  389.             tag_class = ''
  390.             if el['class'] != '': tag_class = ' class="'+el['class']+'"'
  391.             if el['tag'] == 'img':
  392.                 code += ident+'<img id="'+el['id']+'"'+tag_class+\
  393.                         ' src="'+self.img_name(el, self.get_el_conf(el))+'"/>\n'
  394.             else:
  395.                 code += ident+'<'+el['tag']+' id="'+el['id']+'"'+tag_class+'>\n'
  396.                 if child_code:
  397.                     code += child_code
  398.                 else:
  399.                     code += ident+'  Element '+el['id']+'\n'
  400.                 code += ident+'</'+el['tag']+'><!-- id="'+el['id']+'" -->\n'
  401.         return code
  402.  
  403.  
  404.     _css = []
  405.     def reg_css(self, selector, att, val):
  406.         pos = i = -1
  407.         for s in self._css:
  408.             i += 1
  409.             if s['selector'] == selector: pos = i
  410.         if pos == -1:
  411.             pos = i + 1
  412.             self._css.append({ 'selector':selector, 'atts':{} })
  413.         if not att in self._css[pos]['atts']: self._css[pos]['atts'][att] = []
  414.         self._css[pos]['atts'][att].append( val )
  415.  
  416.  
  417.     def css_code(self):
  418.         code = ''
  419.         for s in self._css:
  420.             code += '\n'+s['selector']+' {\n'
  421.             for att in s['atts']:
  422.                 val = s['atts'][att]
  423.                 if att == 'background' and len(val) > 1:
  424.                     code += '  /* the next attribute needs a CSS3 enabled browser */\n'
  425.                 code += '  '+ att +': '+ (', '.join(val)) +';\n'
  426.             code += '}\n'
  427.         return code
  428.  
  429.  
  430.     def output(self):
  431.         # Cancel document serialization to stdout
  432.         pass
  433.  
  434.  
  435. if __name__ == '__main__':
  436.     e = WebSlicer_Export()
  437.     e.affect()
  438.