diff options
| -rw-r--r-- | embroider_print.py | 32 | ||||
| -rw-r--r-- | inkstitch/extensions.py | 81 | ||||
| -rw-r--r-- | print/resources/inkstitch.js | 31 | ||||
| -rw-r--r-- | print/templates/headline.html | 6 |
4 files changed, 122 insertions, 28 deletions
diff --git a/embroider_print.py b/embroider_print.py index 96c3255d..79d9bf45 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/<field_name>/set', methods=['POST']) + def set_field(field_name): + self.metadata[field_name] = request.form['value'] + return "OK" + + @self.app.route('/metadata/<field_mame>', 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 d48fcc8c..57d29231 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 454c9ae2..ed26d367 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 649c02ea..cbc9c43a 100644 --- a/print/templates/headline.html +++ b/print/templates/headline.html @@ -3,9 +3,9 @@ </figure> <div class="headline"> <div class="pageTitle"> - <h1><span class="jobtitle" contenteditable="true" data-placeholder="{{ _('Enter job title...') }}" data-field-name="job-title">{{ job.title }}</span></h1> - <p class="header-field" data-label="{{ _('CLIENT') }}:" contenteditable="true" data-placeholder="{{ _('Enter client name...') }}" data-field-name="client-name">{{ client }}</p> - <p class="header-field" data-label="{{ _('PURCHASE ORDER #:') }}" contenteditable="true" data-placeholder="{{ _('Enter purchase order number...') }}" data-field-name="purchase-order">{{ purchase_order }}</p> + <h1><span class="jobtitle" contenteditable="true" data-placeholder="{{ _('Enter job title...') }}" data-field-name="title"></span></h1> + <p class="header-field" data-label="{{ _('CLIENT') }}:" contenteditable="true" data-placeholder="{{ _('Enter client name...') }}" data-field-name="client-name"></p> + <p class="header-field" data-label="{{ _('PURCHASE ORDER #:') }}" contenteditable="true" data-placeholder="{{ _('Enter purchase order number...') }}" data-field-name="purchase-order"></p> </div> <div class="currentDate">{{ date|datetimeformat(_('%Y.%m.%d')) }}</div> |
