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 / inkex.py < prev    next >
Text File  |  2011-07-08  |  8KB  |  236 lines

  1. #!/usr/bin/env python
  2. """
  3. inkex.py
  4. A helper module for creating Inkscape extensions
  5.  
  6. Copyright (C) 2005,2007 Aaron Spike, aaron@ekips.org
  7.  
  8. This program is free software; you can redistribute it and/or modify
  9. it under the terms of the GNU General Public License as published by
  10. the Free Software Foundation; either version 2 of the License, or
  11. (at your option) any later version.
  12.  
  13. This program is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. GNU General Public License for more details.
  17.  
  18. You should have received a copy of the GNU General Public License
  19. along with this program; if not, write to the Free Software
  20. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21. """
  22. import sys, copy, optparse, random, re
  23. import gettext
  24. from math import *
  25. _ = gettext.gettext
  26.  
  27. #a dictionary of all of the xmlns prefixes in a standard inkscape doc
  28. NSS = {
  29. u'sodipodi' :u'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd',
  30. u'cc'       :u'http://creativecommons.org/ns#',
  31. u'ccOLD'    :u'http://web.resource.org/cc/',
  32. u'svg'      :u'http://www.w3.org/2000/svg',
  33. u'dc'       :u'http://purl.org/dc/elements/1.1/',
  34. u'rdf'      :u'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
  35. u'inkscape' :u'http://www.inkscape.org/namespaces/inkscape',
  36. u'xlink'    :u'http://www.w3.org/1999/xlink',
  37. u'xml'      :u'http://www.w3.org/XML/1998/namespace'
  38. }
  39.  
  40. #a dictionary of unit to user unit conversion factors
  41. uuconv = {'in':90.0, 'pt':1.25, 'px':1, 'mm':3.5433070866, 'cm':35.433070866, 'm':3543.3070866,
  42.           'km':3543307.0866, 'pc':15.0, 'yd':3240 , 'ft':1080}
  43. def unittouu(string):
  44.     '''Returns userunits given a string representation of units in another system'''
  45.     unit = re.compile('(%s)$' % '|'.join(uuconv.keys()))
  46.     param = re.compile(r'(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)')
  47.  
  48.     p = param.match(string)
  49.     u = unit.search(string)    
  50.     if p:
  51.         retval = float(p.string[p.start():p.end()])
  52.     else:
  53.         retval = 0.0
  54.     if u:
  55.         try:
  56.             return retval * uuconv[u.string[u.start():u.end()]]
  57.         except KeyError:
  58.             pass
  59.     return retval
  60.  
  61. def uutounit(val, unit):
  62.     return val/uuconv[unit]
  63.  
  64. try:
  65.     from lxml import etree
  66. except:
  67.     sys.exit(_('The fantastic lxml wrapper for libxml2 is required by inkex.py and therefore this extension. Please download and install the latest version from http://cheeseshop.python.org/pypi/lxml/, or install it through your package manager by a command like: sudo apt-get install python-lxml'))
  68.  
  69. def debug(what):
  70.     sys.stderr.write(str(what) + "\n")
  71.     return what
  72.  
  73. def errormsg(msg):
  74.     """Intended for end-user-visible error messages.
  75.     
  76.        (Currently just writes to stderr with an appended newline, but could do
  77.        something better in future: e.g. could add markup to distinguish error
  78.        messages from status messages or debugging output.)
  79.       
  80.        Note that this should always be combined with translation:
  81.  
  82.          import gettext
  83.          _ = gettext.gettext
  84.          ...
  85.          inkex.errormsg(_("This extension requires two selected paths."))
  86.     """
  87.     sys.stderr.write((unicode(msg) + "\n").encode("UTF-8"))
  88.  
  89. def check_inkbool(option, opt, value):
  90.     if str(value).capitalize() == 'True':
  91.         return True
  92.     elif str(value).capitalize() == 'False':
  93.         return False
  94.     else:
  95.         raise optparse.OptionValueError("option %s: invalid inkbool value: %s" % (opt, value))
  96.  
  97. def addNS(tag, ns=None):
  98.     val = tag
  99.     if ns!=None and len(ns)>0 and NSS.has_key(ns) and len(tag)>0 and tag[0]!='{':
  100.         val = "{%s}%s" % (NSS[ns], tag)
  101.     return val
  102.  
  103. class InkOption(optparse.Option):
  104.     TYPES = optparse.Option.TYPES + ("inkbool",)
  105.     TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
  106.     TYPE_CHECKER["inkbool"] = check_inkbool
  107.  
  108. class Effect:
  109.     """A class for creating Inkscape SVG Effects"""
  110.  
  111.     def __init__(self, *args, **kwargs):
  112.         self.document=None
  113.         self.ctx=None
  114.         self.selected={}
  115.         self.doc_ids={}
  116.         self.options=None
  117.         self.args=None
  118.         self.OptionParser = optparse.OptionParser(usage="usage: %prog [options] SVGfile",option_class=InkOption)
  119.         self.OptionParser.add_option("--id",
  120.                         action="append", type="string", dest="ids", default=[], 
  121.                         help="id attribute of object to manipulate")
  122.  
  123.     def effect(self):
  124.         pass
  125.  
  126.     def getoptions(self,args=sys.argv[1:]):
  127.         """Collect command line arguments"""
  128.         self.options, self.args = self.OptionParser.parse_args(args)
  129.  
  130.     def parse(self,file=None):
  131.         """Parse document in specified file or on stdin"""
  132.         try:
  133.             try:
  134.                 stream = open(file,'r')
  135.             except:
  136.                 stream = open(self.svg_file,'r')
  137.         except:
  138.             stream = sys.stdin
  139.         self.document = etree.parse(stream)
  140.         stream.close()
  141.  
  142.     def getposinlayer(self):
  143.         #defaults
  144.         self.current_layer = self.document.getroot()
  145.         self.view_center = (0.0,0.0)
  146.  
  147.         layerattr = self.document.xpath('//sodipodi:namedview/@inkscape:current-layer', namespaces=NSS)
  148.         if layerattr:
  149.             layername = layerattr[0]
  150.             layer = self.document.xpath('//svg:g[@id="%s"]' % layername, namespaces=NSS)
  151.             if layer:
  152.                 self.current_layer = layer[0]
  153.  
  154.         xattr = self.document.xpath('//sodipodi:namedview/@inkscape:cx', namespaces=NSS)
  155.         yattr = self.document.xpath('//sodipodi:namedview/@inkscape:cy', namespaces=NSS)
  156.         doc_height = unittouu(self.document.getroot().get('height'))
  157.         if xattr and yattr:
  158.             x = xattr[0]
  159.             y = yattr[0]
  160.             if x and y:
  161.                 self.view_center = (float(x), doc_height - float(y)) # FIXME: y-coordinate flip, eliminate it when it's gone in Inkscape
  162.  
  163.     def getselected(self):
  164.         """Collect selected nodes"""
  165.         for i in self.options.ids:
  166.             path = '//*[@id="%s"]' % i
  167.             for node in self.document.xpath(path, namespaces=NSS):
  168.                 self.selected[i] = node
  169.  
  170.     def getElementById(self, id):
  171.         path = '//*[@id="%s"]' % id
  172.         el_list = self.document.xpath(path, namespaces=NSS)
  173.         if el_list:
  174.           return el_list[0]
  175.         else:
  176.           return None
  177.  
  178.     def getParentNode(self, node):
  179.         for parent in self.document.getiterator():
  180.             if node in parent.getchildren():
  181.                 return parent
  182.                 break
  183.  
  184.  
  185.     def getdocids(self):
  186.         docIdNodes = self.document.xpath('//@id', namespaces=NSS)
  187.         for m in docIdNodes:
  188.             self.doc_ids[m] = 1
  189.  
  190.     def getNamedView(self):
  191.         return self.document.xpath('//sodipodi:namedview', namespaces=NSS)[0]
  192.  
  193.     def createGuide(self, posX, posY, angle):
  194.         atts = {
  195.           'position': str(posX)+','+str(posY),
  196.           'orientation': str(sin(radians(angle)))+','+str(-cos(radians(angle)))
  197.           }
  198.         guide = etree.SubElement(
  199.                   self.getNamedView(),
  200.                   addNS('guide','sodipodi'), atts )
  201.         return guide
  202.  
  203.     def output(self):
  204.         """Serialize document into XML on stdout"""
  205.         self.document.write(sys.stdout)
  206.  
  207.     def affect(self, args=sys.argv[1:], output=True):
  208.         """Affect an SVG document with a callback effect"""
  209.         self.svg_file = args[-1]
  210.         self.getoptions(args)
  211.         self.parse()
  212.         self.getposinlayer()
  213.         self.getselected()
  214.         self.getdocids()
  215.         self.effect()
  216.         if output: self.output()
  217.  
  218.     def uniqueId(self, old_id, make_new_id = True):
  219.         new_id = old_id
  220.         if make_new_id:
  221.             while new_id in self.doc_ids:
  222.                 new_id += random.choice('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
  223.             self.doc_ids[new_id] = 1
  224.         return new_id
  225.  
  226.     def xpathSingle(self, path):
  227.         try:
  228.             retval = self.document.xpath(path, namespaces=NSS)[0]
  229.         except:
  230.             errormsg(_("No matching node for expression: %s") % path)
  231.             retval = None
  232.         return retval
  233.             
  234.  
  235. # vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99
  236.