Merge pull request #148 from lexelby/lexelby-print-features

additional print features
pull/152/head
Lex Neva 2018-04-28 13:40:52 -04:00 zatwierdzone przez GitHub
commit 14828d6fa2
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
12 zmienionych plików z 582 dodań i 112 usunięć

Wyświetl plik

@ -9,7 +9,10 @@ import socket
import errno
import time
import logging
from copy import deepcopy
import wx
import appdirs
import json
import inkex
import inkstitch
@ -23,7 +26,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
@ -32,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.
@ -71,6 +97,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
@ -106,7 +133,7 @@ class PrintPreviewServer(Thread):
def shutdown():
self.shutting_down = True
request.environ.get('werkzeug.server.shutdown')()
return 'Server shutting down...'
return _('Closing...') + '<br/><br/>' + _('It is safe to close this window now.')
@self.app.route('/resources/<path:resource>', methods=['GET'])
def resources(resource):
@ -129,6 +156,27 @@ class PrintPreviewServer(Thread):
# nothing to do here -- request_started() will restart the watcher
return "OK"
@self.app.route('/settings/<field_name>', methods=['POST'])
def set_field(field_name):
self.metadata[field_name] = request.json['value']
return "OK"
@self.app.route('/settings/<field_mame>', methods=['GET'])
def get_field(field_name):
return jsonify(self.metadata[field_name])
@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
# the context of a flask request, so we'll just make one
@ -295,11 +343,14 @@ class Print(InkstitchExtension):
'estimated_thread': '', # TODO
},
svg_overview = overview_svg,
svg_scale = '100%',
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 +361,20 @@ class Print(InkstitchExtension):
info_frame.Show()
app.MainLoop()
# don't let inkex print the document out
sys.exit(0)
if __name__ == '__main__':
exception = None
save_stderr()
effect = Print()
effect.affect()
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)

Wyświetl plik

@ -1,6 +1,95 @@
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
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.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
# 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._find_item(name).text = json.dumps(value)
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
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]
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):
i = 0
for i, item in enumerate(self):
pass
return i + 1
class InkstitchExtension(inkex.Effect):
@ -101,3 +190,28 @@ class InkstitchExtension(inkex.Effect):
patches.extend(element.embroider(last_patch))
return patches
def get_inkstitch_metadata(self):
return InkStitchMetadata(self.document)
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['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(self.document,
top_nsmap=inkex.NSS,
keep_ns_prefixes=inkex.NSS.keys())
self.original_document = deepcopy(self.document)

Wyświetl plik

@ -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-28 13:36-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\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 "
@ -311,6 +317,9 @@ msgstr ""
msgid "Page"
msgstr ""
msgid "Click to choose another logo"
msgstr ""
msgid "Enter job title..."
msgstr ""
@ -336,7 +345,7 @@ msgstr ""
msgid "Stops and Trims"
msgstr ""
msgid "Estimated Time"
msgid "Notes"
msgstr ""
msgid "Unique Colors"
@ -360,12 +369,24 @@ msgstr ""
msgid "Total nr trims"
msgstr ""
msgid "Enter operator notes..."
msgstr ""
msgid "Job estimated time"
msgstr ""
msgid "Ctrl + Scroll to Zoom"
msgstr ""
msgid "Scale"
msgstr ""
msgid "Fit"
msgstr ""
msgid "Apply to all"
msgstr ""
msgid "COLOR"
msgstr ""
@ -393,3 +414,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 ""

Wyświetl plik

@ -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) })
@ -13,46 +22,178 @@ 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()
);
}
transform += " scale(" + scale + ")";
var label = parseInt(scale*100);
// center the SVG
transform = "translate(-50%, -50%)";
element.find('svg').css({ transform: transform });
element.find('.scale').text(label);
}
if(scale <= 1) {
transform += " scale(" + scale + ")";
label = parseInt(scale*100) + '%';
} else {
label = "100%";
}
// set preview svg scale to fit into its box if transform is not set
function scaleAllSvg() {
$('.page').each(function() {
if( $(this).find('.inksimulation svg').css('transform') == 'none') {
scaleSVG($(this).find('.inksimulation'), 'fit');
}
});
}
$(this).find('svg').css({ transform: transform });
$(this).find('figcaption span').text(label);
});
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();
scaleInksimulation();
/* Contendeditable Fields */
/* SCALING AND MOVING SVG */
// 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);
});
/* Mousewheel scaling */
$('figure.inksimulation').on( 'DOMMouseScroll mousewheel', function (e) {
if(e.ctrlKey == true) {
var svg = $(this).find('svg');
var transform = svg.css('transform').match(/-?[\d\.]+/g);
var scale = parseFloat(transform[0]);
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 *= 1.03;
}
// set modified scale
transform[0] = scale;
transform[3] = scale;
setSVGTransform($(this), 'matrix(' + transform + ')');
//prevent page fom scrolling
return false;
}
});
/* Fit SVG */
$('button.svg-fit').click(function() {
var svgfigure = $(this).closest('figure');
scaleSVG(svgfigure, 'fit');
});
/* Full Size SVG */
$('button.svg-full').click(function() {
var svgfigure = $(this).closest('figure');
scaleSVG(svgfigure, '1');
});
/* Drag SVG */
$('figure.inksimulation').on('mousedown', function(e) {
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);
// 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');
$('.inksimulation').each(function() {
setSVGTransform($(this), transform);
})
});
/* Contendeditable Fields */
$('[contenteditable="true"]').on('focusout', function() {
/* change svg scale */
var content = $(this).html();
var field_name = $(this).attr('data-field-name');
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 + ')' });
} else {
/* When we focus out from a contenteditable field, we want to
* set the same content to all fields with the same classname */
$('[data-field-name="' + field_name + '"]').html(content);
$.postJSON('/settings/' + field_name, {value: content});
}
});
// 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 if (item.is('figure.inksimulation')) {
setSVGTransform(item, value);
} 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) {
if (e.which == 13) {
// pressing enter defocuses the element
@ -64,14 +205,26 @@ $(function() {
return true;
}
});
/* Settings Bar */
$('button.close').click(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("<html><body>" + data + "</body></html>");
document.close();
}, 1000);
});
});
@ -92,20 +245,54 @@ $(function() {
$('#close-settings').click(function(){
$('#settings-ui').hide();
});
/* Settings */
// 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
$(':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('/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});
});
});

Wyświetl plik

@ -70,6 +70,10 @@
margin: 0 !important;
}
figure.inksimulation div {
display: none;
}
.ui {
display: none;
}
@ -77,7 +81,7 @@
#settings-ui {
display: none !important;
}
#errors {
display: none !important;
}
@ -86,6 +90,10 @@
content: attr(data-label);
padding-right: 0.5em;
}
span.logo-instructions {
display: none;
}
}
@page {
@ -109,7 +117,7 @@ body {
.page {
width: 210mm;
height: 275mm;
padding: 5mm;
padding: 5mm;
background: #fff;
margin: 0 auto;
vertical-align: text-bottom;
@ -163,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 {
@ -207,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;
@ -226,6 +234,10 @@ body {
cursor: pointer;
}
#settings-ui fieldset {
margin-bottom: 1em;
}
/* Header */
@ -247,18 +259,48 @@ 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%;
}
.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;
@ -314,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 {
@ -347,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 */
@ -370,7 +412,7 @@ body {
position: relative;
overflow: hidden;
}
.client-overview-main figure.inksimulation {
height: 155mm;
}
@ -389,6 +431,24 @@ body {
font-weight: bold;
line-height: 12pt;
margin: 2.5mm;
background: rgba(255, 255, 255, 0.73);
padding: 5px;
}
figure.inksimulation div {
position: absolute;
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 */
@ -415,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;
@ -446,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;
@ -497,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;
@ -521,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;
@ -533,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;
@ -552,7 +612,7 @@ body {
max-width: 30mm;
height: 100%;
}
/* Footer */
@ -565,7 +625,7 @@ body {
white-space: wrap;
text-align: center;
padding-top: 2mm;
}
footer p.num_pages {
@ -625,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);
}
@ -665,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 */
@ -717,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 {

Wyświetl plik

@ -1,12 +1,16 @@
<figure class="brandlogo">
<img src="{{ logo.src or "resources/inkstitch-logo.svg" }}" alt="{{ logo.title }}" title="{{ logo.title }}">
<label for="logo-picker">
<img src="{{ logo.src or "resources/inkstitch-logo.svg" }}" alt="{{ logo.title }}" title="{{ logo.title }}" data-field-name="logo">
<input type=file id="logo-picker" />
</label>
<span class="logo-instructions">{{ _("Click to choose another logo") }}</span>
</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>
</div>

Wyświetl plik

@ -23,7 +23,7 @@
<div class="page operator-overview" style="display: {{ 'block' if view.operator_overview else 'none' }}">{% include 'operator_overview.html' %}</div>
{# 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 %}
<div class="page operator-detailedview" style="display: {{ 'block' if view.operator_detailedview else 'none' }}">{% include 'operator_detailedview.html' %}</div>
{% endfor %}

Wyświetl plik

@ -10,7 +10,7 @@
<p>{{ _('Color') }}</p>
<p>{{ _('Thread Consumption') }}</p>
<p>{{ _('Stops and Trims') }}</p>
<p>{{ _('Estimated Time') }}</p>
<p>{{ _('Notes') }}</p>
</div>
{% if outer_loop.index == 1 %}
<div>
@ -25,7 +25,7 @@
<span>{{ _('Color Blocks') }}: {{ job.num_color_blocks }}</span>
</p>
<p>
<span>{{ _('Design box size') }}: {{ job.dimensions }}</span>
<span>{{ _('Design box size') }}: {{ "%0.1fmm X %0.1fmm" | format(*job.dimensions) }}</span>
<span>{{ _('Total thread used') }}: {{job.estimated_thread }}</span>
<span>{{ _('Total stitch count') }}: {{job.num_stitches }}</span>
</p>
@ -34,7 +34,7 @@
<span>{{ _('Total nr trims') }}: {{ job.num_trims }}</span>
</p>
<p>
<span>{{ job.estimated_time }}</span>
<span></span>
</p>
</div>
{% endif %}
@ -45,7 +45,7 @@
<svg xmlns="http://www.w3.org/2000/svg">
<rect fill="rgb{{ color_block.color.rgb }}" width="15mm" height="100%" />
<text fill="rgb{{ color_block.color.font_color }}">
<tspan x="4mm" y="7.5mm" class="color-index">#{{ loop.index + outer_loop.index0 * 13 }}</tspan>
<tspan x="4mm" y="7.5mm" class="color-index">#{{ loop.index + outer_loop.index0 * 12 }}</tspan>
</text>
</svg>
</p>
@ -65,7 +65,7 @@
<span>{{ _('# trims') }}: {{ color_block.num_trims }}</span>
</p>
<p>
<span>{{ color_block.estimatedtime }}</span>
<span class="notes" contenteditable="true" data-field-name="operator-notes-block{{ loop.index0 + outer_loop.index0 * 12 }}" data-placeholder="{{ _("Enter operator notes...") }}"></span>
</p>
</div>
{% endfor %}

Wyświetl plik

@ -11,7 +11,7 @@
</div>
<div>
<div class="table">
<p><span>{{ _('Design box size') }}:</span><span>{{ job.dimensions }}</span></p>
<p><span>{{ _('Design box size') }}:</span><span>{{ "%0.1fmm X %0.1fmm" | format(*job.dimensions) }}</span></p>
<p><span>{{ _('Total stitch count') }}:</span><span>{{job.num_stitches }}</span></p>
<p><span>{{ _('Total thread used') }}:</span><span>{{job.estimated_thread }}</span></p>
</div>
@ -25,9 +25,14 @@
</div>
</header>
<main>
<figure class="inksimulation operator" style="height: 210mm;">
<figure class="inksimulation operator" data-field-name="operator-overview-transform" style="height: 210mm;" title="{{ _('Ctrl + Scroll to Zoom') }}">
{{ svg_overview|safe }}
<figcaption>{{ _('Scale') }} <span>{{ svg_scale }}</span></figcaption>
<figcaption>{{ _('Scale') }} <span class="scale" data-field-name="svg-scale" contenteditable="true" data-placeholder=""></span>%</figcaption>
<div>
<button class="svg-fit">{{ _('Fit') }}</button>
<button class="svg-full">100%</button>
<button class="svg-apply">{{ _('Apply to all') }}</button>
</div>
</figure>
</main>
{% include 'footer.html' %}

Wyświetl plik

@ -15,9 +15,14 @@
</div>
</header>
<main>
<figure class="inksimulation">
<figure class="inksimulation" data-field-name="client-detail-transform-block{{ loop.index0 }}" title="{{ _('Ctrl + Scroll to Zoom') }}">
{{color_block.svg_preview|safe}}
<figcaption>{{ _('Scale') }} <span>{{ svg_scale }}</span></figcaption>
<figcaption>{{ _('Scale') }} <span class="scale" data-field-name="svg-scale" contenteditable="true" data-placeholder=""></span>%</figcaption>
<div>
<button class="svg-fit">Fit</button>
<button class="svg-full">100%</button>
<button class="svg-apply">Apply to all</button>
</div>
</figure>
<div class="color-palette detailed">

Wyświetl plik

@ -11,7 +11,7 @@
</div>
<div>
<div class="table">
<p><span>{{ _('Design box size') }}:</span><span>{{ job.dimensions }}</span></p>
<p><span>{{ _('Design box size') }}:</span><span>{{ "%0.1fmm X %0.1fmm" | format(*job.dimensions) }}</span></p>
<p><span>{{ _('Total stitch count') }}:</span><span>{{job.num_stitches }}</span></p>
<p><span>{{ _('Total thread used') }}:</span><span>{{job.estimated_thread }}</span></p>
</div>
@ -25,9 +25,14 @@
</div>
</header>
<main class="client-overview-main">
<figure class="inksimulation">
<figure class="inksimulation" data-field-name="client-overview-transform" title="{{ _('Ctrl + Scroll to Zoom') }}">
{{ svg_overview|safe }}
<figcaption>{{ _('Scale') }} <span>{{ svg_scale }}</span></figcaption>
<figcaption>{{ _('Scale') }} <span class="scale" data-field-name="svg-scale" contenteditable="true" data-placeholder=""></span>%</figcaption>
<div>
<button class="svg-fit">Fit</button>
<button class="svg-full">100%</button>
<button class="svg-apply">Apply to all</button>
</div>
</figure>
<div class="color-palette">

Wyświetl plik

@ -15,7 +15,7 @@
<h1>{{ _('Settings') }}</h1>
<div>
<p>{{ _('Printing Size') }}:
<select id="printing-size">
<select id="printing-size" data-field-name="paper-size">
<option value="letter" selected="selected">Letter</option>
<option value="a4">A4</option>
</select>
@ -23,11 +23,12 @@
</div>
<div>
<fieldset>
<legend>{{ ('Print Layouts') }}:</legend>
<p><input type="checkbox" id="client-overview" {{ 'checked' if view.client_overview else '' }}><label for="client-overview">Client Overview</label></p>
<p><input type="checkbox" id="client-detailedview" {{ 'checked' if view.client_detailedview else '' }}><label for="client-detailedview">Client Detailed View</label></p>
<p><input type="checkbox" id="operator-overview" {{ 'checked' if view.operator_overview else '' }}><label for="operator-overview">Operator Overview</label></p>
<p><input type="checkbox" id="operator-detailedview" {{ 'checked' if view.operator_detailedview else '' }}><label for="operator-overview">Operator Detailed View</label></p>
<legend>{{ _('Print Layouts') }}:</legend>
<p><input type="checkbox" id="client-overview" data-field-name="client-overview" /><label for="client-overview">Client Overview</label></p>
<p><input type="checkbox" id="client-detailedview" data-field-name="client-detailedview" /><label for="client-detailedview">Client Detailed View</label></p>
<p><input type="checkbox" id="operator-overview" data-field-name="operator-overview" CHECKED /><label for="operator-overview">Operator Overview</label></p>
<p><input type="checkbox" id="operator-detailedview" data-field-name="operator-detailedview" CHECKED /><label for="operator-detailedview">Operator Detailed View</label></p>
</fieldset>
<button id="save-settings" title="{{ _("Includes all settings visible here and also the icon.") }}">{{ _("Save as defaults") }}</button>
</div>
</div>