diff options
| author | Alvin Penner <penner@vaxxine.com> | 2009-12-19 13:45:36 +0000 |
|---|---|---|
| committer | Alvin Penner <penner@vaxxine.com> | 2009-12-19 13:45:36 +0000 |
| commit | a19a8eab4a74ded5007a23c3edff81ff2839b42c (patch) | |
| tree | 805318bf68882fe9085123cccd69219da2025e74 | |
| parent | 2nd patch of bug 488862. Also committed upstream in 2geom. (diff) | |
| download | inkscape-a19a8eab4a74ded5007a23c3edff81ff2839b42c.tar.gz inkscape-a19a8eab4a74ded5007a23c3edff81ff2839b42c.zip | |
modify exit message if non-Ascii characters
Fixed bugs:
- https://launchpad.net/bugs/424162
(bzr r8894)
| -rw-r--r-- | share/extensions/render_alphabetsoup.py | 924 |
1 files changed, 463 insertions, 461 deletions
diff --git a/share/extensions/render_alphabetsoup.py b/share/extensions/render_alphabetsoup.py index 6bc38459b..7e4009328 100644 --- a/share/extensions/render_alphabetsoup.py +++ b/share/extensions/render_alphabetsoup.py @@ -1,461 +1,463 @@ -#!/usr/bin/env python -''' -Copyright (C) 2001-2002 Matt Chisholm matt@theory.org -Copyright (C) 2008 Joel Holdsworth joel@airwebreathe.org.uk - for AP - -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 copy -import inkex -import simplestyle -import math -import cmath -import string -import random -import render_alphabetsoup_config -import bezmisc -import simplepath -import os -import sys - -syntax = render_alphabetsoup_config.syntax -alphabet = render_alphabetsoup_config.alphabet -units = render_alphabetsoup_config.units -font = render_alphabetsoup_config.font - -# Loads a super-path from a given SVG file -def loadPath( svgPath ): - extensionDir = os.path.normpath( - os.path.join( os.getcwd(), os.path.dirname(__file__) ) - ) - # __file__ is better then sys.argv[0] because this file may be a module - # for another one. - tree = inkex.etree.parse( extensionDir + "/" + svgPath ) - root = tree.getroot() - pathElement = root.find('{http://www.w3.org/2000/svg}path') - if pathElement == None: - return None, 0, 0 - d = pathElement.get("d") - width = float(root.get("width")) - height = float(root.get("height")) - return simplepath.parsePath(d), width, height # Currently we only support a single path - -def combinePaths( pathA, pathB ): - if pathA == None and pathB == None: - return None - elif pathA == None: - return pathB - elif pathB == None: - return pathA - else: - return pathA + pathB - -def flipLeftRight( sp, width ): - for cmd,params in sp: - defs = simplepath.pathdefs[cmd] - for i in range(defs[1]): - if defs[3][i] == 'x': - params[i] = width - params[i] - -def flipTopBottom( sp, height ): - for cmd,params in sp: - defs = simplepath.pathdefs[cmd] - for i in range(defs[1]): - if defs[3][i] == 'y': - params[i] = height - params[i] - -def solveQuadratic(a, b, c): - det = b*b - 4.0*a*c - if det >= 0: # real roots - sdet = math.sqrt(det) - else: # complex roots - sdet = cmath.sqrt(det) - return (-b + sdet) / (2*a), (-b - sdet) / (2*a) - -def cbrt(x): - if x >= 0: - return x**(1.0/3.0) - else: - return -((-x)**(1.0/3.0)) - -def findRealRoots(a,b,c,d): - if a != 0: - a, b, c, d = 1, b/float(a), c/float(a), d/float(a) # Divide through by a - t = b / 3.0 - p, q = c - 3 * t**2, d - c * t + 2 * t**3 - u, v = solveQuadratic(1, q, -(p/3.0)**3) - if type(u) == type(0j): # Complex Cubic Root - r = math.sqrt(u.real**2 + u.imag**2) - w = math.atan2(u.imag, u.real) - y1 = 2 * cbrt(r) * math.cos(w / 3.0) - else: # Complex Real Root - y1 = cbrt(u) + cbrt(v) - - y2, y3 = solveQuadratic(1, y1, p + y1**2) - - if type(y2) == type(0j): # Are y2 and y3 complex? - return [y1 - t] - return [y1 - t, y2 - t, y3 - t] - elif b != 0: - det=c*c - 4.0*b*d - if det >= 0: - return [(-c + math.sqrt(det))/(2.0*b),(-c - math.sqrt(det))/(2.0*b)] - elif c != 0: - return [-d/c] - return [] - -def getPathBoundingBox( sp ): - - box = None - last = None - lostctrl = None - - for cmd,params in sp: - - segmentBox = None - - if cmd == 'M': - # A move cannot contribute to the bounding box - last = params[:] - lastctrl = params[:] - elif cmd == 'L': - if last: - segmentBox = (min(params[0], last[0]), max(params[0], last[0]), min(params[1], last[1]), max(params[1], last[1])) - last = params[:] - lastctrl = params[:] - elif cmd == 'C': - if last: - segmentBox = (min(params[4], last[0]), max(params[4], last[0]), min(params[5], last[1]), max(params[5], last[1])) - - bx0, by0 = last[:] - bx1, by1, bx2, by2, bx3, by3 = params[:] - - # Compute the x limits - a = (-bx0 + 3*bx1 - 3*bx2 + bx3)*3 - b = (3*bx0 - 6*bx1 + 3*bx2)*2 - c = (-3*bx0 + 3*bx1) - ts = findRealRoots(0, a, b, c) - for t in ts: - if t >= 0 and t <= 1: - x = (-bx0 + 3*bx1 - 3*bx2 + bx3)*(t**3) + \ - (3*bx0 - 6*bx1 + 3*bx2)*(t**2) + \ - (-3*bx0 + 3*bx1)*t + \ - bx0 - segmentBox = (min(segmentBox[0], x), max(segmentBox[1], x), segmentBox[2], segmentBox[3]) - - # Compute the y limits - a = (-by0 + 3*by1 - 3*by2 + by3)*3 - b = (3*by0 - 6*by1 + 3*by2)*2 - c = (-3*by0 + 3*by1) - ts = findRealRoots(0, a, b, c) - for t in ts: - if t >= 0 and t <= 1: - y = (-by0 + 3*by1 - 3*by2 + by3)*(t**3) + \ - (3*by0 - 6*by1 + 3*by2)*(t**2) + \ - (-3*by0 + 3*by1)*t + \ - by0 - segmentBox = (segmentBox[0], segmentBox[1], min(segmentBox[2], y), max(segmentBox[3], y)) - - last = params[-2:] - lastctrl = params[2:4] - - elif cmd == 'Q': - # Provisional - if last: - segmentBox = (min(params[0], last[0]), max(params[0], last[0]), min(params[1], last[1]), max(params[1], last[1])) - last = params[-2:] - lastctrl = params[2:4] - - elif cmd == 'A': - # Provisional - if last: - segmentBox = (min(params[0], last[0]), max(params[0], last[0]), min(params[1], last[1]), max(params[1], last[1])) - last = params[-2:] - lastctrl = params[2:4] - - if segmentBox: - if box: - box = (min(segmentBox[0],box[0]), max(segmentBox[1],box[1]), min(segmentBox[2],box[2]), max(segmentBox[3],box[3])) - else: - box = segmentBox - return box - -def mxfm( image, width, height, stack ): # returns possibly transformed image - tbimage = image - if ( stack[0] == "-" ): # top-bottom flip - flipTopBottom(tbimage, height) - stack.pop( 0 ) - - lrimage = tbimage - if ( stack[0] == "|" ): # left-right flip - flipLeftRight(tbimage, width) - stack.pop( 0 ) - return lrimage - -def comparerule( rule, nodes ): # compare node list to nodes in rule - for i in range( 0, len(nodes)): # range( a, b ) = (a, a+1, a+2 ... b-2, b-1) - if (nodes[i] == rule[i][0]): - pass - else: return 0 - return 1 - -def findrule( state, nodes ): # find the rule which generated this subtree - ruleset = syntax[state][1] - nodelen = len(nodes) - for rule in ruleset: - rulelen = len(rule) - if ((rulelen == nodelen) and (comparerule( rule, nodes ))): - return rule - return - -def generate( state ): # generate a random tree (in stack form) - stack = [ state ] - if ( len(syntax[state]) == 1 ): # if this is a stop symbol - return stack - else: - stack.append( "[" ) - path = random.randint(0, (len(syntax[state][1])-1)) # choose randomly from next states - for symbol in syntax[state][1][path]: # recurse down each non-terminal - if ( symbol != 0 ): # 0 denotes end of list ### - substack = generate( symbol[0] ) # get subtree - for elt in substack: - stack.append( elt ) - if (symbol[3]):stack.append( "-" ) # top-bottom flip - if (symbol[4]):stack.append( "|" ) # left-right flip - #else: - #inkex.debug("found end of list in generate( state =", state, ")") # this should be deprecated/never happen - stack.append("]") - return stack - -def draw( stack ): # draw a character based on a tree stack - state = stack.pop(0) - #print state, - - image, width, height = loadPath( font+syntax[state][0] ) # load the image - if (stack[0] != "["): # terminal stack element - if (len(syntax[state]) == 1): # this state is a terminal node - return image, width, height - else: - substack = generate( state ) # generate random substack - return draw( substack ) # draw random substack - else: - #inkex.debug("[") - stack.pop(0) - images = [] # list of daughter images - nodes = [] # list of daughter names - while (stack[0] != "]"): # for all nodes in stack - newstate = stack[0] # the new state - newimage, width, height = draw( stack ) # draw the daughter state - if (newimage): - tfimage = mxfm( newimage, width, height, stack ) # maybe transform daughter state - images.append( [tfimage, width, height] ) # list of daughter images - nodes.append( newstate ) # list of daughter nodes - else: - #inkex.debug(("recurse on",newstate,"failed")) # this should never happen - return None, 0, 0 - rule = findrule( state, nodes ) # find the rule for this subtree - - for i in range( 0, len(images)): - currimg, width, height = images[i] - - if currimg: - #box = getPathBoundingBox(currimg) - dx = rule[i][1]*units - dy = rule[i][2]*units - #newbox = ((box[0]+dx),(box[1]+dy),(box[2]+dx),(box[3]+dy)) - simplepath.translatePath(currimg, dx, dy) - image = combinePaths( image, currimg ) - - stack.pop( 0 ) - return image, width, height - -def draw_crop_scale( stack, zoom ): # draw, crop and scale letter image - image, width, height = draw(stack) - bbox = getPathBoundingBox(image) - simplepath.translatePath(image, -bbox[0], 0) - simplepath.scalePath(image, zoom/units, zoom/units) - return image, bbox[1] - bbox[0], bbox[3] - bbox[2] - -def randomize_input_string( str, zoom ): # generate list of images based on input string - imagelist = [] - - for i in range(0,len(str)): - char = str[i] - #if ( re.match("[a-zA-Z0-9?]", char)): - if ( alphabet.has_key(char)): - if ((i > 0) and (char == str[i-1])): # if this letter matches previous letter - imagelist.append(imagelist[len(stack)-1])# make them the same image - else: # generate image for letter - stack = string.split( alphabet[char][random.randint(0,(len(alphabet[char])-1))] , "." ) - #stack = string.split( alphabet[char][random.randint(0,(len(alphabet[char])-2))] , "." ) - imagelist.append( draw_crop_scale( stack, zoom )) - elif( char == " "): # add a " " space to the image list - imagelist.append( " " ) - else: # this character is not in config.alphabet, skip it - print "bad character", char - return imagelist - -def optikern( image, width, zoom ): # optical kerning algorithm - left = [] - right = [] - - for i in range( 0, 36 ): - y = 0.5 * (i + 0.5) * zoom - xmin = None - xmax = None - - for cmd,params in image: - - segmentBox = None - - if cmd == 'M': - # A move cannot contribute to the bounding box - last = params[:] - lastctrl = params[:] - elif cmd == 'L': - if (y >= last[1] and y <= params[1]) or (y >= params[1] and y <= last[1]): - if params[0] == last[0]: - x = params[0] - else: - a = (params[1] - last[1]) / (params[0] - last[0]) - b = last[1] - a * last[0] - if a != 0: - x = (y - b) / a - else: x = None - - if x: - if xmin == None or x < xmin: xmin = x - if xmax == None or x > xmax: xmax = x - - last = params[:] - lastctrl = params[:] - elif cmd == 'C': - if last: - bx0, by0 = last[:] - bx1, by1, bx2, by2, bx3, by3 = params[:] - - d = by0 - y - c = -3*by0 + 3*by1 - b = 3*by0 - 6*by1 + 3*by2 - a = -by0 + 3*by1 - 3*by2 + by3 - - ts = findRealRoots(a, b, c, d) - - for t in ts: - if t >= 0 and t <= 1: - x = (-bx0 + 3*bx1 - 3*bx2 + bx3)*(t**3) + \ - (3*bx0 - 6*bx1 + 3*bx2)*(t**2) + \ - (-3*bx0 + 3*bx1)*t + \ - bx0 - if xmin == None or x < xmin: xmin = x - if xmax == None or x > xmax: xmax = x - - last = params[-2:] - lastctrl = params[2:4] - - elif cmd == 'Q': - # Quadratic beziers are ignored - last = params[-2:] - lastctrl = params[2:4] - - elif cmd == 'A': - # Arcs are ignored - last = params[-2:] - lastctrl = params[2:4] - - - if xmin != None and xmax != None: - left.append( xmin ) # distance from left edge of region to left edge of bbox - right.append( width - xmax ) # distance from right edge of region to right edge of bbox - else: - left.append( width ) - right.append( width ) - - return (left, right) - -def layoutstring( imagelist, zoom ): # layout string of letter-images using optical kerning - kernlist = [] - length = zoom - for entry in imagelist: - if (entry == " "): # leaving room for " " space characters - length = length + (zoom * render_alphabetsoup_config.space) - else: - image, width, height = entry - length = length + width + zoom # add letter length to overall length - kernlist.append( optikern(image, width, zoom) ) # append kerning data for this image - - workspace = None - - position = zoom - for i in range(0, len(kernlist)): - while(imagelist[i] == " "): - position = position + (zoom * render_alphabetsoup_config.space ) - imagelist.pop(i) - image, width, height = imagelist[i] - - # set the kerning - if i == 0: kern = 0 # for first image, kerning is zero - else: - kerncompare = [] # kerning comparison array - for j in range( 0, len(kernlist[i][0])): - kerncompare.append( kernlist[i][0][j]+kernlist[i-1][1][j] ) - kern = min( kerncompare ) - - position = position - kern # move position back by kern amount - thisimage = copy.deepcopy(image) - simplepath.translatePath(thisimage, position, 0) - workspace = combinePaths(workspace, thisimage) - position = position + width + zoom # advance position by letter width - - return workspace - -class AlphabetSoup(inkex.Effect): - def __init__(self): - inkex.Effect.__init__(self) - self.OptionParser.add_option("-t", "--text", - action="store", type="string", - dest="text", default="Inkscape", - help="The text for alphabet soup") - self.OptionParser.add_option("-z", "--zoom", - action="store", type="float", - dest="zoom", default="8.0", - help="The zoom on the output graphics") - self.OptionParser.add_option("-s", "--seed", - action="store", type="int", - dest="seed", default="0", - help="The random seed for the soup") - - def effect(self): - zoom = self.options.zoom - random.seed(self.options.seed) - - imagelist = randomize_input_string(self.options.text, zoom) - image = layoutstring( imagelist, zoom ) - - if image: - s = { 'stroke': 'none', 'fill': '#000000' } - - new = inkex.etree.Element(inkex.addNS('path','svg')) - new.set('style', simplestyle.formatStyle(s)) - - new.set('d', simplepath.formatPath(image)) - self.current_layer.append(new) - -if __name__ == '__main__': - e = AlphabetSoup() - e.affect() - +#!/usr/bin/env python
+'''
+Copyright (C) 2001-2002 Matt Chisholm matt@theory.org
+Copyright (C) 2008 Joel Holdsworth joel@airwebreathe.org.uk
+ for AP
+
+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 copy
+import inkex
+import simplestyle
+import math
+import cmath
+import string
+import random
+import render_alphabetsoup_config
+import bezmisc
+import simplepath
+import os
+import sys
+import gettext
+_ = gettext.gettext
+
+syntax = render_alphabetsoup_config.syntax
+alphabet = render_alphabetsoup_config.alphabet
+units = render_alphabetsoup_config.units
+font = render_alphabetsoup_config.font
+
+# Loads a super-path from a given SVG file
+def loadPath( svgPath ):
+ extensionDir = os.path.normpath(
+ os.path.join( os.getcwd(), os.path.dirname(__file__) )
+ )
+ # __file__ is better then sys.argv[0] because this file may be a module
+ # for another one.
+ tree = inkex.etree.parse( extensionDir + "/" + svgPath )
+ root = tree.getroot()
+ pathElement = root.find('{http://www.w3.org/2000/svg}path')
+ if pathElement == None:
+ return None, 0, 0
+ d = pathElement.get("d")
+ width = float(root.get("width"))
+ height = float(root.get("height"))
+ return simplepath.parsePath(d), width, height # Currently we only support a single path
+
+def combinePaths( pathA, pathB ):
+ if pathA == None and pathB == None:
+ return None
+ elif pathA == None:
+ return pathB
+ elif pathB == None:
+ return pathA
+ else:
+ return pathA + pathB
+
+def flipLeftRight( sp, width ):
+ for cmd,params in sp:
+ defs = simplepath.pathdefs[cmd]
+ for i in range(defs[1]):
+ if defs[3][i] == 'x':
+ params[i] = width - params[i]
+
+def flipTopBottom( sp, height ):
+ for cmd,params in sp:
+ defs = simplepath.pathdefs[cmd]
+ for i in range(defs[1]):
+ if defs[3][i] == 'y':
+ params[i] = height - params[i]
+
+def solveQuadratic(a, b, c):
+ det = b*b - 4.0*a*c
+ if det >= 0: # real roots
+ sdet = math.sqrt(det)
+ else: # complex roots
+ sdet = cmath.sqrt(det)
+ return (-b + sdet) / (2*a), (-b - sdet) / (2*a)
+
+def cbrt(x):
+ if x >= 0:
+ return x**(1.0/3.0)
+ else:
+ return -((-x)**(1.0/3.0))
+
+def findRealRoots(a,b,c,d):
+ if a != 0:
+ a, b, c, d = 1, b/float(a), c/float(a), d/float(a) # Divide through by a
+ t = b / 3.0
+ p, q = c - 3 * t**2, d - c * t + 2 * t**3
+ u, v = solveQuadratic(1, q, -(p/3.0)**3)
+ if type(u) == type(0j): # Complex Cubic Root
+ r = math.sqrt(u.real**2 + u.imag**2)
+ w = math.atan2(u.imag, u.real)
+ y1 = 2 * cbrt(r) * math.cos(w / 3.0)
+ else: # Complex Real Root
+ y1 = cbrt(u) + cbrt(v)
+
+ y2, y3 = solveQuadratic(1, y1, p + y1**2)
+
+ if type(y2) == type(0j): # Are y2 and y3 complex?
+ return [y1 - t]
+ return [y1 - t, y2 - t, y3 - t]
+ elif b != 0:
+ det=c*c - 4.0*b*d
+ if det >= 0:
+ return [(-c + math.sqrt(det))/(2.0*b),(-c - math.sqrt(det))/(2.0*b)]
+ elif c != 0:
+ return [-d/c]
+ return []
+
+def getPathBoundingBox( sp ):
+
+ box = None
+ last = None
+ lostctrl = None
+
+ for cmd,params in sp:
+
+ segmentBox = None
+
+ if cmd == 'M':
+ # A move cannot contribute to the bounding box
+ last = params[:]
+ lastctrl = params[:]
+ elif cmd == 'L':
+ if last:
+ segmentBox = (min(params[0], last[0]), max(params[0], last[0]), min(params[1], last[1]), max(params[1], last[1]))
+ last = params[:]
+ lastctrl = params[:]
+ elif cmd == 'C':
+ if last:
+ segmentBox = (min(params[4], last[0]), max(params[4], last[0]), min(params[5], last[1]), max(params[5], last[1]))
+
+ bx0, by0 = last[:]
+ bx1, by1, bx2, by2, bx3, by3 = params[:]
+
+ # Compute the x limits
+ a = (-bx0 + 3*bx1 - 3*bx2 + bx3)*3
+ b = (3*bx0 - 6*bx1 + 3*bx2)*2
+ c = (-3*bx0 + 3*bx1)
+ ts = findRealRoots(0, a, b, c)
+ for t in ts:
+ if t >= 0 and t <= 1:
+ x = (-bx0 + 3*bx1 - 3*bx2 + bx3)*(t**3) + \
+ (3*bx0 - 6*bx1 + 3*bx2)*(t**2) + \
+ (-3*bx0 + 3*bx1)*t + \
+ bx0
+ segmentBox = (min(segmentBox[0], x), max(segmentBox[1], x), segmentBox[2], segmentBox[3])
+
+ # Compute the y limits
+ a = (-by0 + 3*by1 - 3*by2 + by3)*3
+ b = (3*by0 - 6*by1 + 3*by2)*2
+ c = (-3*by0 + 3*by1)
+ ts = findRealRoots(0, a, b, c)
+ for t in ts:
+ if t >= 0 and t <= 1:
+ y = (-by0 + 3*by1 - 3*by2 + by3)*(t**3) + \
+ (3*by0 - 6*by1 + 3*by2)*(t**2) + \
+ (-3*by0 + 3*by1)*t + \
+ by0
+ segmentBox = (segmentBox[0], segmentBox[1], min(segmentBox[2], y), max(segmentBox[3], y))
+
+ last = params[-2:]
+ lastctrl = params[2:4]
+
+ elif cmd == 'Q':
+ # Provisional
+ if last:
+ segmentBox = (min(params[0], last[0]), max(params[0], last[0]), min(params[1], last[1]), max(params[1], last[1]))
+ last = params[-2:]
+ lastctrl = params[2:4]
+
+ elif cmd == 'A':
+ # Provisional
+ if last:
+ segmentBox = (min(params[0], last[0]), max(params[0], last[0]), min(params[1], last[1]), max(params[1], last[1]))
+ last = params[-2:]
+ lastctrl = params[2:4]
+
+ if segmentBox:
+ if box:
+ box = (min(segmentBox[0],box[0]), max(segmentBox[1],box[1]), min(segmentBox[2],box[2]), max(segmentBox[3],box[3]))
+ else:
+ box = segmentBox
+ return box
+
+def mxfm( image, width, height, stack ): # returns possibly transformed image
+ tbimage = image
+ if ( stack[0] == "-" ): # top-bottom flip
+ flipTopBottom(tbimage, height)
+ stack.pop( 0 )
+
+ lrimage = tbimage
+ if ( stack[0] == "|" ): # left-right flip
+ flipLeftRight(tbimage, width)
+ stack.pop( 0 )
+ return lrimage
+
+def comparerule( rule, nodes ): # compare node list to nodes in rule
+ for i in range( 0, len(nodes)): # range( a, b ) = (a, a+1, a+2 ... b-2, b-1)
+ if (nodes[i] == rule[i][0]):
+ pass
+ else: return 0
+ return 1
+
+def findrule( state, nodes ): # find the rule which generated this subtree
+ ruleset = syntax[state][1]
+ nodelen = len(nodes)
+ for rule in ruleset:
+ rulelen = len(rule)
+ if ((rulelen == nodelen) and (comparerule( rule, nodes ))):
+ return rule
+ return
+
+def generate( state ): # generate a random tree (in stack form)
+ stack = [ state ]
+ if ( len(syntax[state]) == 1 ): # if this is a stop symbol
+ return stack
+ else:
+ stack.append( "[" )
+ path = random.randint(0, (len(syntax[state][1])-1)) # choose randomly from next states
+ for symbol in syntax[state][1][path]: # recurse down each non-terminal
+ if ( symbol != 0 ): # 0 denotes end of list ###
+ substack = generate( symbol[0] ) # get subtree
+ for elt in substack:
+ stack.append( elt )
+ if (symbol[3]):stack.append( "-" ) # top-bottom flip
+ if (symbol[4]):stack.append( "|" ) # left-right flip
+ #else:
+ #inkex.debug("found end of list in generate( state =", state, ")") # this should be deprecated/never happen
+ stack.append("]")
+ return stack
+
+def draw( stack ): # draw a character based on a tree stack
+ state = stack.pop(0)
+ #print state,
+
+ image, width, height = loadPath( font+syntax[state][0] ) # load the image
+ if (stack[0] != "["): # terminal stack element
+ if (len(syntax[state]) == 1): # this state is a terminal node
+ return image, width, height
+ else:
+ substack = generate( state ) # generate random substack
+ return draw( substack ) # draw random substack
+ else:
+ #inkex.debug("[")
+ stack.pop(0)
+ images = [] # list of daughter images
+ nodes = [] # list of daughter names
+ while (stack[0] != "]"): # for all nodes in stack
+ newstate = stack[0] # the new state
+ newimage, width, height = draw( stack ) # draw the daughter state
+ if (newimage):
+ tfimage = mxfm( newimage, width, height, stack ) # maybe transform daughter state
+ images.append( [tfimage, width, height] ) # list of daughter images
+ nodes.append( newstate ) # list of daughter nodes
+ else:
+ #inkex.debug(("recurse on",newstate,"failed")) # this should never happen
+ return None, 0, 0
+ rule = findrule( state, nodes ) # find the rule for this subtree
+
+ for i in range( 0, len(images)):
+ currimg, width, height = images[i]
+
+ if currimg:
+ #box = getPathBoundingBox(currimg)
+ dx = rule[i][1]*units
+ dy = rule[i][2]*units
+ #newbox = ((box[0]+dx),(box[1]+dy),(box[2]+dx),(box[3]+dy))
+ simplepath.translatePath(currimg, dx, dy)
+ image = combinePaths( image, currimg )
+
+ stack.pop( 0 )
+ return image, width, height
+
+def draw_crop_scale( stack, zoom ): # draw, crop and scale letter image
+ image, width, height = draw(stack)
+ bbox = getPathBoundingBox(image)
+ simplepath.translatePath(image, -bbox[0], 0)
+ simplepath.scalePath(image, zoom/units, zoom/units)
+ return image, bbox[1] - bbox[0], bbox[3] - bbox[2]
+
+def randomize_input_string( str, zoom ): # generate list of images based on input string
+ imagelist = []
+
+ for i in range(0,len(str)):
+ char = str[i]
+ #if ( re.match("[a-zA-Z0-9?]", char)):
+ if ( alphabet.has_key(char)):
+ if ((i > 0) and (char == str[i-1])): # if this letter matches previous letter
+ imagelist.append(imagelist[len(stack)-1])# make them the same image
+ else: # generate image for letter
+ stack = string.split( alphabet[char][random.randint(0,(len(alphabet[char])-1))] , "." )
+ #stack = string.split( alphabet[char][random.randint(0,(len(alphabet[char])-2))] , "." )
+ imagelist.append( draw_crop_scale( stack, zoom ))
+ elif( char == " "): # add a " " space to the image list
+ imagelist.append( " " )
+ else: # this character is not in config.alphabet, skip it
+ inkex.errormsg(_("bad character") + " = 0x%x" % ord(char))
+ return imagelist
+
+def optikern( image, width, zoom ): # optical kerning algorithm
+ left = []
+ right = []
+
+ for i in range( 0, 36 ):
+ y = 0.5 * (i + 0.5) * zoom
+ xmin = None
+ xmax = None
+
+ for cmd,params in image:
+
+ segmentBox = None
+
+ if cmd == 'M':
+ # A move cannot contribute to the bounding box
+ last = params[:]
+ lastctrl = params[:]
+ elif cmd == 'L':
+ if (y >= last[1] and y <= params[1]) or (y >= params[1] and y <= last[1]):
+ if params[0] == last[0]:
+ x = params[0]
+ else:
+ a = (params[1] - last[1]) / (params[0] - last[0])
+ b = last[1] - a * last[0]
+ if a != 0:
+ x = (y - b) / a
+ else: x = None
+
+ if x:
+ if xmin == None or x < xmin: xmin = x
+ if xmax == None or x > xmax: xmax = x
+
+ last = params[:]
+ lastctrl = params[:]
+ elif cmd == 'C':
+ if last:
+ bx0, by0 = last[:]
+ bx1, by1, bx2, by2, bx3, by3 = params[:]
+
+ d = by0 - y
+ c = -3*by0 + 3*by1
+ b = 3*by0 - 6*by1 + 3*by2
+ a = -by0 + 3*by1 - 3*by2 + by3
+
+ ts = findRealRoots(a, b, c, d)
+
+ for t in ts:
+ if t >= 0 and t <= 1:
+ x = (-bx0 + 3*bx1 - 3*bx2 + bx3)*(t**3) + \
+ (3*bx0 - 6*bx1 + 3*bx2)*(t**2) + \
+ (-3*bx0 + 3*bx1)*t + \
+ bx0
+ if xmin == None or x < xmin: xmin = x
+ if xmax == None or x > xmax: xmax = x
+
+ last = params[-2:]
+ lastctrl = params[2:4]
+
+ elif cmd == 'Q':
+ # Quadratic beziers are ignored
+ last = params[-2:]
+ lastctrl = params[2:4]
+
+ elif cmd == 'A':
+ # Arcs are ignored
+ last = params[-2:]
+ lastctrl = params[2:4]
+
+
+ if xmin != None and xmax != None:
+ left.append( xmin ) # distance from left edge of region to left edge of bbox
+ right.append( width - xmax ) # distance from right edge of region to right edge of bbox
+ else:
+ left.append( width )
+ right.append( width )
+
+ return (left, right)
+
+def layoutstring( imagelist, zoom ): # layout string of letter-images using optical kerning
+ kernlist = []
+ length = zoom
+ for entry in imagelist:
+ if (entry == " "): # leaving room for " " space characters
+ length = length + (zoom * render_alphabetsoup_config.space)
+ else:
+ image, width, height = entry
+ length = length + width + zoom # add letter length to overall length
+ kernlist.append( optikern(image, width, zoom) ) # append kerning data for this image
+
+ workspace = None
+
+ position = zoom
+ for i in range(0, len(kernlist)):
+ while(imagelist[i] == " "):
+ position = position + (zoom * render_alphabetsoup_config.space )
+ imagelist.pop(i)
+ image, width, height = imagelist[i]
+
+ # set the kerning
+ if i == 0: kern = 0 # for first image, kerning is zero
+ else:
+ kerncompare = [] # kerning comparison array
+ for j in range( 0, len(kernlist[i][0])):
+ kerncompare.append( kernlist[i][0][j]+kernlist[i-1][1][j] )
+ kern = min( kerncompare )
+
+ position = position - kern # move position back by kern amount
+ thisimage = copy.deepcopy(image)
+ simplepath.translatePath(thisimage, position, 0)
+ workspace = combinePaths(workspace, thisimage)
+ position = position + width + zoom # advance position by letter width
+
+ return workspace
+
+class AlphabetSoup(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.OptionParser.add_option("-t", "--text",
+ action="store", type="string",
+ dest="text", default="Inkscape",
+ help="The text for alphabet soup")
+ self.OptionParser.add_option("-z", "--zoom",
+ action="store", type="float",
+ dest="zoom", default="8.0",
+ help="The zoom on the output graphics")
+ self.OptionParser.add_option("-s", "--seed",
+ action="store", type="int",
+ dest="seed", default="0",
+ help="The random seed for the soup")
+
+ def effect(self):
+ zoom = self.options.zoom
+ random.seed(self.options.seed)
+
+ imagelist = randomize_input_string(self.options.text, zoom)
+ image = layoutstring( imagelist, zoom )
+
+ if image:
+ s = { 'stroke': 'none', 'fill': '#000000' }
+
+ new = inkex.etree.Element(inkex.addNS('path','svg'))
+ new.set('style', simplestyle.formatStyle(s))
+
+ new.set('d', simplepath.formatPath(image))
+ self.current_layer.append(new)
+
+if __name__ == '__main__':
+ e = AlphabetSoup()
+ e.affect()
+
|
