evil-mad-EggBot/inkscape_deprecated/eggbot_acrostic.py

271 wiersze
11 KiB
Python
Executable File

# coding=utf-8
# eggbot_acrostic.py
#
# Render an acrostic poem using the Hershey fonts
#
# This extension requires the hersheydata.py file which is part of the
# Hershey Text rendering extension written by Windell H. Oskay of
# www.evilmadscientist.com. Information on that extension may be found at
#
# http://www.evilmadscientist.com/go/hershey
#
# Copyright 2011, Daniel C. Newman,
#
# Significant portions of this code were written by Windell H. Oskay and are
# Copyright 2011, Windell H. Oskay, www.evilmadscientist.com
#
# 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 inkex
import simplestyle
import hersheydata
WIDTH = 3200
HEIGHT = 800
MAX_H = 32 # Maximum height of a Hershey font character (parens)
LINE_SKIP = 6 # Baseline skip between lines of text
# Mapping table to map the names used here to the corresponding
# names used in hersheydata.py. This helps prevent end users from
# being impacted by a name change in hersheydata.py. This can also
# be used to deal with a face being removed from hersheydata.py
map_our_names_to_hersheydata = {
'astrology': 'astrology',
'cursive': 'cursive',
'cyrillic': 'cyrillic',
'futural': 'futural',
'futuram': 'futuram',
'gothiceng': 'gothiceng',
'gothicger': 'gothicger',
'gothicita': 'gothicita',
'greek': 'greek',
'japanese': 'japanese',
'markers': 'markers',
'mathlow': 'mathlow',
'mathupp': 'mathupp',
'meteorology': 'meteorology',
'music': 'music',
'scriptc': 'scriptc',
'scripts': 'scripts',
'symbolic': 'symbolic',
'timesg': 'timesg',
'timesi': 'timesi',
'timesib': 'timesib',
'timesr': 'timesr',
'timesrb': 'timesrb'}
# The following two routines are lifted with impunity from Windell H. Oskay's
# hershey.py Hershey Text extension for Inkscape. They are,
# Copyright 2011, Windell H. Oskay, www.evilmadscientist.com
def draw_svg_text(char, face, offset, vertoffset, parent):
style = {'stroke': '#000000', 'fill': 'none'}
path_string = face[char]
split_string = path_string.split()
midpoint = offset - int(split_string[0])
i = path_string.find("M")
if i >= 0:
path_string = path_string[i:] # portion after first move
trans = 'translate(' + str(midpoint) + ',' + str(vertoffset) + ')'
text_attribs = {'style': simplestyle.formatStyle(style), 'd': path_string, 'transform': trans}
inkex.etree.SubElement(parent, inkex.addNS('path', 'svg'), text_attribs)
return midpoint + int(split_string[1]) # new offset value
def renderText(parent, w, y, text, typeface):
"""
Render a string of text starting from the point (w, y) and using
the supplied typeface data.
"""
if not text:
return
spacing = 3 # spacing between letters
letter_vals = (ord(q) - 32 for q in text)
for q in letter_vals:
if q < 0 or q > 95:
w += 2 * spacing
else:
w = draw_svg_text(q, typeface, w, y, parent)
return w
def renderLine(parent, x, y, line, typeface1, typeface2):
"""
Render a single line of text:
+ The text runs horizontally from left to right starting at the point (x,y)
+ The first character of the line is written using "typeface1"
+ The remainder of the line of text is written using "typeface2"
The entire line is stored as individual paths which are child elements
of the SVG element "parent". The text in typeface2 (line[1:]) is also
placed in it's own subgroup. The leading character is not placed in
that subgroup. The reasoning is that the user may want to pick out
the first character of each line of text and put them in another layer
for plotting in a different color.
"""
# Return now if there's nothing to do
if not line:
return
# Render the first character
x = renderText(parent, x, y, [line[0]], typeface1)
# Render the rest of the line
line = line[1:]
if not line:
return
g = inkex.etree.SubElement(parent, 'g')
renderText(g, x, y, line, typeface2)
class AcrosticText(inkex.Effect):
def __init__(self):
inkex.Effect.__init__(self)
self.OptionParser.add_option("--tab", # NOTE: value is not used.
action="store", type="string", dest="tab", default="splash",
help="The active tab when Apply was pressed")
self.OptionParser.add_option("--line01", action="store",
type="string", dest="line1", default="")
self.OptionParser.add_option("--line02", action="store",
type="string", dest="line2", default="")
self.OptionParser.add_option("--line03", action="store",
type="string", dest="line3", default="")
self.OptionParser.add_option("--line04", action="store",
type="string", dest="line4", default="")
self.OptionParser.add_option("--line05", action="store",
type="string", dest="line5", default="")
self.OptionParser.add_option("--line06", action="store",
type="string", dest="line6", default="")
self.OptionParser.add_option("--line07", action="store",
type="string", dest="line7", default="")
self.OptionParser.add_option("--line08", action="store",
type="string", dest="line8", default="")
self.OptionParser.add_option("--line09", action="store",
type="string", dest="line9", default="")
self.OptionParser.add_option("--line10", action="store",
type="string", dest="line10", default="")
self.OptionParser.add_option("--line11", action="store",
type="string", dest="line11", default="")
self.OptionParser.add_option("--line12", action="store",
type="string", dest="line12", default="")
self.OptionParser.add_option("--face1",
action="store", type="string", dest="face1", default="scriptc",
help="Leading font typeface")
self.OptionParser.add_option("--face2", action="store",
type="string", dest="face2", default="scripts",
help="Secondary typeface")
self.OptionParser.add_option("--flip", action="store", type="inkbool",
dest="flip", default=False,
help="Flip the text for plotting with the egg's bottom at the egg motor")
self.OptionParser.add_option("--stretch",
action="store", type="inkbool", dest="stretch", default=True,
help="Stretch the text horizontally to account for egg distortions")
def effect(self):
# Process the lines, ignoring leading or trailing blank lines
# and collapsing multiple internal runs of blank lines into a
# single blank line.
lines = []
prior_empty = False
for i in range(1, 13):
line = getattr(self.options, "line{}".format(i)).strip()
if line == '':
if len(lines) != 0:
prior_empty = True
else:
if prior_empty:
lines.append('')
prior_empty = False
lines.append(line)
# Return now if there are no lines to print
line_count = len(lines)
if line_count == 0:
return
# Determine how much vertical room we need for our text
h = line_count * MAX_H + (line_count - 1) * LINE_SKIP
svg = self.document.getroot()
doc_height = self.unittouu(svg.attrib['height'])
if doc_height <= 0:
doc_height = HEIGHT
doc_width = self.unittouu(svg.attrib['width'])
if doc_width <= 0:
doc_width = WIDTH
# Scale to doc_height pixels high
scale_y = float(doc_height) / float(h)
if self.options.stretch:
scale_x = scale_y * 1.5
else:
scale_x = scale_y
# Determine where to position the text
# We do not bother centering the text horizontally
# to do that we would need to pre-render the text to determine
# the length of the longest line. That's too much bother so
# we just skip that potential nice-to-have.
x = float(doc_width) / (2.0 * scale_x)
y = float(MAX_H) / scale_y
# Get the two type faces
name1 = self.options.face1
if name1 in map_our_names_to_hersheydata:
name1 = map_our_names_to_hersheydata[name1]
face1 = getattr(hersheydata, name1)
name2 = self.options.face2
if name2 in map_our_names_to_hersheydata:
name2 = map_our_names_to_hersheydata[name2]
face2 = getattr(hersheydata, name2)
# Create the group which will contain all of the text
# We DO NOT make this a child of the current layer as that
# would subject us to any transforms it might have. That's
# an issue even in the simple case of someone opening a default
# document and then changing its dimensions to 3200 x 800:
# Inkscape imposes a transform in that situation. While that
# transform is no big deal, it's another complication in trying
# to just make the resulting text look right (right size, right
# approximate position, etc.).
if self.options.flip:
attribs = {'transform': 'matrix(-{0:f},0,0,-{1:f},{2:d},{3:d})'.format(scale_x, scale_y, doc_width, doc_height)}
else:
attribs = {'transform': 'scale({0:f},{1:f})'.format(scale_x, scale_y)}
container = inkex.etree.SubElement(self.document.getroot(), 'g', attribs)
# Finally, we render each line of text
for line in lines:
if line:
g = inkex.etree.SubElement(container, 'g')
renderLine(g, x, y, line, face1, face2)
y += MAX_H + LINE_SKIP
if __name__ == '__main__':
e = AcrosticText()
e.affect()