# -*- coding: utf-8 -*-
#!/usr/bin/env python
# Copyright (C) 2008 Eben Eliason
# Copyright (C) 2013 Jorge Alberto Gómez López
# 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 3 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, see .
import sys
import xml.dom.minidom
import getopt
import re
import os
import string
HELP = '''\nUsage: sugar-iconify.py [options] input.svg\n
Options:\n
-c\t\tApply default color entities (#666666, #ffffff) to output
-d directory\tThe preferred output directory
-e\t\tDo not insert entities for strokes and fills
-f hex\t\tHex value to replace with fill entity
-g\t\tAutomatically accept guesses for stroke and fill entities
-h\t\tDisplay this help message
-i\t\tInsert "isolated stroke" entities
-m\t\tMultiple export; export top level groups as separate icons
-o\t\tOverwrite the input file; overridden by -m
-p pattern\tOnly export icons whose name matches pattern; for use with -m
-s hex\t\tHex value to replace with stroke entity
-x\t\tOutput example SVGs, for previewing their appearance in Sugar;
\t\tignored with -m
-v\t\tverbose'''
class SugarIconify():
def __init__(self, command_line=False):
# Declare variables and constants
self.default_stroke_color = '#666666'
self.default_fill_color = '#ffffff'
self.transparent_color = '#00000000'
self.stroke_color = self.default_stroke_color
self.fill_color = self.default_fill_color
self._target_stroke = None
self._target_fill = None
self.stroke_entity = 'stroke_color'
self.fill_entity = 'fill_color'
self.iso_stroke_entity = 'iso_stroke_color'
self.output_path = ''
self.pattern = ''
self.entities_passed = 0
self.use_default_colors = False
self.confirm_guess = True
self.use_entities = True
self.multiple = False
self.verbose = False
self.overwrite_input = False
self.output_examples = False
self.use_iso_strokes = False
if command_line:
self._parse_command_line()
def usage(self):
# Define help output
print HELP
def _parse_command_line(self):
''' Try to make sense of the command-line arguments. '''
try:
opts, arg = getopt.getopt(sys.argv[1:], 's:f:gcd:imp:oehvx',
['stroke=', 'fill=', 'guess', 'help',
'overwrite', 'verbose'])
except:
self.usage()
sys.exit(2)
if len(arg) < 1:
self.usage()
sys.exit(2)
# Interpret arguments
for o, a in opts:
if o in ['-s', '--stroke']:
self.set_stroke_color(a)
elif o in ['-f', '--fill']:
self.set_fill_color(a)
elif o in ['-g', '--guess']:
self.set_confirm_guess(False)
elif o == '-c':
self.set_use_default_colors(True)
elif o in ['-o', '--overwrite']:
self.set_overwrite_input(True)
elif o == '-d':
self.set_output_path(a)
elif o == '-e':
self.set_use_entities(False)
elif o in ['-v', 'verbose']:
self.set_verbose(True)
elif o == '-p':
self.set_pattern(a)
elif o in ['-h', '--help']:
usage()
sys.exit(2)
elif o == '-m':
self.set_multiple(True)
elif o == '-x':
self.set_output_examples(True)
elif o == '-i':
self.set_use_iso_strokes(True)
self.iconify(arg[0])
def rgb_to_hex(self, rgb_str):
s = re.sub(r'.*rgb\(([^)]*).*', r'\1', rgb_str)
percent_list = s.split(',')
hex_str = '#'
for value in percent_list:
hex_str += self.percent_to_hex(value)
return hex_str
def percent_to_hex(self, num):
number = float(num.strip()[:-1])
decimal = (number*255)/100
decimal = int(round(decimal, 0))
hex_val = hex(decimal).split('x')[1]
if len(hex_val) == 1:
hex_val = '0' + hex_val
return hex_val
def set_stroke_color(self, s=None):
if s is not None:
if 'rgb' in s.lower():
self.stroke_color = self.rgb_to_hex(s)
self._target_stroke = s
else:
self.stroke_color = '#' + s.lstrip('#').lower()
self.entities_passed += 1
def set_fill_color(self, f=None):
if f is not None:
if 'rgb' in f.lower():
self.fill_color = self.rgb_to_hex(f)
self._target_fill = f
else:
self.fill_color = '#' + f.lstrip('#').lower()
self.entities_passed += 1
def set_confirm_guess(self, g=False):
self.confirm_guess = g
def set_use_default_colors(self, c=False):
self.use_default_colors = c
def set_overwrite_input(self, o=False):
self.overwrite_input = o
def set_output_path(self, d=None):
if d is not None:
self.output_path = d.rstrip('/') + '/'
def set_use_entity(self, e=False):
self.use_entities = e
def set_verbose(self, v=False):
self.verbose = v
def set_pattern(self, p=None):
if p is not None:
self.pattern = p
def set_multiple(self, m=False):
self.multiple = m
def set_output_examples(self, x=False):
self.output_examples = x
def set_use_iso_strokes(self, i=False):
self.use_iso_strokes = i
def iconify(self, file_path):
# Isolate important parts of the input path
self.svgfilepath = file_path
self.svgdirpath, self.sep, self.svgfilename = \
self.svgfilepath.rpartition('/')
svgbasename = re.sub(r'(.*)\.([^.]+)', r'\1', self.svgfilename)
# Load the SVG as text
try:
self.svgfile = open(self.svgfilepath, 'r')
except:
sys.exit('Error: Could not locate ' + self.svgfilepath)
try:
self.svgtext = self.svgfile.read()
self.svgfile.close()
except:
self.svgfile.close()
sys.exit('Error: Could not read ' + self.svgfilepath)
# Determine the creator of the SVG (we only care about
# Inkscape and Illustrator)
self.creator = 'unknown'
if re.search('illustrator', self.svgtext, re.I):
self.creator = 'illustrator'
elif re.search('inkscape', self.svgtext, re.I):
self.creator = 'inkscape'
if self.verbose:
print 'The self.creator of this svg is ' + self.creator + '.'
# Hack the entities into the readonly DTD
if self.use_entities:
# Before replacing them, we read the stroke/fill values
# out, should they have previously been defined, to prevent
# needing to make guesses for them later
self.stroke_match = re.search(r'stroke_color\s*\"([^"]*)\"',
self.svgtext)
self.fill_match = re.search(r'fill_color\s*\"([^"]*)\"',
self.svgtext)
if self.stroke_match is not None:
self.stroke_color = self.stroke_match.group(1).lower()
self.entities_passed += 1
if self.fill_match is not None:
self.fill_color = self.fill_match.group(1).lower()
self.entities_passed += 1
# Define the entities
if self.fill_match and self.stroke_match:
self.entities = '\t\n'
self.entities += '\t\n'
if self.use_iso_strokes:
self.entities += '\t\n'
else:
self.entities = '\t\n'
self.entities += '\t\n'
if self.use_iso_strokes:
self.entities += '\t\n'
# For simplicity, we simply replace the entire entity
# declaration block; this obviously would remove any other
# custom self.entities declared within the SVG, but we
# assume that's an extreme edge case
self.svgtext, self.n = \
re.subn(r'(\[]*)(\[[^\]]*\])*\>',
r'\1 \n[\n' + self.entities + ']>\n', self.svgtext)
# Add a doctype if none already exists, adding the
# appropriate self.entities as well
if self.n == 0:
self.svgtext, self.n = \
re.subn('