pull/1/merge
Brenda Bell 2016-07-17 13:12:13 -04:00
rodzic 89a0d64792
commit f28473d42e
8 zmienionych plików z 843 dodań i 0 usunięć

Wyświetl plik

@ -0,0 +1 @@
# intentionally empty

153
handlers/actions.py 100644
Wyświetl plik

@ -0,0 +1,153 @@
import cgi
from os import curdir
import sys
import traceback
from modules.pcgenerator import PCGenerator
def pcgenerator_get(handler):
f = open("{}/templates/{}".format(
curdir,
"pcgenerator.html"))
try:
handler.send_response(200)
handler.send_header('Content-type', 'text/html')
handler.end_headers()
handler.wfile.write(f.read())
return
except Exception:
exc_type, exc_value, exc_traceback = sys.exc_info()
handler.wfile.write(
"<h1>Aw, snap! We seem to have a problem.</h1><p><b>")
handler.wfile.write(
repr(traceback.format_exception(exc_type, exc_value,exc_traceback)))
handler.wfile.write(
"</b><p>Please report this error via private message to "
"<a href='http://www.ravelry.com/people/beebell'>beebell on Ravelry</a>. "
"It will be helpful if you include the pattern you uploaded to help me "
"diagnose the issue.")
handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback))
finally:
f.close()
def pcgenerator_post(handler):
try:
ctype, pdict = cgi.parse_header(handler.headers.getheader('content-type'))
if ctype == 'multipart/form-data':
query=cgi.parse_multipart(handler.rfile, pdict)
upfilecontent = query.get('upfile')
if len(upfilecontent[0]) > 2500:
handler.send_response(302)
handler.send_header('Content-type', 'text/html')
handler.end_headers()
handler.wfile.write("Sorry. Your file cannot exceed 2500 bytes!")
else:
horz_repeat = query.get('horz')
vert_repeat = query.get('vert')
cell_height = query.get('rowheight')
cell_width = query.get('colwidth')
generator = PCGenerator(
upfilecontent[0],
float(cell_height[0]),
float(cell_width[0]),
int(horz_repeat[0]),
int(vert_repeat[0]))
result = generator.generate()
handler.send_response(200)
handler.send_header('Content-type', 'image/svg+xml')
handler.send_header("Content-Disposition", "attachment; filename=punchcard.svg")
handler.end_headers()
handler.wfile.write(result)
return
except ValueError as e:
handler.send_response(302)
handler.send_header('Content-type', 'text/html')
handler.end_headers()
handler.wfile.write(
"<h1>Aw, snap!</h1><p>")
handler.wfile.write(e)
except Exception:
exc_type, exc_value, exc_traceback = sys.exc_info()
handler.send_response(302)
handler.send_header('Content-type', 'text/html')
handler.end_headers()
handler.wfile.write(
"<h1>Aw, snap! We seem to have a problem.</h1><p><b>")
handler.wfile.write(
repr(traceback.format_exception(exc_type, exc_value,exc_traceback)))
handler.wfile.write(
"</b><p>Please report this error via private message to "
"<a href='http://www.ravelry.com/people/beebell'>beebell on Ravelry</a>. "
"It will be helpful if you include the pattern you uploaded to help me "
"diagnose the issue.")
handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback))
def calculator_get(handler):
f = open("{}/templates/{}".format(
curdir,
"calculator.html"))
try:
handler.send_response(200)
handler.send_header('Content-type', 'text/html')
handler.end_headers()
handler.wfile.write(f.read())
return
except Exception:
exc_type, exc_value, exc_traceback = sys.exc_info()
handler.wfile.write(
"<h1>Aw, snap! We seem to have a problem.</h1><p><b>")
handler.wfile.write(
repr(traceback.format_exception(exc_type, exc_value,exc_traceback)))
handler.wfile.write(
"</b><p>Please report this error via private message to "
"<a href='http://www.ravelry.com/people/beebell'>beebell on Ravelry</a>. "
"It will be helpful if you include the pattern you uploaded to help me "
"diagnose the issue.")
handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback))
finally:
f.close()
def index_get(handler):
f = open("{}/templates/{}".format(
curdir,
"index.html"))
try:
handler.send_response(200)
handler.send_header('Content-type', 'text/html')
handler.end_headers()
handler.wfile.write(f.read())
return
except Exception:
exc_type, exc_value, exc_traceback = sys.exc_info()
handler.wfile.write(
"<h1>Aw, snap! We seem to have a problem.</h1><p><b>")
handler.wfile.write(
repr(traceback.format_exception(exc_type, exc_value,exc_traceback)))
handler.wfile.write(
"</b><p>Please report this error via private message to "
"<a href='http://www.ravelry.com/people/beebell'>beebell on Ravelry</a>. "
"It will be helpful if you include the pattern you uploaded to help me "
"diagnose the issue.")
handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback))
finally:
f.close()

Wyświetl plik

@ -0,0 +1 @@
# intentionally empty

Wyświetl plik

@ -0,0 +1,244 @@
import svgwrite
# default values
# number of overlapping blank rows at the top of the card
blank_rows = 2
# width of the side margin in mm
side_margin = 17.0
# height of one row on the card in mm
row_height = 5.0
# width of one stitch on the card in mm
stitch_width = 4.5
#stitch_width = 9.0
# radius of a pattern hole in mm
pattern_hole_radius = 3.5
# radius of a clip hole in mm
clip_hole_radius = 3.0
# radius of a sprocket hole in mm
sprocket_hole_radius = 3.5
# drawing stroke width
stroke_width='.1'
# fill color
fill_color = 'white'
# stroke_color
stroke_color = 'black'
card_width = 0
card_height = 0
card_rows = 0
card_stitches = 0
class PCGenerator:
def __init__(self, data, cell_height, cell_width, horz_repeat, vert_repeat):
global row_height
global stitch_width
self.data = data.split()
self.horz_repeat = horz_repeat
self.vert_repeat = vert_repeat
row_height = cell_height
stitch_width = cell_width
def generate(self):
global card_rows
global card_stitches
global card_width
global card_height
card_rows = len(self.data)
card_stitches = len(self.data[0])
if card_rows > 200 or card_stitches > 30:
raise ValueError(
"Your pattern seems to exceed 200 rows and/or 30 stitches. "
"Are you sure you uploaded the right text file?")
card_width = (side_margin * 2) + (card_stitches * self.horz_repeat * stitch_width)
card_height = ((blank_rows * 2) + (card_rows * self.vert_repeat)) * row_height
diagram = self.create_card()
objects = []
self.draw_pattern(diagram, self.data, objects)
self.draw_blank_lines(diagram, objects)
self.draw_clip_holes(diagram, objects)
self.draw_sprocket_holes(diagram, objects)
# sort the list to optimize cutting
sorted_objects = sorted(objects, key=lambda x: (float(x.attribs['cy']), float(x.attribs['cx'])))
for i in sorted_objects:
diagram.add(i)
return diagram.tostring()
def create_card(self):
global card_width
global card_height
diagram = svgwrite.Drawing(
"punchcard.svg",
size=(
'{0}mm'.format(card_width),
'{0}mm'.format(card_height)),
viewBox=(
'0 0 {0} {1}'.format(card_width, card_height)),
preserveAspectRatio='none')
shape_points = [
(2, 0),
(card_width-2, 0),
(card_width-1, 1),
(card_width-1, 20),
(card_width, 22),
(card_width, card_height-22),
(card_width-1, card_height-20),
(card_width-1, card_height-1),
(card_width-2, card_height),
(2, card_height),
(1, card_height-1),
(1, card_height-20),
(0, card_height-22),
(0, 22),
(1, 20),
(1, 1)]
diagram.add(diagram.polygon(
points=shape_points,
fill=fill_color,
stroke=stroke_color,
stroke_width=stroke_width))
return diagram
def draw_pattern(self, diagram, lines, objects):
global card_rows
global card_stitches
global fill_color
global pattern_hole_radius
global row_color
global row_height
global side_margin
global stitch_width
global stroke_color
global stroke_width
# main body of card
yoffset = 10.0 + (row_height / 2)
for row_repeat in range(self.vert_repeat):
for rows in range(card_rows):
xoffset = side_margin + (stitch_width / 2)
for stitch_repeat in range(self.horz_repeat):
for stitches in range(card_stitches):
if lines[rows][stitches].upper() == 'X':
objects.append(diagram.circle(
center=(xoffset, yoffset),
fill=fill_color,
r = (pattern_hole_radius / 2),
stroke=stroke_color,
stroke_width=stroke_width))
xoffset += stitch_width
yoffset += row_height
def draw_blank_lines(self, diagram, objects):
global blank_rows
global card_stitches
global fill_color
global pattern_hole_radius
global row_height
global side_margin
global stitch_width
global stroke_color
global stroke_width
# blank rows at top
yoffset = row_height / 2
for rows in range(blank_rows):
xoffset = side_margin + (stitch_width / 2)
for stitch_repeat in range(self.horz_repeat):
for stitches in range(card_stitches):
objects.append(diagram.circle(
center=(xoffset, yoffset),
fill=fill_color,
r = (pattern_hole_radius / 2),
stroke=stroke_color,
stroke_width=stroke_width))
xoffset += stitch_width
yoffset += row_height
# blank rows at bottom
yoffset = (card_height - (row_height * blank_rows)) + (row_height / 2)
for rows in range(blank_rows):
xoffset = side_margin + (stitch_width / 2)
for stitch_repeat in range(self.horz_repeat):
for stitches in range(card_stitches):
objects.append(diagram.circle(
center=(xoffset, yoffset),
fill=fill_color,
r = (pattern_hole_radius / 2),
stroke=stroke_color,
stroke_width=stroke_width))
xoffset += stitch_width
yoffset += row_height
def draw_clip_holes(self, diagram, objects):
global card_height
global clip_hole_radius
global fill_color
global row_height
global side_margin
global stitch_width
global stroke_color
global stroke_width
left_xoffset = side_margin + (stitch_width / 2) - 6.0
right_xoffset = (card_width - side_margin - (stitch_width / 2)) + 6.0
yoffset = row_height / 2
while yoffset < card_height:
# clip holes on left
objects.append(diagram.circle(
center=(left_xoffset, yoffset),
fill=fill_color,
r = (clip_hole_radius / 2),
stroke=stroke_color,
stroke_width=stroke_width))
# clip holes on right
objects.append(diagram.circle(
center=(right_xoffset, yoffset),
fill=fill_color,
r = (clip_hole_radius / 2),
stroke=stroke_color,
stroke_width=stroke_width))
yoffset += row_height
def draw_sprocket_holes(self, diagram, objects):
left_xoffset = 6.5
right_xoffset = card_width - 6.5
yoffset = row_height
for row_repeat in range(self.vert_repeat):
for rows in range(((card_rows * self.vert_repeat) + (blank_rows * 2)) / 2):
# sprocket holes on left
objects.append(diagram.circle(
center=(left_xoffset, yoffset),
fill=fill_color,
r = (sprocket_hole_radius / 2),
stroke=stroke_color,
stroke_width=stroke_width))
# sprocket holes on left
objects.append(diagram.circle(
center=(right_xoffset, yoffset),
fill=fill_color,
r = (sprocket_hole_radius / 2),
stroke=stroke_color,
stroke_width=stroke_width))
yoffset += (row_height * 2)

68
server.py 100644
Wyświetl plik

@ -0,0 +1,68 @@
from BaseHTTPServer import BaseHTTPRequestHandler
from BaseHTTPServer import HTTPServer
from handlers import actions
pcgenerator_actions = {
'get': actions.pcgenerator_get,
'post': actions.pcgenerator_post }
calculator_actions = {
'get': actions.calculator_get,
'post': None
}
index_actions = {
'get': actions.index_get,
'post': None
}
template_map = {
'/pcgenerator': pcgenerator_actions,
'/pcgenerator/': pcgenerator_actions,
'/calculator': calculator_actions,
'/calculator/': calculator_actions,
'/': index_actions,
'/index': index_actions }
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
print self.path
actions = template_map.get(self.path, None)
if actions is None:
self.send_response(404)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write('Not found!')
actions['get'](self)
def do_POST(self):
print self.path
actions = template_map.get(self.path, None)
if actions is None:
self.send_response(404)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write('Not found!')
actions['post'](self)
def main():
try:
server = HTTPServer(('', 8080), MyHandler)
print 'started httpserver...'
server.serve_forever()
except KeyboardInterrupt:
print '^C received, shutting down server'
except Exception:
print sys.exc_info()[0]
finally:
server.socket.close()
if __name__ == '__main__':
main()

Wyświetl plik

@ -0,0 +1,204 @@
<!DOCTYPE HTML SYSTEM>
<html>
<head>
<title>Knitting Gauge Calculator</title>
<style>
body {
font-family: arial, sans-serif;
margin-top: 40px;
margin-left: 10%;
width: 80%;
background-color: #eeeeee;
}
table {
border-collapse: collapse;
margin-left: 40px;
}
td, th {
border: 1px solid #cccccc;
text-align: center;
padding: 8px;
}
.boxed {
border: 1px solid gray;
}
</style>
</head>
<body>
<h1>Knitting Gauge Calculator</h1>
<a href="/">&lt;Back&gt;</a>
<hr>
<p>This app will help you calculate stitches and rows when your gauge differs from the gauge specified on the pattern.
<p>The forms are reactive. I.e., when you enter a value in any field, the other fields will automatically be adjusted for the value you entered. For example, if you enter inches in the "Imperial" column, the equivalent value in centimeters will automatically displayed in the "Metric" column.
<hr>
<p>
<b>How do I convert from centimeters to/from inches, meters to/from yards or grams to/from ounces?</b>
<p>
<form>
<table class="boxed">
<tr style="background-color:darkgray">
<th style="width:400px">Conversion</th>
<th style="width:150px">Metric</th>
<th style-"width:150px">Imperial</th>
</tr>
<tr>
<td>Centimeters &amp; Inches</td>
<td><input type="number" id="cm" oninput="inch.value = Math.round(cm.value * 0.393701 * 1000) / 1000;"><sup> a</sup></td>
<td><input type="number" id="inch" oninput="cm.value = Math.round(inch.value * 2.54 * 1000) / 1000;"><sup> b</sup></td>
</tr>
<tr>
<td>Meters &amp; Yards</td>
<td><input type="number" id="m" oninput="yd.value = Math.round(m.value * 1.093613 * 1000) / 1000;"><sup> c</sup></td>
<td><input type="number" id="yd" oninput="m.value = Math.round(yd.value * 0.9144 * 1000) / 1000;"><sup> d</sup></td>
</tr>
<tr>
<td>Grams &amp; Ounces</td>
<td><input type="number" id="gm" oninput="oz.value = Math.round(gm.value * 0.035274 * 1000) / 1000;"><sup> e</sup></td>
<td><input type="number" id="oz" oninput="gm.value = Math.round(oz.value * 28.349523 * 1000) / 1000;"><sup> f</sup></td>
</tr>
</table>
</form>
<p><b>How it's done:</b>
<p style="margin-left: 40px;">a = b * 2.54
<br>b = a * 0.393701
<br>c = d * 0.9144
<br>d = c * 1.093613
<br>e = f * 28.349523
<br>f = e * 0.035274
<hr>
<p>
<b>My pattern is written for one gauge, but I get a different gauge (rows or stitches) on my knitting machine. How do I calculate the number of stitches to cast on or the number of rows to knit to achieve the desired size?</b>
<p>
<form>
<table class="boxed">
<tr style="background-color:darkgray">
<th style="width:400px">Description</th>
<th style="width:150px">Value</th>
<tr>
<td>Rows/stitches per 10cm/4" on the pattern<sup> a</sup></td>
<td><input
type="number"
id="a"
min="1.0"
max="100.0"
step="0.1"
value="1.0"
oninput="d.value = Math.round((b.value / a.value) * c.value)"></td>
</tr>
<tr>
<td>Pattern rows/stitches<sup> b</sup></td>
<td><input
type="number"
id="b"
min="1.0"
max="100.0"
step="0.1"
value="1.0"
oninput="d.value = Math.round((b.value / a.value) * c.value)"></td>
</tr>
<tr>
<td>Rows/stitches per 10cm/4" on your knitting machine<sup> c</sup></td>
<td><input
type="number"
id="c"
min="1.0"
max="100.0"
step="0.1"
value="1.0"
oninput="d.value = Math.round((b.value / a.value) * c.value)"></td>
</tr>
<tr>
<td>Equivalent rows/stitches on your knitting machine<sup> d</sup></td>
<td><output
name="d"
for="a b c"
value="1">
</output></td>
</tr>
</table>
</form>
<p><b>How it's done:</b>
<p style="margin-left: 40px">d = (b / a) * c
<hr>
<p>
<b>How do I calculate the number of stitches to cast on or the number of rows to knit to achieve the desired width or length?</b>
<p>
<form>
<table class="boxed">
<tr style="background-color:darkgray">
<th style="width:400px">Description</th>
<th style="width:150px">Metric (cm)</th>
<th style="width:150px">Imperial (in)</th>
</tr>
<tr>
<td>Rows/stitches per 10cm/4"<sup> a</sup></td>
<td colspan="2"><input
type="number"
id="a"
name="gauge"
min="1.0"
max="100.0"
step="0.1"
value="1.0"
oninput="c1.value = Math.round(a.value / 10 * b1.value); c2.value = Math.round(a.value / 4 * b1.value);"></td>
</tr>
<tr>
<td>desired width/length<sup> b</sup></td>
<td><input
type="number"
id="b1"
name="target_size"
min="1.0"
max="100.0"
step="0.1"
value="1.0"
oninput="b2.value = Math.round(b1.value * 0.393701 * 1000) / 1000; c1.value = Math.round(a.value / 10 * b1.value); c2.value = Math.round(a.value / 4 * b2.value);"></td>
<td><input
type="number"
id="b2"
name="target_size"
min="1.0"
max="100.0"
step="0.1"
value="1.0"
oninput="b1.value= Math.round(b2.value * 2.54 * 1000) / 1000; c1.value = Math.round(a.value / 10 * b1.value); c2.value = Math.round(a.value / 4 * b2.value);"></td>
</tr>
<tr>
<td># stitches to cast on or rows to knit<sup> c</sup></td>
<td><output
name="c1"
for="a b1"
value="1">
</output></td>
<td><output
name="c2"
for="a b2"
value="1">
</output></td>
</tr>
</table>
</form>
<p><b>How it's done:</b>
<p style="margin-left: 40px">c = (a / 10) * b (in centimeters)
<br>c = (a / 4) * b (in inches)
<hr>
<p><b>Quick Tips:</b>
<p>After you compute the number of stitches or rows required for your gauge, consult the pattern to see if knitting a different size will produce the desired results.
<p style="margin-left: 40px">For example, if the pattern instructs you to cast on 80 stitches for a size medium but your gauge requires you to cast on 90 stitches to achieve the same width, look at the pattern to see if the next larger size specifies a cast on that's closer to 90 stitches. Knitting a larger size at a smaller gauge (or smaller size at a larger gauge) may produce the same result.
<p>If you have questions or feedback, please contact <a href="http://www.ravelry.com/people/beebell">beebell on Ravelry</a> via private message.</p>
</body>
</html>

Wyświetl plik

@ -0,0 +1,35 @@
<!DOCTYPE HTML SYSTEM>
<html>
<head>
<title>Knitting Tools</title>
<style>
body {
font-family: arial, sans-serif;
margin-top: 40px;
margin-left: 10%;
width: 80%;
background-color: #eeeeee;
}
table {
border-collapse: collapse;
}
td, th {
border: 1px solid #cccccc;
text-align: right;
padding: 8px;
}
</style>
</head>
<body>
<h1>Knitting Tools</h1>
<hr>
<p><a href="/pcgenerator">Punchcard Generator</a>: Generate SVG files from text files for cutting punchcards on a die cutter
<p><a href="/calculator">Knitting Calculator</a>: Perform a variety of calculations including unit and gauge conversions
<hr>
</body>
</html>

Wyświetl plik

@ -0,0 +1,137 @@
<!DOCTYPE HTML SYSTEM>
<html>
<head>
<title>PCGenerator</title>
<style>
body {
font-family: arial, sans-serif;
margin-top: 40px;
margin-left: 10%;
width: 80%;
background-color: #eeeeee;
}
table {
border-collapse: collapse;
}
td, th {
border: None;
text-align: left;
padding: 8px;
}
</style>
</head>
<body>
<h1>Punchcard Generator</h1>
<a href="/">&lt;Back&gt;</a>
<hr>
<p>This app will allow you to upload a small text file that contains your pattern represented as a sequence of rows with X's for punched holes and any other character except space for unpunched holes. The program will generate an SVG file that can be imported into the software you use to cut material with your die cutter.
<h2>TL;DR</h2>
<ul>
<li>Select the file to upload.</li>
<li>Enter your punchcard row height and column width in millimeters.</li>
<li>Enter horizontal and vertical repeats if you need them.</li>
<li>Click the Upload button.</li>
</ul>In a few seconds, you should see a dialog prompting you for where you want to save your file. If you need more detailed instructions, keep reading below.
<p>
<form
id="main"
method="POST"
enctype="multipart/form-data"
action="/pcgenerator"
onfocus="x.value=horz.value; y.value=vert.value"
oninput="x.value=horz.value; y.value=vert.value">
<table>
<tr>
<td style="text-align:right">Select file to upload:<br>&nbsp;</td>
<td colspan="2"><input type="file" name="upfile"><br><small>(maximum file size 2500 characters)<small></td>
</tr>
<tr>
<td style="text-align:right">Height of 1 punchcard row (mm):</td>
<td colspan="2"><input type="number" name="rowheight" min="1.0" max="10.0" step="0.1" value="5.0"></td>
</tr>
<tr>
<td style="text-align:right">Width of 1 punchcard column (mm):</td>
<td colspan="2"><input type="number" name="colwidth" min="1.0" max="10.0" step="0.1" value="4.5"></td>
</tr>
<tr>
<td style="text-align:right"># Horizontal repeats (up to 12):</td>
<td colspan="2"><input type="range" id="horz" name="horz" min="1" max="12" step="1" value="1">
<output id="x" name="x" value="1"></output>
<script>x.value=horz.value;</script></td>
</tr>
<tr>
<td style="text-align:right"># Vertical repeats (up to 12):</td>
<td><input type="range" id="vert" name="vert" min="1" max="12" step="1" value="1">
<output id="y" name="y" value="1"></output>
<script>y.value=vert.value;</script></td>
</tr>
<tr>
<td></td>
<td colspan="2"><input type="submit" value="Upload"></td>
</tr>
</table>
<br>
<br>
</form>
<hr>
<h1>Instructions</h1>
<p>Both upper and lower case X's are supported. The total width in stitches is determined by the number of characters in the first row. For example, the following text file has 4 rows and 12 stitches:
<pre>
x-----x-----
-x-----x----
--x-----x---
---x-----x--
</pre>
<p>By default, the generator will do 1 horizontal repeat and 1 vertical repeat. I.e., if your file contains 36 rows with 24 characters per row, the generated file will produce a 24 row x 36 stitch card. You can increase the width and/or length by adjusting the horizontal and/or vertical repeat before clicking the upload button.
<p>What this means is that you can design a simple 4x4 card and use repeats to make the card as wide or as long as it needs to be in order to work properly. For example, the following text file will generate a 24 row x 48 stitch card if you specify 6 for the horizontal repeat and 12 for the vertical repeat:
<pre>
x-x-
-x-x
x-x-
-x-x
</pre>
<p>You'll need to specify the height and width of 1 row and 1 column on the punchcard. You can determine the correct values by measuring the size of the squares on a card that works with your knitting machine. If your card doesn't have squares, you can measure the distance between the centers of two adjacent holes. For reference:
<p>
<table>
<tr>
<th>Knitting Machine</th>
<th style="text-align:center">Row Height<br>(mm)</th>
<th style="text-align:center">Column Width<br>(mm)</th>
</tr>
<tr>
<td>Brother 24-Stitch Standard</td>
<td style="text-align:center">5</td>
<td style="text-align:center">4.5</td>
</tr>
<tr>
<td>Brother 12-Stitch Bulky</td>
<td style="text-align:center">5</td>
<td style="text-align:center">9</td>
</tr>
</table>
<p>The resulting SVG file can be imported into any application that understands SVG. You can also display the SVG in some browsers. Neither the text file or resulting SVG will be saved on this site. Be sure to keep them in a safe location so they don't get lost.
<p>This program has only been tested using Sure Cuts A Lot on a Pazzles Creative Mighty. YMMV!
<p>If you have questions or feedback, please contact <a href="http://www.ravelry.com/people/beebell">beebell on Ravelry</a> via private message.</p>
</body>
</html>