From e4c4eec55f34215c188325eb669c6cb524503007 Mon Sep 17 00:00:00 2001 From: Kaalleen <36401965+kaalleen@users.noreply.github.com> Date: Sun, 8 Apr 2018 17:34:35 +0200 Subject: [PATCH 01/23] Update inkstitch.js A first start: zooming and dragging the SVG --- print/resources/inkstitch.js | 45 ++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js index 454c9ae27..e804e3255 100644 --- a/print/resources/inkstitch.js +++ b/print/resources/inkstitch.js @@ -26,9 +26,9 @@ function scaleInksimulation() { if(scale <= 1) { transform += " scale(" + scale + ")"; - label = parseInt(scale*100) + '%'; + label = parseInt(scale*100); } else { - label = "100%"; + label = "100"; } $(this).find('svg').css({ transform: transform }); @@ -41,6 +41,47 @@ $(function() { setPageNumbers(); scaleInksimulation(); + /* Mousewheel scaling */ + $('figure.inksimulation').on( 'DOMMouseScroll mousewheel', function ( event ) { + var transform = $(this).find('svg').css('transform').match(/-?[\d\.]+/g); + var scale = parseFloat(transform[0]); + if( scale > 0.01 && (event.originalEvent.detail > 0 || event.originalEvent.wheelDelta < 0)) { + // scroll down + scale -= 0.01; + } else { + //scroll up + scale += 0.01; + } + // set modified scale + transform[0] = scale; + transform[3] = scale; + $(this).find('svg').css({ transform: 'matrix(' + transform + ')' }); + + // set scale caption text + $(this).find("span").text(parseInt(scale*100)); + + //prevent page fom scrolling + return false; + }); + + /* Drag SVG */ + $('figure.inksimulation').on('mousedown', function(e) { + $(this).data('p0', { x: e.pageX, y: e.pageY }); + $(this).css({cursor: 'move'}); + }).on('mouseup', function(e) { + var p0 = $(this).data('p0'), + p1 = { x: e.pageX, y: e.pageY }, + d = Math.sqrt(Math.pow(p1.x - p0.x, 2) + Math.pow(p1.y - p0.y, 2)); + if (d > 4) { + var transform = $(this).find('svg').css('transform').match(/-?[\d\.]+/g); + transform[4] = parseFloat(transform[4]) + parseFloat(p1.x-p0.x); + transform[5] = parseFloat(transform[5]) + parseFloat(p1.y-p0.y); + // set modified translate + $(this).find('svg').css({ transform: 'matrix(' + transform + ')' }); + $(this).css({cursor: 'auto'}); + } + }) + /* Contendeditable Fields */ // When we focus out from a contenteditable field, we want to From 6e1f4969e154343b458bad9f3b9cc0c0eb8203a4 Mon Sep 17 00:00:00 2001 From: Kaalleen <36401965+kaalleen@users.noreply.github.com> Date: Sun, 8 Apr 2018 17:35:24 +0200 Subject: [PATCH 02/23] Update operator_overview.html --- print/templates/operator_overview.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/print/templates/operator_overview.html b/print/templates/operator_overview.html index 25048ab76..c9706dbe8 100644 --- a/print/templates/operator_overview.html +++ b/print/templates/operator_overview.html @@ -27,7 +27,7 @@
{{ svg_overview|safe }} -
{{ _('Scale') }} {{ svg_scale }}
+
{{ _('Scale') }} {{ svg_scale }}%
{% include 'footer.html' %} From 29d62264328e88ec8b1e45704aad85304441e8f3 Mon Sep 17 00:00:00 2001 From: Kaalleen <36401965+kaalleen@users.noreply.github.com> Date: Sun, 8 Apr 2018 17:35:40 +0200 Subject: [PATCH 03/23] Update print_detail.html --- print/templates/print_detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/print/templates/print_detail.html b/print/templates/print_detail.html index 18a70bbaf..2453faa4e 100644 --- a/print/templates/print_detail.html +++ b/print/templates/print_detail.html @@ -17,7 +17,7 @@
{{color_block.svg_preview|safe}} -
{{ _('Scale') }} {{ svg_scale }}
+
{{ _('Scale') }} {{ svg_scale }}%
From 3e00ba26da77add9bdb1315b6a1993c0b53135f2 Mon Sep 17 00:00:00 2001 From: Kaalleen <36401965+kaalleen@users.noreply.github.com> Date: Sun, 8 Apr 2018 17:35:54 +0200 Subject: [PATCH 04/23] Update print_overview.html --- print/templates/print_overview.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/print/templates/print_overview.html b/print/templates/print_overview.html index f5632cebc..dd2a13587 100644 --- a/print/templates/print_overview.html +++ b/print/templates/print_overview.html @@ -27,7 +27,7 @@
{{ svg_overview|safe }} -
{{ _('Scale') }} {{ svg_scale }}
+
{{ _('Scale') }} {{ svg_scale }}%
From 15222ed4c39bab5e19b622e9179f7abc88c5b1da Mon Sep 17 00:00:00 2001 From: kalleen Date: Mon, 9 Apr 2018 19:20:02 +0200 Subject: [PATCH 05/23] add buttons to quickly adjust svg size --- print/resources/inkstitch.js | 90 ++++++++++++++++++-------- print/resources/style.css | 11 ++++ print/templates/operator_overview.html | 8 ++- print/templates/print_detail.html | 4 ++ print/templates/print_overview.html | 4 ++ 5 files changed, 89 insertions(+), 28 deletions(-) diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js index e804e3255..26e88f38a 100644 --- a/print/resources/inkstitch.js +++ b/print/resources/inkstitch.js @@ -13,49 +13,64 @@ function setPageNumbers() { }); } -// set preview svg scale to fit into its box -function scaleInksimulation() { - $('.inksimulation').each(function() { +// Scale SVG (fit || full size) +function scaleSVG(element, scale = 'fit') { + + // always center svg + transform = "translate(-50%, -50%)"; + + if(scale == 'fit') { var scale = Math.min( - $(this).width() / $(this).find('svg').width(), - $(this).height() / $(this).find('svg').height() + element.width() / element.find('svg').width(), + element.height() / element.find('svg').height() ); + // Do not scale to more than 100% + scale = (scale <= 1) ? scale : 1; + } + + transform += " scale(" + scale + ")"; + var label = parseInt(scale*100); - // center the SVG - transform = "translate(-50%, -50%)"; + element.find('svg').css({ transform: transform }); + element.find('figcaption span').text(label); +} - if(scale <= 1) { - transform += " scale(" + scale + ")"; - label = parseInt(scale*100); - } else { - label = "100"; - } +// set preview svg scale to fit into its box +function scaleAllSvg(element = "") { - $(this).find('svg').css({ transform: transform }); - $(this).find('figcaption span').text(label); - }); + if(element == "") { + $('.inksimulation').each(function() { + scaleSVG($(this)); + }); + } } $(function() { setTimeout(ping, 1000); setPageNumbers(); - scaleInksimulation(); + scaleAllSvg(); - /* Mousewheel scaling */ - $('figure.inksimulation').on( 'DOMMouseScroll mousewheel', function ( event ) { - var transform = $(this).find('svg').css('transform').match(/-?[\d\.]+/g); - var scale = parseFloat(transform[0]); - if( scale > 0.01 && (event.originalEvent.detail > 0 || event.originalEvent.wheelDelta < 0)) { + /* SCALING AND MOVING SVG */ + + /* Mousewheel scaling */ + $('figure.inksimulation').on( 'DOMMouseScroll mousewheel', function (e) { + + var svg = $(this).find('svg'); + var transform = svg.css('transform').match(/-?[\d\.]+/g); + var scale = parseFloat(transform[0]); + + if( scale > 0.01 && (e.originalEvent.detail > 0 || e.originalEvent.wheelDelta < 0)) { // scroll down scale -= 0.01; } else { //scroll up scale += 0.01; } + // set modified scale transform[0] = scale; transform[3] = scale; - $(this).find('svg').css({ transform: 'matrix(' + transform + ')' }); + svg.css({ transform: 'matrix(' + transform + ')' }); // set scale caption text $(this).find("span").text(parseInt(scale*100)); @@ -64,6 +79,18 @@ $(function() { return false; }); + /* Fit SVG */ + $('button.svg-fit').click(function() { + var svgfigure = $(this).closest('figure'); + scaleSVG(svgfigure, 'fit'); + }); + + /* Fit SVG */ + $('button.svg-full').click(function() { + var svgfigure = $(this).closest('figure'); + scaleSVG(svgfigure, '1'); + }); + /* Drag SVG */ $('figure.inksimulation').on('mousedown', function(e) { $(this).data('p0', { x: e.pageX, y: e.pageY }); @@ -88,9 +115,21 @@ $(function() { // 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 content = $(this).html(); var field_name = $(this).attr('data-field-name'); $('[data-field-name="' + field_name + '"]').html(content); + + /* change svg scale */ + if(field_name == 'svg-scale') { + var scale = parseInt(content); + var svg = $(this).parent().siblings('svg'); + var transform = svg.css('transform').match(/-?[\d\.]+/g); + + transform[0] = scale / 100; + transform[3] = scale / 100; + svg.css({ transform: 'matrix(' + transform + ')' }); + } + }); }); @@ -98,7 +137,6 @@ $(function() { if (e.which == 13) { // pressing enter defocuses the element this.blur(); - // also suppress the enter keystroke to avoid adding a new line return false; } else { @@ -145,7 +183,7 @@ $(function() { $(':checkbox').change(function() { $('.' + this.id).toggle(); setPageNumbers(); - scaleInksimulation(); + scaleAllSvg(); }); }); diff --git a/print/resources/style.css b/print/resources/style.css index 824f8dcef..146cfacc5 100644 --- a/print/resources/style.css +++ b/print/resources/style.css @@ -69,6 +69,10 @@ page-break-after: always; margin: 0 !important; } + + figure.inksimulation div { + display: none; + } .ui { display: none; @@ -390,6 +394,13 @@ body { line-height: 12pt; margin: 2.5mm; } + + figure.inksimulation div { + position: absolute; + bottom: 0; + left: 0; + right: 0; + } /* Color Swatches */ diff --git a/print/templates/operator_overview.html b/print/templates/operator_overview.html index c9706dbe8..ce28ae8e5 100644 --- a/print/templates/operator_overview.html +++ b/print/templates/operator_overview.html @@ -25,9 +25,13 @@
-
+
{{ svg_overview|safe }} -
{{ _('Scale') }} {{ svg_scale }}%
+
{{ _('Scale') }} {{ svg_scale }}%
+
+ + +
{% include 'footer.html' %} diff --git a/print/templates/print_detail.html b/print/templates/print_detail.html index 2453faa4e..b8b748b36 100644 --- a/print/templates/print_detail.html +++ b/print/templates/print_detail.html @@ -18,6 +18,10 @@
{{color_block.svg_preview|safe}}
{{ _('Scale') }} {{ svg_scale }}%
+
+ + +
diff --git a/print/templates/print_overview.html b/print/templates/print_overview.html index dd2a13587..d32cf1e99 100644 --- a/print/templates/print_overview.html +++ b/print/templates/print_overview.html @@ -28,6 +28,10 @@
{{ svg_overview|safe }}
{{ _('Scale') }} {{ svg_scale }}%
+
+ + +
From 75aa656c2cfb7ee4189c31a6c7a0d23d83b4d03a Mon Sep 17 00:00:00 2001 From: kalleen Date: Wed, 11 Apr 2018 18:57:55 +0200 Subject: [PATCH 06/23] add apply to all button for svg transforms --- embroider_print.py | 2 +- print/resources/inkstitch.js | 96 ++++++++++++++++---------- print/resources/style.css | 11 +++ print/templates/operator_overview.html | 7 +- print/templates/print_detail.html | 5 +- print/templates/print_overview.html | 5 +- 6 files changed, 81 insertions(+), 45 deletions(-) diff --git a/embroider_print.py b/embroider_print.py index 96c3255d2..c0b49bf2c 100644 --- a/embroider_print.py +++ b/embroider_print.py @@ -295,7 +295,7 @@ class Print(InkstitchExtension): 'estimated_thread': '', # TODO }, svg_overview = overview_svg, - svg_scale = '100%', + svg_transform = '', # Format: matrix(0.2, 0, 0, 0.2, 0, 0) color_blocks = stitch_plan.color_blocks, ) diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js index 26e88f38a..c6e88e522 100644 --- a/print/resources/inkstitch.js +++ b/print/resources/inkstitch.js @@ -35,14 +35,23 @@ function scaleSVG(element, scale = 'fit') { element.find('figcaption span').text(label); } -// set preview svg scale to fit into its box -function scaleAllSvg(element = "") { - - if(element == "") { - $('.inksimulation').each(function() { - scaleSVG($(this)); +// set preview svg scale to fit into its box if transform is not set +function scaleAllSvg() { + $('.page').each(function() { + if( $(this).css('display') != 'none' ) { + if( $(this).find('.inksimulation svg').css('transform') == 'none') { + if( $(this).find('.inksimulation span').text() == '') { + scaleSVG($(this).find('.inksimulation')); + } + else { + var transform = $(this).find('.inksimulation span').text(); + var scale = transform.match(/-?[\d\.]+/g)[0]; + $(this).find('.inksimulation svg').css({ transform: transform }); + $(this).find('.inksimulation span').text(parseInt(scale*100)); + } + } + } }); - } } $(function() { @@ -54,29 +63,31 @@ $(function() { /* Mousewheel scaling */ $('figure.inksimulation').on( 'DOMMouseScroll mousewheel', function (e) { + if(event.ctrlKey == true) { - var svg = $(this).find('svg'); - var transform = svg.css('transform').match(/-?[\d\.]+/g); - var scale = parseFloat(transform[0]); - - if( scale > 0.01 && (e.originalEvent.detail > 0 || e.originalEvent.wheelDelta < 0)) { - // scroll down - scale -= 0.01; - } else { - //scroll up - scale += 0.01; - } - - // set modified scale - transform[0] = scale; - transform[3] = scale; - svg.css({ transform: 'matrix(' + transform + ')' }); - - // set scale caption text - $(this).find("span").text(parseInt(scale*100)); + var svg = $(this).find('svg'); + var transform = svg.css('transform').match(/-?[\d\.]+/g); + var scale = parseFloat(transform[0]); + + if( scale > 0.01 && (e.originalEvent.detail > 0 || e.originalEvent.wheelDelta < 0)) { + // scroll down + scale -= 0.01; + } else { + //scroll up + scale += 0.01; + } + + // set modified scale + transform[0] = scale; + transform[3] = scale; + svg.css({ transform: 'matrix(' + transform + ')' }); + + // set scale caption text + $(this).find("span").text(parseInt(scale*100)); - //prevent page fom scrolling - return false; + //prevent page fom scrolling + return false; + } }); /* Fit SVG */ @@ -85,7 +96,7 @@ $(function() { scaleSVG(svgfigure, 'fit'); }); - /* Fit SVG */ + /* Full Size SVG */ $('button.svg-full').click(function() { var svgfigure = $(this).closest('figure'); scaleSVG(svgfigure, '1'); @@ -96,6 +107,7 @@ $(function() { $(this).data('p0', { x: e.pageX, y: e.pageY }); $(this).css({cursor: 'move'}); }).on('mouseup', function(e) { + $(this).css({cursor: 'auto'}); var p0 = $(this).data('p0'), p1 = { x: e.pageX, y: e.pageY }, d = Math.sqrt(Math.pow(p1.x - p0.x, 2) + Math.pow(p1.y - p0.y, 2)); @@ -105,21 +117,27 @@ $(function() { transform[5] = parseFloat(transform[5]) + parseFloat(p1.y-p0.y); // set modified translate $(this).find('svg').css({ transform: 'matrix(' + transform + ')' }); - $(this).css({cursor: 'auto'}); } - }) + }) + + /* Apply transforms to All */ + $('button.svg-apply').click(function() { + var transform = $(this).parent().siblings('svg').css('transform'); + var scale = transform.match(/-?[\d\.]+/g)[0]; + $('.inksimulation').each(function() { + $(this).find('svg').css({ transform: transform }); + $(this).find("span").text(parseInt(scale*100)); + + }) + }); /* 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() { + /* change svg scale */ var content = $(this).html(); var field_name = $(this).attr('data-field-name'); - $('[data-field-name="' + field_name + '"]').html(content); - - /* change svg scale */ if(field_name == 'svg-scale') { var scale = parseInt(content); var svg = $(this).parent().siblings('svg'); @@ -129,7 +147,11 @@ $(function() { transform[3] = scale / 100; svg.css({ transform: 'matrix(' + transform + ')' }); } - + /* When we focus out from a contenteditable field, we want to + set the same content to all fields with the same classname */ + else { + $('[data-field-name="' + field_name + '"]').html(content); + } }); }); diff --git a/print/resources/style.css b/print/resources/style.css index 146cfacc5..333af7aa8 100644 --- a/print/resources/style.css +++ b/print/resources/style.css @@ -393,6 +393,8 @@ body { font-weight: bold; line-height: 12pt; margin: 2.5mm; + background: rgba(255, 255, 255, 0.73); + padding: 5px; } figure.inksimulation div { @@ -400,6 +402,15 @@ body { bottom: 0; left: 0; right: 0; + margin-left: auto; + margin-right: auto; + width: fit-content; + } + + figure.inksimulation button { + border: none; + background: grey; + color: white; } /* Color Swatches */ diff --git a/print/templates/operator_overview.html b/print/templates/operator_overview.html index ce28ae8e5..38da77466 100644 --- a/print/templates/operator_overview.html +++ b/print/templates/operator_overview.html @@ -25,12 +25,13 @@
-
+
{{ svg_overview|safe }} -
{{ _('Scale') }} {{ svg_scale }}%
+
{{ _('Scale') }} {{ svg_transform }}%
- + +
diff --git a/print/templates/print_detail.html b/print/templates/print_detail.html index b8b748b36..eddcf36fa 100644 --- a/print/templates/print_detail.html +++ b/print/templates/print_detail.html @@ -15,12 +15,13 @@
-
+
{{color_block.svg_preview|safe}} -
{{ _('Scale') }} {{ svg_scale }}%
+
{{ _('Scale') }} {{ svg_transform }}%
+
diff --git a/print/templates/print_overview.html b/print/templates/print_overview.html index d32cf1e99..7a396b4a4 100644 --- a/print/templates/print_overview.html +++ b/print/templates/print_overview.html @@ -25,12 +25,13 @@
-
+
{{ svg_overview|safe }} -
{{ _('Scale') }} {{ svg_scale }}%
+
{{ _('Scale') }} {{ svg_transform }}%
+
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 @@
-

{{ job.title }}

-

{{ client }}

-

{{ purchase_order }}

+

+

+

{{ date|datetimeformat(_('%Y.%m.%d')) }}
From 78532e8efec1b39d8f7b9286508cc10e5050fe69 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Fri, 13 Apr 2018 23:39:49 -0400 Subject: [PATCH 09/23] populate fields on load --- print/resources/inkstitch.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js index ed26d3671..15cf494ac 100644 --- a/print/resources/inkstitch.js +++ b/print/resources/inkstitch.js @@ -48,10 +48,17 @@ $(function() { $('[contenteditable="true"]').on('focusout', function() { var content = $(this).html(); var field_name = $(this).attr('data-field-name'); - $('[data-field-name="' + field_name + '"]').html(content); + $('[data-field-name="' + field_name + '"]').text(content); $.post('/metadata/' + field_name + '/set', {value: content}); }); + // load up initial metadata values + $.getJSON('/metadata', function(metadata) { + $.each(metadata, function(field_name, value) { + $('[data-field-name="' + field_name + '"]').text(value); + }); + }); + $('[contenteditable="true"]').keypress(function(e) { if (e.which == 13) { // pressing enter defocuses the element From f9b90d31b7730103e296b0c2509885b07000eac4 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 14 Apr 2018 20:39:59 -0400 Subject: [PATCH 10/23] round-trip editable fields and checkboxes --- embroider_print.py | 2 +- inkstitch/extensions.py | 16 +++++++++++++--- print/resources/inkstitch.js | 29 ++++++++++++++++++++++++++--- print/templates/ui.html | 8 ++++---- 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/embroider_print.py b/embroider_print.py index 79d9bf45c..a8daece48 100644 --- a/embroider_print.py +++ b/embroider_print.py @@ -133,7 +133,7 @@ class PrintPreviewServer(Thread): @self.app.route('/metadata//set', methods=['POST']) def set_field(field_name): - self.metadata[field_name] = request.form['value'] + self.metadata[field_name] = request.json['value'] return "OK" @self.app.route('/metadata/', methods=['GET']) diff --git a/inkstitch/extensions.py b/inkstitch/extensions.py index 57d292313..0e0e49f82 100644 --- a/inkstitch/extensions.py +++ b/inkstitch/extensions.py @@ -1,5 +1,6 @@ import inkex import re +import json 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 @@ -49,15 +50,23 @@ class InkStitchMetadata(MutableMapping): # implement these five methods and we get a full dict-like interface. def __setitem__(self, name, value): - self[name].text = value + self._find_item(name).text = json.dumps(value) - def __getitem__(self, name): + def _find_item(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 + return item + + def __getitem__(self, name): + item = self._find_item(name) + + try: + return json.loads(item.text) + except ValueError: + return None def __delitem__(self, name): item = self[name] @@ -71,6 +80,7 @@ class InkStitchMetadata(MutableMapping): yield strip_namespace(child.tag) def __len__(self): + i = 0 for i, item in enumerate(self): pass diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js index 15cf494ac..8123a31fc 100644 --- a/print/resources/inkstitch.js +++ b/print/resources/inkstitch.js @@ -1,3 +1,12 @@ +$.postJSON = function(url, data, success=null) { + return $.ajax(url, { + type: 'POST', + data: JSON.stringify(data), + contentType: 'application/json', + success: success + }); +}; + function ping() { $.get("/ping") .done(function() { setTimeout(ping, 1000) }) @@ -49,13 +58,22 @@ $(function() { var content = $(this).html(); var field_name = $(this).attr('data-field-name'); $('[data-field-name="' + field_name + '"]').text(content); - $.post('/metadata/' + field_name + '/set', {value: content}); + $.postJSON('/metadata/' + field_name + '/set', {value: content}); }); // load up initial metadata values $.getJSON('/metadata', function(metadata) { $.each(metadata, function(field_name, value) { - $('[data-field-name="' + field_name + '"]').text(value); + $('[data-field-name="' + field_name + '"]').each(function(i, item) { + console.log(item); + if ($(item).is(':checkbox')) { + console.log("is a checkbox"); + $(item).prop('checked', value).trigger('change'); + } else { + console.log("is not a checkbox"); + $(item).text(value); + } + }); }); }); @@ -108,9 +126,14 @@ $(function() { //Checkbox $(':checkbox').change(function() { - $('.' + this.id).toggle(); + var checked = $(this).prop('checked'); + var field_name = $(this).attr('data-field-name'); + + $('.' + field_name).toggle(checked); setPageNumbers(); scaleInksimulation(); + + $.postJSON('/metadata/' + field_name + '/set', {value: checked}); }); }); diff --git a/print/templates/ui.html b/print/templates/ui.html index 078f1a4ca..9acdd1bc2 100644 --- a/print/templates/ui.html +++ b/print/templates/ui.html @@ -24,10 +24,10 @@
{{ ('Print Layouts') }}: -

-

-

-

+

+

+

+

From 7b0804562e4ce3b440e67de6cb001ac021326990 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Mon, 16 Apr 2018 20:17:07 -0400 Subject: [PATCH 11/23] add 'save as defaults' button --- embroider_print.py | 45 ++++++++++++++++++++++++----- inkstitch/extensions.py | 6 +++- messages.po | 11 ++++++- print/resources/inkstitch.js | 54 ++++++++++++++++++++++++++++------- print/resources/style.css | 25 +++++++++++++++- print/templates/headline.html | 5 +++- print/templates/ui.html | 5 ++-- 7 files changed, 127 insertions(+), 24 deletions(-) diff --git a/embroider_print.py b/embroider_print.py index a8daece48..fa144d1d7 100644 --- a/embroider_print.py +++ b/embroider_print.py @@ -11,6 +11,8 @@ import time import logging from copy import deepcopy import wx +import appdirs +import json import inkex import inkstitch @@ -33,6 +35,29 @@ def datetimeformat(value, format='%Y/%m/%d'): return value.strftime(format) +def defaults_path(): + defaults_dir = appdirs.user_config_dir('inkstitch') + + if not os.path.exists(defaults_dir): + os.makedirs(defaults_dir) + + return os.path.join(defaults_dir, 'print_settings.json') + + +def load_defaults(): + try: + with open(defaults_path(), 'r') as defaults_file: + defaults = json.load(defaults_file) + return defaults + except: + return {} + + +def save_defaults(defaults): + with open(defaults_path(), 'w') as defaults_file: + json.dump(defaults, defaults_file) + + def open_url(url): # Avoid spurious output from xdg-open. Any output on stdout will crash # inkscape. @@ -131,20 +156,26 @@ class PrintPreviewServer(Thread): # nothing to do here -- request_started() will restart the watcher return "OK" - @self.app.route('/metadata//set', methods=['POST']) + @self.app.route('/settings/', methods=['POST']) def set_field(field_name): self.metadata[field_name] = request.json['value'] return "OK" - @self.app.route('/metadata/', methods=['GET']) + @self.app.route('/settings/', 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)) + @self.app.route('/settings', methods=['GET']) + def get_settings(): + settings = {} + settings.update(load_defaults()) + settings.update(self.metadata) + return jsonify(settings) + + @self.app.route('/defaults', methods=['POST']) + def set_defaults(): + save_defaults(request.json['value']) + return "OK" def stop(self): # for whatever reason, shutting down only seems possible in diff --git a/inkstitch/extensions.py b/inkstitch/extensions.py index 0e0e49f82..70341cd3d 100644 --- a/inkstitch/extensions.py +++ b/inkstitch/extensions.py @@ -42,7 +42,11 @@ class InkStitchMetadata(MutableMapping): metadata = self.document.find(SVG_METADATA_TAG) if metadata is None: - metadata = inkex.etree.SubElement(self.document, SVG_METADATA_TAG) + metadata = inkex.etree.SubElement(self.document.getroot(), SVG_METADATA_TAG) + + # move it so that it goes right after the first element, sodipodi:namedview + self.document.getroot().remove(metadata) + self.document.getroot().insert(1, metadata) return metadata diff --git a/messages.po b/messages.po index aa2a05239..c6ff2fde2 100644 --- a/messages.po +++ b/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2018-04-02 22:11-0400\n" +"POT-Creation-Date: 2018-04-16 20:17-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -393,3 +393,12 @@ msgstr "" msgid "Printing Size" msgstr "" +msgid "Print Layouts" +msgstr "" + +msgid "Includes all settings visible here and also the icon." +msgstr "" + +msgid "Save as defaults" +msgstr "" + diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js index 8123a31fc..ab0b587da 100644 --- a/print/resources/inkstitch.js +++ b/print/resources/inkstitch.js @@ -58,20 +58,22 @@ $(function() { var content = $(this).html(); var field_name = $(this).attr('data-field-name'); $('[data-field-name="' + field_name + '"]').text(content); - $.postJSON('/metadata/' + field_name + '/set', {value: content}); + $.postJSON('/settings/' + field_name, {value: content}); }); // load up initial metadata values - $.getJSON('/metadata', function(metadata) { - $.each(metadata, function(field_name, value) { + $.getJSON('/settings', function(settings) { + $.each(settings, function(field_name, value) { $('[data-field-name="' + field_name + '"]').each(function(i, item) { - console.log(item); - if ($(item).is(':checkbox')) { - console.log("is a checkbox"); - $(item).prop('checked', value).trigger('change'); + var item = $(item); + if (item.is(':checkbox')) { + item.prop('checked', value).trigger('change'); + } else if (item.is('img')) { + item.attr('src', value); + } else if (item.is('select')) { + item.val(value).trigger('change'); } else { - console.log("is not a checkbox"); - $(item).text(value); + item.text(value); } }); }); @@ -121,7 +123,9 @@ $(function() { // Paper Size $('select#printing-size').change(function(){ - $('.page').toggleClass('a4'); + var size = $(this).find(':selected').val(); + $('.page').toggleClass('a4', size == 'a4'); + $.postJSON('/settings/paper-size', {value: size}); }); //Checkbox @@ -133,8 +137,36 @@ $(function() { setPageNumbers(); scaleInksimulation(); - $.postJSON('/metadata/' + field_name + '/set', {value: checked}); + $.postJSON('/settings/' + field_name, {value: checked}); }); + // Logo + $('#logo-picker').change(function(e) { + var file = e.originalEvent.srcElement.files[0]; + var reader = new FileReader(); + reader.onloadend = function() { + var data = reader.result; + $('figure.brandlogo img').attr('src', data); + $.postJSON('/settings/logo', {value: data}); + }; + reader.readAsDataURL(file); + }); + + // "save as defaults" button + $('#save-settings').click(function(e) { + var settings = {}; + settings["client-overview"] = $("[data-field-name='client-overview']").is(':checked'); + settings["client-detailedview"] = $("[data-field-name='client-detailedview']").is(':checked'); + settings["operator-overview"] = $("[data-field-name='operator-overview']").is(':checked'); + settings["operator-detailedview"] = $("[data-field-name='operator-detailedview']").is(':checked'); + settings["paper-size"] = $('select#printing-size').find(':selected').val(); + + var logo = $("figure.brandlogo img").attr('src'); + if (logo.startsWith("data:")) { + settings["logo"] = logo; + } + + $.postJSON('/defaults', {'value': settings}); + }); }); diff --git a/print/resources/style.css b/print/resources/style.css index 824f8dcef..8be2370d4 100644 --- a/print/resources/style.css +++ b/print/resources/style.css @@ -226,6 +226,10 @@ body { cursor: pointer; } + #settings-ui fieldset { + margin-bottom: 1em; + } + /* Header */ @@ -247,11 +251,30 @@ body { margin: 2.5mm; } + figure.brandlogo label { + display: block; + width: 100%; + height: 100%; + line-height: 30mm; + text-align: center; + } + figure.brandlogo img { max-width: 30mm; max-height: 30mm; + display: inline; + vertical-align: middle; } - + + /* hide the actual file picker control, since we just want them to click the + * image instead + */ + #logo-picker { + width: 0px; + height: 0px; + opacity: 0%; + } + .operator-detailedview figure.brandlogo { height: 20mm; width: 30mm; diff --git a/print/templates/headline.html b/print/templates/headline.html index cbc9c43a0..421202e4e 100644 --- a/print/templates/headline.html +++ b/print/templates/headline.html @@ -1,5 +1,8 @@
diff --git a/print/templates/ui.html b/print/templates/ui.html index 9acdd1bc2..f7246962a 100644 --- a/print/templates/ui.html +++ b/print/templates/ui.html @@ -15,7 +15,7 @@

{{ _('Settings') }}

{{ _('Printing Size') }}: - @@ -23,11 +23,12 @@

- {{ ('Print Layouts') }}: + {{ _('Print Layouts') }}:

+
From 51c7febb4ab845f609c2613d3180f8456f38b285 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Mon, 16 Apr 2018 21:15:39 -0400 Subject: [PATCH 12/23] scale proportionally rather than an absolute amount --- print/resources/inkstitch.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js index 253e92430..2323ec7ae 100644 --- a/print/resources/inkstitch.js +++ b/print/resources/inkstitch.js @@ -78,12 +78,14 @@ $(function() { var transform = svg.css('transform').match(/-?[\d\.]+/g); var scale = parseFloat(transform[0]); - if( scale > 0.01 && (e.originalEvent.detail > 0 || e.originalEvent.wheelDelta < 0)) { - // scroll down - scale -= 0.01; + if (e.originalEvent.detail > 0 || e.originalEvent.wheelDelta < 0) { + // scroll down = zoom out + scale *= 0.97; + if (scale < 0.01) + scale = 0.01; } else { //scroll up - scale += 0.01; + scale *= 1.03; } // set modified scale From 32af3d4f89d7f3bcc26e231223ce64bcd7d22122 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 17 Apr 2018 15:01:38 -0400 Subject: [PATCH 13/23] tidy up drag code and update continuously --- print/resources/inkstitch.js | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js index 2323ec7ae..497b87d49 100644 --- a/print/resources/inkstitch.js +++ b/print/resources/inkstitch.js @@ -115,21 +115,24 @@ $(function() { /* Drag SVG */ $('figure.inksimulation').on('mousedown', function(e) { - $(this).data('p0', { x: e.pageX, y: e.pageY }); - $(this).css({cursor: 'move'}); + var p0 = { x: e.pageX, y: e.pageY }; + var start_transform = $(this).find('svg').css('transform').match(/-?[\d\.]+/g); + var start_offset = { x: parseFloat(start_transform[4]), y: parseFloat(start_transform[5]) }; + + $(this).css({cursor: 'move'}); + $(this).on('mousemove', function(e) { + var p1 = { x: e.pageX, y: e.pageY }; + // set modified translate + var transform = $(this).find('svg').css('transform').match(/-?[\d\.]+/g); + transform[4] = start_offset.x + (p1.x - p0.x); + transform[5] = start_offset.y + (p1.y - p0.y); + $(this).find('svg').css({ transform: 'matrix(' + transform + ')' }); + }); }).on('mouseup', function(e) { - $(this).css({cursor: 'auto'}); - var p0 = $(this).data('p0'), - p1 = { x: e.pageX, y: e.pageY }, - d = Math.sqrt(Math.pow(p1.x - p0.x, 2) + Math.pow(p1.y - p0.y, 2)); - if (d > 4) { - var transform = $(this).find('svg').css('transform').match(/-?[\d\.]+/g); - transform[4] = parseFloat(transform[4]) + parseFloat(p1.x-p0.x); - transform[5] = parseFloat(transform[5]) + parseFloat(p1.y-p0.y); - // set modified translate - $(this).find('svg').css({ transform: 'matrix(' + transform + ')' }); - } - }) + $(this).css({cursor: 'auto'}); + $(this).data('p0', null); + $(this).off('mousemove'); + }); /* Apply transforms to All */ $('button.svg-apply').click(function() { From cb1b3756d8d029c7f92e1748e9eb42a78d89b5a2 Mon Sep 17 00:00:00 2001 From: Kaalleen <36401965+kaalleen@users.noreply.github.com> Date: Wed, 18 Apr 2018 19:20:33 +0200 Subject: [PATCH 14/23] Update inkstitch.js fixed scrolling function event variable --- print/resources/inkstitch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js index 497b87d49..0571f432f 100644 --- a/print/resources/inkstitch.js +++ b/print/resources/inkstitch.js @@ -72,7 +72,7 @@ $(function() { /* Mousewheel scaling */ $('figure.inksimulation').on( 'DOMMouseScroll mousewheel', function (e) { - if(event.ctrlKey == true) { + if(e.ctrlKey == true) { var svg = $(this).find('svg'); var transform = svg.css('transform').match(/-?[\d\.]+/g); From 540bf2062a87da5629c7ed98300b39caeffe397f Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 17 Apr 2018 15:16:50 -0400 Subject: [PATCH 15/23] fit scales up svg if necessary --- print/resources/inkstitch.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js index 0571f432f..8c5e8fa6d 100644 --- a/print/resources/inkstitch.js +++ b/print/resources/inkstitch.js @@ -33,8 +33,6 @@ function scaleSVG(element, scale = 'fit') { element.width() / element.find('svg').width(), element.height() / element.find('svg').height() ); - // Do not scale to more than 100% - scale = (scale <= 1) ? scale : 1; } transform += " scale(" + scale + ")"; From 51741745b0bb691a55b642a2ea938ac74324da2d Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 17 Apr 2018 15:48:38 -0400 Subject: [PATCH 16/23] don't get transform from html --- embroider_print.py | 1 - print/resources/inkstitch.js | 53 +++++++++++--------------- print/templates/operator_overview.html | 2 +- print/templates/print_detail.html | 2 +- print/templates/print_overview.html | 2 +- 5 files changed, 25 insertions(+), 35 deletions(-) diff --git a/embroider_print.py b/embroider_print.py index bc96b2cd0..2aa76ec62 100644 --- a/embroider_print.py +++ b/embroider_print.py @@ -343,7 +343,6 @@ class Print(InkstitchExtension): 'estimated_thread': '', # TODO }, svg_overview = overview_svg, - svg_transform = '', # Format: matrix(0.2, 0, 0, 0.2, 0, 0) color_blocks = stitch_plan.color_blocks, ) diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js index 8c5e8fa6d..825130c88 100644 --- a/print/resources/inkstitch.js +++ b/print/resources/inkstitch.js @@ -39,24 +39,14 @@ function scaleSVG(element, scale = 'fit') { var label = parseInt(scale*100); element.find('svg').css({ transform: transform }); - element.find('figcaption span').text(label); + element.find('.scale').text(label); } // set preview svg scale to fit into its box if transform is not set function scaleAllSvg() { $('.page').each(function() { - if( $(this).css('display') != 'none' ) { - if( $(this).find('.inksimulation svg').css('transform') == 'none') { - if( $(this).find('.inksimulation span').text() == '') { - scaleSVG($(this).find('.inksimulation')); - } - else { - var transform = $(this).find('.inksimulation span').text(); - var scale = transform.match(/-?[\d\.]+/g)[0]; - $(this).find('.inksimulation svg').css({ transform: transform }); - $(this).find('.inksimulation span').text(parseInt(scale*100)); - } - } + if( $(this).find('.inksimulation svg').css('transform') == 'none') { + scaleSVG($(this).find('.inksimulation'), 'fit'); } }); } @@ -64,7 +54,6 @@ function scaleAllSvg() { $(function() { setTimeout(ping, 1000); setPageNumbers(); - scaleAllSvg(); /* SCALING AND MOVING SVG */ @@ -92,7 +81,7 @@ $(function() { svg.css({ transform: 'matrix(' + transform + ')' }); // set scale caption text - $(this).find("span").text(parseInt(scale*100)); + $(this).find(".scale").text(parseInt(scale*100)); //prevent page fom scrolling return false; @@ -138,7 +127,7 @@ $(function() { var scale = transform.match(/-?[\d\.]+/g)[0]; $('.inksimulation').each(function() { $(this).find('svg').css({ transform: transform }); - $(this).find("span").text(parseInt(scale*100)); + $(this).find(".scale").text(parseInt(scale*100)); }) }); @@ -167,20 +156,22 @@ $(function() { // load up initial metadata values $.getJSON('/settings', function(settings) { - $.each(settings, function(field_name, value) { - $('[data-field-name="' + field_name + '"]').each(function(i, item) { - var item = $(item); - if (item.is(':checkbox')) { - item.prop('checked', value).trigger('change'); - } else if (item.is('img')) { - item.attr('src', value); - } else if (item.is('select')) { - item.val(value).trigger('change'); - } else { - item.text(value); - } - }); - }); + $.each(settings, function(field_name, value) { + $('[data-field-name="' + field_name + '"]').each(function(i, item) { + var item = $(item); + if (item.is(':checkbox')) { + item.prop('checked', value).trigger('change'); + } else if (item.is('img')) { + item.attr('src', value); + } else if (item.is('select')) { + item.val(value).trigger('change'); + } else { + item.text(value); + } + }); + }); + // wait until page size is set (if they've specified one) and then scale SVGs to fit + setTimeout(function() { scaleAllSvg() }, 500); }); $('[contenteditable="true"]').keypress(function(e) { @@ -229,6 +220,7 @@ $(function() { $('select#printing-size').change(function(){ var size = $(this).find(':selected').val(); $('.page').toggleClass('a4', size == 'a4'); + console.log("" + Date.now() + "paper size changed"); $.postJSON('/settings/paper-size', {value: size}); }); @@ -239,7 +231,6 @@ $(function() { $('.' + field_name).toggle(checked); setPageNumbers(); - scaleAllSvg(); $.postJSON('/settings/' + field_name, {value: checked}); }); diff --git a/print/templates/operator_overview.html b/print/templates/operator_overview.html index 38da77466..8615c42ac 100644 --- a/print/templates/operator_overview.html +++ b/print/templates/operator_overview.html @@ -27,7 +27,7 @@
{{ svg_overview|safe }} -
{{ _('Scale') }} {{ svg_transform }}%
+
{{ _('Scale') }} %
diff --git a/print/templates/print_detail.html b/print/templates/print_detail.html index eddcf36fa..d3b064a51 100644 --- a/print/templates/print_detail.html +++ b/print/templates/print_detail.html @@ -17,7 +17,7 @@
{{color_block.svg_preview|safe}} -
{{ _('Scale') }} {{ svg_transform }}%
+
{{ _('Scale') }} %
diff --git a/print/templates/print_overview.html b/print/templates/print_overview.html index 7a396b4a4..f876ad70b 100644 --- a/print/templates/print_overview.html +++ b/print/templates/print_overview.html @@ -27,7 +27,7 @@
{{ svg_overview|safe }} -
{{ _('Scale') }} {{ svg_transform }}%
+
{{ _('Scale') }} %
From b5968dc11ff479f2c7cd5217c47a872fe04295e4 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 17 Apr 2018 15:55:52 -0400 Subject: [PATCH 17/23] fix formatting of dimensions --- print/templates/operator_detailedview.html | 2 +- print/templates/operator_overview.html | 2 +- print/templates/print_overview.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/print/templates/operator_detailedview.html b/print/templates/operator_detailedview.html index 633b9286d..92c3445f2 100644 --- a/print/templates/operator_detailedview.html +++ b/print/templates/operator_detailedview.html @@ -25,7 +25,7 @@ {{ _('Color Blocks') }}: {{ job.num_color_blocks }}

- {{ _('Design box size') }}: {{ job.dimensions }} + {{ _('Design box size') }}: {{ "%0.1fmm X %0.1fmm" | format(*job.dimensions) }} {{ _('Total thread used') }}: {{job.estimated_thread }} {{ _('Total stitch count') }}: {{job.num_stitches }}

diff --git a/print/templates/operator_overview.html b/print/templates/operator_overview.html index 8615c42ac..59b2e028a 100644 --- a/print/templates/operator_overview.html +++ b/print/templates/operator_overview.html @@ -11,7 +11,7 @@
-

{{ _('Design box size') }}:{{ job.dimensions }}

+

{{ _('Design box size') }}:{{ "%0.1fmm X %0.1fmm" | format(*job.dimensions) }}

{{ _('Total stitch count') }}:{{job.num_stitches }}

{{ _('Total thread used') }}:{{job.estimated_thread }}

diff --git a/print/templates/print_overview.html b/print/templates/print_overview.html index f876ad70b..62d2982c9 100644 --- a/print/templates/print_overview.html +++ b/print/templates/print_overview.html @@ -11,7 +11,7 @@
-

{{ _('Design box size') }}:{{ job.dimensions }}

+

{{ _('Design box size') }}:{{ "%0.1fmm X %0.1fmm" | format(*job.dimensions) }}

{{ _('Total stitch count') }}:{{job.num_stitches }}

{{ _('Total thread used') }}:{{job.estimated_thread }}

From a5900865c76b4b65c712ef6d39fda9eaae2c5007 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 18 Apr 2018 16:09:43 -0400 Subject: [PATCH 18/23] save and restore zoom and pan --- print/resources/inkstitch.js | 38 ++++++++++++++++++++------ print/templates/operator_overview.html | 2 +- print/templates/print_detail.html | 2 +- print/templates/print_overview.html | 2 +- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js index 825130c88..25a5354e3 100644 --- a/print/resources/inkstitch.js +++ b/print/resources/inkstitch.js @@ -51,6 +51,23 @@ function scaleAllSvg() { }); } +var saveTimerHandles = {}; + +function setSVGTransform(figure, transform) { + var field_name = $(figure).data('field-name'); + var scale = transform.match(/-?[\d\.]+/g)[0]; + figure.find('svg').css({ transform: transform }); + figure.find(".scale").text(parseInt(scale*100)); + + // avoid spamming updates + if (saveTimerHandles[field_name] != null) + clearTimeout(saveTimerHandles[field_name]); + + saveTimerHandles[field_name] = setTimeout(function() { + $.postJSON('/settings/' + field_name, {value: transform}); + }, 250); +} + $(function() { setTimeout(ping, 1000); setPageNumbers(); @@ -78,10 +95,8 @@ $(function() { // set modified scale transform[0] = scale; transform[3] = scale; - svg.css({ transform: 'matrix(' + transform + ')' }); - - // set scale caption text - $(this).find(".scale").text(parseInt(scale*100)); + + setSVGTransform($(this), 'matrix(' + transform + ')'); //prevent page fom scrolling return false; @@ -113,22 +128,25 @@ $(function() { var transform = $(this).find('svg').css('transform').match(/-?[\d\.]+/g); transform[4] = start_offset.x + (p1.x - p0.x); transform[5] = start_offset.y + (p1.y - p0.y); - $(this).find('svg').css({ transform: 'matrix(' + transform + ')' }); + + // I'd ike to use setSVGTransform() here but this code runs many + // times per second and it's just too CPU-intensive. + $(this).find('svg').css({transform: 'matrix(' + transform + ')'}); }); }).on('mouseup', function(e) { $(this).css({cursor: 'auto'}); $(this).data('p0', null); $(this).off('mousemove'); + + // set it using setSVGTransform() to ensure that it's saved to the server + setSVGTransform($(this), $(this).find('svg').css('transform')); }); /* Apply transforms to All */ $('button.svg-apply').click(function() { var transform = $(this).parent().siblings('svg').css('transform'); - var scale = transform.match(/-?[\d\.]+/g)[0]; $('.inksimulation').each(function() { - $(this).find('svg').css({ transform: transform }); - $(this).find(".scale").text(parseInt(scale*100)); - + setSVGTransform($(this), transform); }) }); @@ -165,6 +183,8 @@ $(function() { item.attr('src', value); } else if (item.is('select')) { item.val(value).trigger('change'); + } else if (item.is('figure.inksimulation')) { + setSVGTransform(item, value); } else { item.text(value); } diff --git a/print/templates/operator_overview.html b/print/templates/operator_overview.html index 59b2e028a..367d6d2bf 100644 --- a/print/templates/operator_overview.html +++ b/print/templates/operator_overview.html @@ -25,7 +25,7 @@
-
+
{{ svg_overview|safe }}
{{ _('Scale') }} %
diff --git a/print/templates/print_detail.html b/print/templates/print_detail.html index d3b064a51..714d33a24 100644 --- a/print/templates/print_detail.html +++ b/print/templates/print_detail.html @@ -15,7 +15,7 @@
-
+
{{color_block.svg_preview|safe}}
{{ _('Scale') }} %
diff --git a/print/templates/print_overview.html b/print/templates/print_overview.html index 62d2982c9..efcf5b2eb 100644 --- a/print/templates/print_overview.html +++ b/print/templates/print_overview.html @@ -25,7 +25,7 @@
-
+
{{ svg_overview|safe }}
{{ _('Scale') }} %
From 9148d5484462e342ab716a7b4b0859c7c298702a Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 18 Apr 2018 16:12:42 -0400 Subject: [PATCH 19/23] hide stderr but print exceptions --- embroider_print.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/embroider_print.py b/embroider_print.py index 2aa76ec62..0dbff9296 100644 --- a/embroider_print.py +++ b/embroider_print.py @@ -363,7 +363,18 @@ class Print(InkstitchExtension): if __name__ == '__main__': - #save_stderr() - effect = Print() - effect.affect() - #restore_stderr() + exception = None + + save_stderr() + try: + effect = Print() + effect.affect() + except: + exception = traceback.format_exc() + restore_stderr() + + if exception: + print >> sys.stderr, exception + sys.exit(1) + else: + sys.exit(0) From aea5919c63796efd748d724752a86773b79b7c3b Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 18 Apr 2018 16:29:06 -0400 Subject: [PATCH 20/23] add notes in operator detailed view switch to 12 color blocks per page to give more room for detailed notes --- messages.po | 7 +++++-- print/templates/index.html | 2 +- print/templates/operator_detailedview.html | 8 ++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/messages.po b/messages.po index 92b6aa61d..ce85878dd 100644 --- a/messages.po +++ b/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2018-04-16 21:03-0400\n" +"POT-Creation-Date: 2018-04-18 16:29-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -336,7 +336,7 @@ msgstr "" msgid "Stops and Trims" msgstr "" -msgid "Estimated Time" +msgid "Notes" msgstr "" msgid "Unique Colors" @@ -360,6 +360,9 @@ msgstr "" msgid "Total nr trims" msgstr "" +msgid "Enter operator notes..." +msgstr "" + msgid "Job estimated time" msgstr "" diff --git a/print/templates/index.html b/print/templates/index.html index 21ca74ab4..0c9cedfdc 100644 --- a/print/templates/index.html +++ b/print/templates/index.html @@ -23,7 +23,7 @@
{% include 'operator_overview.html' %}
{# operator detailed view #} - {% for color_block_part in color_blocks | batch(13) %} + {% for color_block_part in color_blocks | batch(12) %} {% set outer_loop = loop %}
{% include 'operator_detailedview.html' %}
{% endfor %} diff --git a/print/templates/operator_detailedview.html b/print/templates/operator_detailedview.html index 92c3445f2..f78028d7c 100644 --- a/print/templates/operator_detailedview.html +++ b/print/templates/operator_detailedview.html @@ -10,7 +10,7 @@

{{ _('Color') }}

{{ _('Thread Consumption') }}

{{ _('Stops and Trims') }}

-

{{ _('Estimated Time') }}

+

{{ _('Notes') }}

{% if outer_loop.index == 1 %}
@@ -34,7 +34,7 @@ {{ _('Total nr trims') }}: {{ job.num_trims }}

- {{ job.estimated_time }} +

{% endif %} @@ -45,7 +45,7 @@ - #{{ loop.index + outer_loop.index0 * 13 }} + #{{ loop.index + outer_loop.index0 * 12 }}

@@ -65,7 +65,7 @@ {{ _('# trims') }}: {{ color_block.num_trims }}

- {{ color_block.estimatedtime }} +

{% endfor %} From dc547108e1d85c2dd241d329e0858dced90bf92c Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 18 Apr 2018 22:30:26 -0400 Subject: [PATCH 21/23] handle firefox's window.close() permission denied --- embroider_print.py | 2 +- messages.po | 8 +++++++- print/resources/inkstitch.js | 13 ++++++++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/embroider_print.py b/embroider_print.py index 0dbff9296..cbdaeb09d 100644 --- a/embroider_print.py +++ b/embroider_print.py @@ -133,7 +133,7 @@ class PrintPreviewServer(Thread): def shutdown(): self.shutting_down = True request.environ.get('werkzeug.server.shutdown')() - return 'Server shutting down...' + return _('Closing...') + '

' + _('It is safe to close this window now.') @self.app.route('/resources/', methods=['GET']) def resources(resource): diff --git a/messages.po b/messages.po index ce85878dd..27a25d3ea 100644 --- a/messages.po +++ b/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2018-04-18 16:29-0400\n" +"POT-Creation-Date: 2018-04-18 22:30-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -98,6 +98,12 @@ msgid "" "\"Overwrite\"" msgstr "" +msgid "Closing..." +msgstr "" + +msgid "It is safe to close this window now." +msgstr "" + msgid "" "A print preview has been opened in your web browser. This window will " "stay open in order to communicate with the JavaScript code running in " diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js index 25a5354e3..498b12112 100644 --- a/print/resources/inkstitch.js +++ b/print/resources/inkstitch.js @@ -213,6 +213,18 @@ $(function() { $.post('/shutdown', {}) .done(function(data) { window.close(); + + /* Chrome and Firefox both have a rule: scripts can only close windows + * that they opened. Chrome seems to have an exception for windows that + * were opened by an outside program, so the above works fine. Firefox + * steadfastly refuses to allow us to close the window, so we'll tell + * the user (in their language) that they can close it. + */ + setTimeout(function() { + document.open(); + document.write("" + data + ""); + document.close(); + }, 1000); }); }); @@ -240,7 +252,6 @@ $(function() { $('select#printing-size').change(function(){ var size = $(this).find(':selected').val(); $('.page').toggleClass('a4', size == 'a4'); - console.log("" + Date.now() + "paper size changed"); $.postJSON('/settings/paper-size', {value: size}); }); From 250ae0d7179699b2baa8561e959cbeb7e4007d63 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 24 Apr 2018 15:41:39 -0400 Subject: [PATCH 22/23] fix namespace issue --- inkstitch/extensions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/inkstitch/extensions.py b/inkstitch/extensions.py index 70341cd3d..8d8a5fbb6 100644 --- a/inkstitch/extensions.py +++ b/inkstitch/extensions.py @@ -1,6 +1,7 @@ import inkex import re import json +from copy import deepcopy 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 @@ -213,3 +214,4 @@ class InkstitchExtension(inkex.Effect): inkex.etree.cleanup_namespaces(self.document, top_nsmap=inkex.NSS, keep_ns_prefixes=inkex.NSS.keys()) + self.original_document = deepcopy(self.document) From 8e3dd74a286d4549fdb1dd9cc7a9fcfadb85bb9e Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 28 Apr 2018 13:36:03 -0400 Subject: [PATCH 23/23] add hint about clicking logo --- messages.po | 5 +- print/resources/style.css | 99 ++++++++++++++++++++--------------- print/templates/headline.html | 3 +- 3 files changed, 63 insertions(+), 44 deletions(-) diff --git a/messages.po b/messages.po index 27a25d3ea..bce29caeb 100644 --- a/messages.po +++ b/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2018-04-18 22:30-0400\n" +"POT-Creation-Date: 2018-04-28 13:36-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -317,6 +317,9 @@ msgstr "" msgid "Page" msgstr "" +msgid "Click to choose another logo" +msgstr "" + msgid "Enter job title..." msgstr "" diff --git a/print/resources/style.css b/print/resources/style.css index 013f567c0..97dee6a8e 100644 --- a/print/resources/style.css +++ b/print/resources/style.css @@ -69,7 +69,7 @@ page-break-after: always; margin: 0 !important; } - + figure.inksimulation div { display: none; } @@ -81,7 +81,7 @@ #settings-ui { display: none !important; } - + #errors { display: none !important; } @@ -90,6 +90,10 @@ content: attr(data-label); padding-right: 0.5em; } + + span.logo-instructions { + display: none; + } } @page { @@ -113,7 +117,7 @@ body { .page { width: 210mm; height: 275mm; - padding: 5mm; + padding: 5mm; background: #fff; margin: 0 auto; vertical-align: text-bottom; @@ -167,13 +171,13 @@ body { .ui button.close { border: 1px solid rgb(197,5,5); - + } .ui button.close:hover { background: rgb(197,5,5); color: white; - + } .ui button.settings { @@ -211,7 +215,7 @@ body { border-bottom: 1px solid rgba(129, 129, 129, 0.5); box-shadow: 0 1px 1px 1px rgba(194, 191, 191, 0.5); } - + #settings-ui div { text-align: left; font-size: 12pt; @@ -279,13 +283,24 @@ body { opacity: 0%; } + .logo-instructions { + white-space: nowrap; + + /* chrome ignores this :( + text-align: center; + */ + + font-size: 10px; + color: rgb(192, 192, 192); + } + .operator-detailedview figure.brandlogo { height: 20mm; width: 30mm; margin: 0 2.5mm; text-align: left; } - + .operator-detailedview figure.brandlogo img { max-width: 30mm; max-height: 20mm; @@ -341,15 +356,15 @@ body { div.job-details p span:first-child { font-weight: bold; - padding-right: 1mm; + padding-right: 1mm; } div.job-details p span:last-child { - text-align: left; + text-align: left; } div.job-details > div:last-child span { - text-align: right !important; + text-align: right !important; } div.client-detailedview .job-details { @@ -374,16 +389,16 @@ body { font-size: 12pt; font-weight: bold; } - + /* client dedailed view header */ .client-detailedview div.job-details p span:first-child { - width: 20mm; + width: 20mm; } - + .client-detailedview div.job-details p span:last-child { - max-width: 60mm; + max-width: 60mm; } - + /* SVG Preview Image */ @@ -397,7 +412,7 @@ body { position: relative; overflow: hidden; } - + .client-overview-main figure.inksimulation { height: 155mm; } @@ -419,7 +434,7 @@ body { background: rgba(255, 255, 255, 0.73); padding: 5px; } - + figure.inksimulation div { position: absolute; bottom: 0; @@ -429,7 +444,7 @@ body { margin-right: auto; width: fit-content; } - + figure.inksimulation button { border: none; background: grey; @@ -460,7 +475,7 @@ body { font-weight: 700; font-size: 12pt; color: black; - background: white; + background: white; border: 0.5mm solid white; margin: 0px; padding: 0px; @@ -491,44 +506,44 @@ body { line-height: 30mm; text-align: center; } - + /* detailedview color swatch */ - + .color-palette.detailed > div { height: 100%; position: relative; } - + .color-palette.detailed .color-info { position: absolute; top: 2mm; left: 45mm; } - + .color-palette.detailed .color-info > div { display: table; } - + .color-palette.detailed .color-info p { display: table-row; } - + .color-palette.detailed .color-info span { display: table-cell; padding-right: 5mm; } - + /* Operator Detailed View */ .operator-detailedview header { height: 25mm; } - + .operator-detailedveiw figure.brandlogo{ height: 15mm; width: 15mm; } - + .operator-detailedveiw figure.brandlogo img { max-width: 12.5mm; max-height: 12.5mm; @@ -542,21 +557,21 @@ body { display: table; width: 100%; } - + .operator-job-info div { display: table-row; } - + div.job-headline { display: table-header-group; font-size: 9pt; font-weight: bold; } - + div.job-headline p { height: 1em; } - + .operator-job-info p { height: 15mm; max-width: 15mm; @@ -566,11 +581,11 @@ body { overflow: hidden; border: 1px solid rgb(239,239,239); } - + .operator-job-info span { display: block; } - + .operator-job-info span.color-index { position: absolute; top: 0; @@ -578,17 +593,17 @@ body { line-height: 15mm; width: 10mm; } - + .operator-svg.operator-colorswatch { width: 15mm; } - + .operator-svg.operator-preview { min-width: 15mm; max-width: 20mm; height: 15mm; } - + .operator-svg svg { position: absolute; top: 0; @@ -597,7 +612,7 @@ body { max-width: 30mm; height: 100%; } - + /* Footer */ @@ -610,7 +625,7 @@ body { white-space: wrap; text-align: center; padding-top: 2mm; - + } footer p.num_pages { @@ -670,7 +685,7 @@ body { /* five items */ .color-swatch:first-child:nth-last-child(n+5), .color-swatch:first-child:nth-last-child(n+5) ~ .color-swatch { - font-size: 9pt; + font-size: 9pt; width: calc(100% / 5); } @@ -710,7 +725,7 @@ body { /* fourteen items */ .color-swatch:first-child:nth-last-child(n+14), .color-swatch:first-child:nth-last-child(n+14) ~ .color-swatch { - width: calc(100% / 5); + width: calc(100% / 5); } /* sixteen items */ @@ -762,7 +777,7 @@ body { .color-swatch:first-child:nth-last-child(n+40) ~ .color-swatch { width: calc(100% / 12); } - + /* fourty-nine items */ .color-swatch:first-child:nth-last-child(n+49), .color-swatch:first-child:nth-last-child(n+40) ~ .color-swatch { diff --git a/print/templates/headline.html b/print/templates/headline.html index 421202e4e..7a7059b98 100644 --- a/print/templates/headline.html +++ b/print/templates/headline.html @@ -3,6 +3,7 @@ {{ logo.title }} + {{ _("Click to choose another logo") }}
@@ -10,6 +11,6 @@

- +
{{ date|datetimeformat(_('%Y.%m.%d')) }}