From d323059b9db610ac0dda23475729cde5f5c0a619 Mon Sep 17 00:00:00 2001
From: Lex Neva
Date: Thu, 12 Apr 2018 20:05:01 -0400
Subject: [PATCH 07/23] always add inkstitch XML namespace
---
inkstitch/extensions.py | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/inkstitch/extensions.py b/inkstitch/extensions.py
index 7795abb8f..d48fcc8c7 100644
--- a/inkstitch/extensions.py
+++ b/inkstitch/extensions.py
@@ -101,3 +101,24 @@ class InkstitchExtension(inkex.Effect):
patches.extend(element.embroider(last_patch))
return patches
+
+ def parse(self):
+ """Override inkex.Effect to add Ink/Stitch xml namespace"""
+
+ # SVG parsers don't actually look for anything at this URL. They just
+ # care that it's unique. That defines a "namespace" of element and
+ # attribute names to disambiguate conflicts with element and
+ # attribute names other XML namespaces.
+ #
+ # Updating inkex.NSS here allows us to pass 'inkstitch' into
+ # inkex.addNS().
+ inkex.NSS.update('inkstitch', 'http://inkstitch.org/namespace')
+
+ # call the superclass's method first
+ inkex.Effect.parse(self)
+
+ # This is the only way I could find to add a namespace to an existing
+ # element tree at the top without getting ugly prefixes like "ns0".
+ inkex.etree.cleanup_namespaces(inkex.document,
+ top_nsmap=inkex.NSS,
+ keep_ns_prefixes=inkex.NSS.keys())
From 85a8b6b1cfe008eed49c678bc0af9e2bea931f3c Mon Sep 17 00:00:00 2001
From: Lex Neva
Date: Fri, 13 Apr 2018 21:23:00 -0400
Subject: [PATCH 08/23] inkstitch metadata framework
---
embroider_print.py | 32 +++++++++++---
inkstitch/extensions.py | 81 ++++++++++++++++++++++++++++++++++-
print/resources/inkstitch.js | 31 +++++++-------
print/templates/headline.html | 6 +--
4 files changed, 122 insertions(+), 28 deletions(-)
diff --git a/embroider_print.py b/embroider_print.py
index 96c3255d2..79d9bf45c 100644
--- a/embroider_print.py
+++ b/embroider_print.py
@@ -9,6 +9,7 @@ import socket
import errno
import time
import logging
+from copy import deepcopy
import wx
import inkex
@@ -23,7 +24,7 @@ from jinja2 import Environment, FileSystemLoader, select_autoescape
from datetime import date
import base64
-from flask import Flask, request, Response, send_from_directory
+from flask import Flask, request, Response, send_from_directory, jsonify
import webbrowser
import requests
@@ -71,6 +72,7 @@ def open_url(url):
class PrintPreviewServer(Thread):
def __init__(self, *args, **kwargs):
self.html = kwargs.pop('html')
+ self.metadata = kwargs.pop('metadata')
Thread.__init__(self, *args, **kwargs)
self.daemon = True
self.last_request_time = None
@@ -129,6 +131,21 @@ class PrintPreviewServer(Thread):
# nothing to do here -- request_started() will restart the watcher
return "OK"
+ @self.app.route('/metadata//set', methods=['POST'])
+ def set_field(field_name):
+ self.metadata[field_name] = request.form['value']
+ return "OK"
+
+ @self.app.route('/metadata/', methods=['GET'])
+ def get_field(field_name):
+ return jsonify(self.metadata[field_name])
+
+ @self.app.route('/metadata', methods=['GET'])
+ def get_metadata():
+ # It's necessary to convert the metadata to a dict because json doesn't
+ # trust that a MutableMapping is dict-like :(
+ return jsonify(dict(self.metadata))
+
def stop(self):
# for whatever reason, shutting down only seems possible in
# the context of a flask request, so we'll just make one
@@ -299,7 +316,11 @@ class Print(InkstitchExtension):
color_blocks = stitch_plan.color_blocks,
)
- print_server = PrintPreviewServer(html=html)
+ # We've totally mucked with the SVG. Restore it so that we can save
+ # metadata into it.
+ self.document = deepcopy(self.original_document)
+
+ print_server = PrintPreviewServer(html=html, metadata=self.get_inkstitch_metadata())
print_server.start()
time.sleep(1)
@@ -310,12 +331,9 @@ class Print(InkstitchExtension):
info_frame.Show()
app.MainLoop()
- # don't let inkex print the document out
- sys.exit(0)
-
if __name__ == '__main__':
- save_stderr()
+ #save_stderr()
effect = Print()
effect.affect()
- restore_stderr()
+ #restore_stderr()
diff --git a/inkstitch/extensions.py b/inkstitch/extensions.py
index d48fcc8c7..57d292313 100644
--- a/inkstitch/extensions.py
+++ b/inkstitch/extensions.py
@@ -1,6 +1,80 @@
import inkex
+import re
+from collections import MutableMapping
from .elements import AutoFill, Fill, Stroke, SatinColumn, Polyline, EmbroideryElement
from . import SVG_POLYLINE_TAG, SVG_GROUP_TAG, SVG_DEFS_TAG, INKSCAPE_GROUPMODE, EMBROIDERABLE_TAGS, PIXELS_PER_MM
+from .utils import cache
+
+
+SVG_METADATA_TAG = inkex.addNS("metadata", "svg")
+
+
+def strip_namespace(tag):
+ """Remove xml namespace from a tag name.
+
+ >>> {http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd}namedview
+ <<< namedview
+ """
+
+ match = re.match('^\{[^}]+\}(.+)$', tag)
+
+ if match:
+ return match.group(1)
+ else:
+ return tag
+
+
+class InkStitchMetadata(MutableMapping):
+ """Helper class to get and set inkstitch-specific metadata attributes.
+
+ Operates on a document and acts like a dict. Setting an item adds or
+ updates a metadata element in the document. Getting an item retrieves
+ a metadata element's text contents or None if an element by that name
+ doesn't exist.
+ """
+
+ def __init__(self, document):
+ self.document = document
+ self.metadata = self._get_or_create_metadata()
+
+ def _get_or_create_metadata(self):
+ metadata = self.document.find(SVG_METADATA_TAG)
+
+ if metadata is None:
+ metadata = inkex.etree.SubElement(self.document, SVG_METADATA_TAG)
+
+ return metadata
+
+ # Because this class inherints from MutableMapping, all we have to do is
+ # implement these five methods and we get a full dict-like interface.
+
+ def __setitem__(self, name, value):
+ self[name].text = value
+
+ def __getitem__(self, name):
+ tag = inkex.addNS(name, "inkstitch")
+ item = self.metadata.find(tag)
+ if item is None:
+ item = inkex.etree.SubElement(self.metadata, tag)
+
+ return item.text
+
+ def __delitem__(self, name):
+ item = self[name]
+
+ if item:
+ self.metadata.remove(item)
+
+ def __iter__(self):
+ for child in self.metadata:
+ if child.prefix == "inkstitch":
+ yield strip_namespace(child.tag)
+
+ def __len__(self):
+ for i, item in enumerate(self):
+ pass
+
+ return i + 1
class InkstitchExtension(inkex.Effect):
@@ -102,6 +176,9 @@ class InkstitchExtension(inkex.Effect):
return patches
+ def get_inkstitch_metadata(self):
+ return InkStitchMetadata(self.document)
+
def parse(self):
"""Override inkex.Effect to add Ink/Stitch xml namespace"""
@@ -112,13 +189,13 @@ class InkstitchExtension(inkex.Effect):
#
# Updating inkex.NSS here allows us to pass 'inkstitch' into
# inkex.addNS().
- inkex.NSS.update('inkstitch', 'http://inkstitch.org/namespace')
+ inkex.NSS['inkstitch'] = 'http://inkstitch.org/namespace'
# call the superclass's method first
inkex.Effect.parse(self)
# This is the only way I could find to add a namespace to an existing
# element tree at the top without getting ugly prefixes like "ns0".
- inkex.etree.cleanup_namespaces(inkex.document,
+ inkex.etree.cleanup_namespaces(self.document,
top_nsmap=inkex.NSS,
keep_ns_prefixes=inkex.NSS.keys())
diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js
index 454c9ae27..ed26d3671 100644
--- a/print/resources/inkstitch.js
+++ b/print/resources/inkstitch.js
@@ -40,19 +40,18 @@ $(function() {
setTimeout(ping, 1000);
setPageNumbers();
scaleInksimulation();
-
+
/* Contendeditable Fields */
-
+
// When we focus out from a contenteditable field, we want to
// set the same content to all fields with the same classname
- document.querySelectorAll('[contenteditable="true"]').forEach(function(elem) {
- elem.addEventListener('focusout', function() {
- var content = $(this).html();
- var field_name = $(this).attr('data-field-name');
- $('[data-field-name="' + field_name + '"]').html(content);
- });
+ $('[contenteditable="true"]').on('focusout', function() {
+ var content = $(this).html();
+ var field_name = $(this).attr('data-field-name');
+ $('[data-field-name="' + field_name + '"]').html(content);
+ $.post('/metadata/' + field_name + '/set', {value: content});
});
-
+
$('[contenteditable="true"]').keypress(function(e) {
if (e.which == 13) {
// pressing enter defocuses the element
@@ -64,10 +63,10 @@ $(function() {
return true;
}
});
-
-
+
+
/* Settings Bar */
-
+
$('button.close').click(function() {
$.post('/shutdown', {})
.done(function(data) {
@@ -92,20 +91,20 @@ $(function() {
$('#close-settings').click(function(){
$('#settings-ui').hide();
});
-
+
/* Settings */
-
+
// Paper Size
$('select#printing-size').change(function(){
$('.page').toggleClass('a4');
});
-
+
//Checkbox
$(':checkbox').change(function() {
$('.' + this.id).toggle();
setPageNumbers();
scaleInksimulation();
});
-
+
});
diff --git a/print/templates/headline.html b/print/templates/headline.html
index 649c02ea4..cbc9c43a0 100644
--- a/print/templates/headline.html
+++ b/print/templates/headline.html
@@ -3,9 +3,9 @@