kopia lustrzana https://github.com/evil-mad/EggBot
271 wiersze
11 KiB
Python
Executable File
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()
|