From 4a227b54bf58248a511b5c63a9b5f990429f6658 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Sat, 19 Dec 2020 17:39:48 +0100 Subject: [PATCH] add LTO barcode generator App --- debian/proxmox-backup-docs.links | 1 + docs/Makefile | 17 +- docs/conf.py | 1 + docs/lto-barcode/code39.js | 351 +++++++++++++++++++++++++++ docs/lto-barcode/index.html | 51 ++++ docs/lto-barcode/label-list.js | 140 +++++++++++ docs/lto-barcode/label-setup.js | 107 ++++++++ docs/lto-barcode/label-style.js | 20 ++ docs/lto-barcode/lto-barcode.js | 214 ++++++++++++++++ docs/lto-barcode/page-calibration.js | 142 +++++++++++ docs/lto-barcode/page-layout.js | 167 +++++++++++++ docs/lto-barcode/paper-size.js | 49 ++++ docs/lto-barcode/prefix-field.js | 15 ++ docs/lto-barcode/tape-type.js | 23 ++ 14 files changed, 1297 insertions(+), 1 deletion(-) create mode 100644 docs/lto-barcode/code39.js create mode 100644 docs/lto-barcode/index.html create mode 100644 docs/lto-barcode/label-list.js create mode 100644 docs/lto-barcode/label-setup.js create mode 100644 docs/lto-barcode/label-style.js create mode 100644 docs/lto-barcode/lto-barcode.js create mode 100644 docs/lto-barcode/page-calibration.js create mode 100644 docs/lto-barcode/page-layout.js create mode 100644 docs/lto-barcode/paper-size.js create mode 100644 docs/lto-barcode/prefix-field.js create mode 100644 docs/lto-barcode/tape-type.js diff --git a/debian/proxmox-backup-docs.links b/debian/proxmox-backup-docs.links index 719dd555..739115fa 100644 --- a/debian/proxmox-backup-docs.links +++ b/debian/proxmox-backup-docs.links @@ -1,2 +1,3 @@ /usr/share/doc/proxmox-backup/proxmox-backup.pdf /usr/share/doc/proxmox-backup/html/proxmox-backup.pdf /usr/share/javascript/extjs /usr/share/doc/proxmox-backup/html/prune-simulator/extjs +/usr/share/javascript/extjs /usr/share/doc/proxmox-backup/html/lto-barcode/extjs diff --git a/docs/Makefile b/docs/Makefile index 4c1d2b26..a6e8c988 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -20,6 +20,19 @@ PRUNE_SIMULATOR_FILES := \ prune-simulator/clear-trigger.png \ prune-simulator/prune-simulator.js +LTO_BARCODE_FILES := \ + lto-barcode/index.html \ + lto-barcode/code39.js \ + lto-barcode/prefix-field.js \ + lto-barcode/label-style.js \ + lto-barcode/tape-type.js \ + lto-barcode/paper-size.js \ + lto-barcode/page-layout.js \ + lto-barcode/page-calibration.js \ + lto-barcode/label-list.js \ + lto-barcode/label-setup.js \ + lto-barcode/lto-barcode.js + # Sphinx documentation setup SPHINXOPTS = SPHINXBUILD = sphinx-build @@ -79,11 +92,13 @@ onlinehelpinfo: @echo "Build finished. OnlineHelpInfo.js is in $(BUILDDIR)/scanrefs." .PHONY: html -html: ${GENERATED_SYNOPSIS} images/proxmox-logo.svg custom.css conf.py ${PRUNE_SIMULATOR_FILES} +html: ${GENERATED_SYNOPSIS} images/proxmox-logo.svg custom.css conf.py ${PRUNE_SIMULATOR_FILES} ${LTO_BARCODE_FILES} $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html install -m 0644 custom.js custom.css images/proxmox-logo.svg $(BUILDDIR)/html/_static/ install -dm 0755 $(BUILDDIR)/html/prune-simulator install -m 0644 ${PRUNE_SIMULATOR_FILES} $(BUILDDIR)/html/prune-simulator + install -dm 0755 $(BUILDDIR)/html/lto-barcode + install -m 0644 ${LTO_BARCODE_FILES} $(BUILDDIR)/html/lto-barcode @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." diff --git a/docs/conf.py b/docs/conf.py index 0e741124..45497d85 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -172,6 +172,7 @@ html_theme_options = { 'Proxmox Homepage': 'https://proxmox.com', 'PDF': 'proxmox-backup.pdf', 'Prune Simulator' : 'prune-simulator/index.html', + 'LTO Barcode Generator' : 'lto-barcode/index.html', }, 'sidebar_width': '320px', diff --git a/docs/lto-barcode/code39.js b/docs/lto-barcode/code39.js new file mode 100644 index 00000000..d17c2ece --- /dev/null +++ b/docs/lto-barcode/code39.js @@ -0,0 +1,351 @@ +// Code39 barcode generator +// see https://en.wikipedia.org/wiki/Code_39 + +// IBM LTO Ultrium Cartridge Label Specification +// http://www-01.ibm.com/support/docview.wss?uid=ssg1S7000429 + +let code39_codes = { + "1": ['B', 's', 'b', 'S', 'b', 's', 'b', 's', 'B'], + "A": ['B', 's', 'b', 's', 'b', 'S', 'b', 's', 'B'], + "K": ['B', 's', 'b', 's', 'b', 's', 'b', 'S', 'B'], + "U": ['B', 'S', 'b', 's', 'b', 's', 'b', 's', 'B'], + + "2": ['b', 's', 'B', 'S', 'b', 's', 'b', 's', 'B'], + "B": ['b', 's', 'B', 's', 'b', 'S', 'b', 's', 'B'], + "L": ['b', 's', 'B', 's', 'b', 's', 'b', 'S', 'B'], + "V": ['b', 'S', 'B', 's', 'b', 's', 'b', 's', 'B'], + + "3": ['B', 's', 'B', 'S', 'b', 's', 'b', 's', 'b'], + "C": ['B', 's', 'B', 's', 'b', 'S', 'b', 's', 'b'], + "M": ['B', 's', 'B', 's', 'b', 's', 'b', 'S', 'b'], + "W": ['B', 'S', 'B', 's', 'b', 's', 'b', 's', 'b'], + + "4": ['b', 's', 'b', 'S', 'B', 's', 'b', 's', 'B'], + "D": ['b', 's', 'b', 's', 'B', 'S', 'b', 's', 'B'], + "N": ['b', 's', 'b', 's', 'B', 's', 'b', 'S', 'B'], + "X": ['b', 'S', 'b', 's', 'B', 's', 'b', 's', 'B'], + + "5": ['B', 's', 'b', 'S', 'B', 's', 'b', 's', 'b'], + "E": ['B', 's', 'b', 's', 'B', 'S', 'b', 's', 'b'], + "O": ['B', 's', 'b', 's', 'B', 's', 'b', 'S', 'b'], + "Y": ['B', 'S', 'b', 's', 'B', 's', 'b', 's', 'b'], + + "6": ['b', 's', 'B', 'S', 'B', 's', 'b', 's', 'b'], + "F": ['b', 's', 'B', 's', 'B', 'S', 'b', 's', 'b'], + "P": ['b', 's', 'B', 's', 'B', 's', 'b', 'S', 'b'], + "Z": ['b', 'S', 'B', 's', 'B', 's', 'b', 's', 'b'], + + "7": ['b', 's', 'b', 'S', 'b', 's', 'B', 's', 'B'], + "G": ['b', 's', 'b', 's', 'b', 'S', 'B', 's', 'B'], + "Q": ['b', 's', 'b', 's', 'b', 's', 'B', 'S', 'B'], + "-": ['b', 'S', 'b', 's', 'b', 's', 'B', 's', 'B'], + + "8": ['B', 's', 'b', 'S', 'b', 's', 'B', 's', 'b'], + "H": ['B', 's', 'b', 's', 'b', 'S', 'B', 's', 'b'], + "R": ['B', 's', 'b', 's', 'b', 's', 'B', 'S', 'b'], + ".": ['B', 'S', 'b', 's', 'b', 's', 'B', 's', 'b'], + + "9": ['b', 's', 'B', 'S', 'b', 's', 'B', 's', 'b'], + "I": ['b', 's', 'B', 's', 'b', 'S', 'B', 's', 'b'], + "S": ['b', 's', 'B', 's', 'b', 's', 'B', 'S', 'b'], + " ": ['b', 'S', 'B', 's', 'b', 's', 'B', 's', 'b'], + + "0": ['b', 's', 'b', 'S', 'B', 's', 'B', 's', 'b'], + "J": ['b', 's', 'b', 's', 'B', 'S', 'B', 's', 'b'], + "T": ['b', 's', 'b', 's', 'B', 's', 'B', 'S', 'b'], + "*": ['b', 'S', 'b', 's', 'B', 's', 'B', 's', 'b'] +}; + +let colors = [ + '#BB282E', + '#FAE54A', + '#9AC653', + '#01A5E2', + '#9EAAB6', + '#D97E35', + '#E27B99', + '#67A945', + '#F6B855', + '#705A81' +]; + +let lto_label_width = 70; +let lto_label_height = 17; + +function foreach_label(page_layout, callback) { + + let count = 0; + let row = 0; + let height = page_layout.margin_top; + + while ((height + page_layout.label_height) <= page_layout.page_height) { + + let column = 0; + let width = page_layout.margin_left; + + while ((width + page_layout.label_width) <= page_layout.page_width) { + + callback(column, row, count, width, height); + count += 1; + + column += 1; + width += page_layout.label_width; + width += page_layout.column_spacing; + } + + row += 1; + height += page_layout.label_height; + height += page_layout.row_spacing; + } + +} + +function compute_max_labels(page_layout) { + + let max_labels = 0; + foreach_label(page_layout, function() { max_labels += 1; }); + return max_labels; +} + +function svg_label(mode, label, label_type, pagex, pagey, label_borders) { + let svg = ""; + + if (label.length != 6) { + throw "wrong label length"; + } + if (label_type.length != 2) { + throw "wrong label_type length"; + } + + let ratio = 2.75; + let parts = 3*ratio + 6; // 3*wide + 6*small; + let barcode_width = (lto_label_width/12)*10; // 10*code + 2margin + let small = barcode_width/(parts*10 + 9); + let code_width = small*parts; + let wide = small*ratio; + let xpos = pagex + code_width; + let height = 12; + + if (mode === 'placeholder') { + if (label_borders) { + svg += ``; + } + return svg; + } + if (label_borders) { + svg += ``; + } + + if (mode === "color" || mode == "frame") { + let w = lto_label_width/8; + let h = lto_label_height - height; + for (var i = 0; i < 7; i++) { + let textx = w/2 + pagex + i*w; + let texty = pagey; + + let fill = "none"; + if (mode === "color" && (i < 6)) { + let letter = label.charAt(i); + if (letter >= '0' && letter <= '9') { + fill = colors[parseInt(letter, 10)]; + } + } + + svg += ``; + + if (i == 6) { + textx += 3; + texty += 3.7; + svg += `${label_type}`; + } else { + let letter = label.charAt(i); + textx += 3.5; + texty += 4; + svg += `${letter}`; + } + } + } + + let raw_label = `*${label}${label_type}*`; + + for (var i = 0; i < raw_label.length; i++) { + let letter = raw_label.charAt(i); + + let code = code39_codes[letter]; + if (code === undefined) { + throw `unable to encode letter '${letter}' with code39`; + } + + if (mode === "simple") { + let textx = xpos + code_width/2; + let texty = pagey + 4; + + if (i > 0 && (i+1) < raw_label.length) { + svg += `${letter}`; + } + } + + for (let c of code) { + + if (c === 's') { + xpos += small; + continue; + } + if (c === 'S') { + xpos += wide; + continue; + } + + let w = c === 'B' ? wide : small; + let ypos = pagey + lto_label_height - height; + + svg += ``; + xpos = xpos + w; + } + xpos += small; + } + + return svg; +} + +function html_page_header() { + let html = ""; + + html += ""; + + //html += ""; + html += ""; + + return html; +} + +function svg_page_header(page_width, page_height) { + let svg = "`; + + return svg; +} + +function printBarcodePage() { + let frame = document.getElementById("print_frame"); + + let window = frame.contentWindow; + window.print(); +} + +function generate_barcode_page(target_id, page_layout, label_list, calibration) { + + let svg = svg_page_header(page_layout.page_width, page_layout.page_height); + + let c = calibration; + + console.log(calibration); + + svg += "= label_list.length) { return; } + + let item = label_list[count]; + + svg += svg_label(item.mode, item.label, item.tape_type, xpos, ypos, page_layout.label_borders); + }); + + svg += ""; + svg += ""; + + let html = html_page_header(); + html += svg; + html += ""; + html += ""; + + let frame = document.getElementById(target_id); + + setupPrintFrame(frame, page_layout.page_width, page_layout.page_height); + + let fwindow = frame.contentWindow; + + fwindow.document.open(); + fwindow.document.write(html); + fwindow.document.close(); +} + +function setupPrintFrame(frame, page_width, page_height) { + let dpi = 98; + + let dpr = window.devicePixelRatio; + if (dpr !== undefined) { + dpi = dpi*dpr; + } + + let ppmm = dpi/25.4; + + frame.width = page_width*ppmm; + frame.height = page_height*ppmm; +} + +function generate_calibration_page(target_id, page_layout, calibration) { + + let frame = document.getElementById(target_id); + + setupPrintFrame(frame, page_layout.page_width, page_layout.page_height); + + let svg = svg_page_header( page_layout.page_width, page_layout.page_height); + + svg += ""; + svg += "Sx = 50mm`; + svg += ""; + + svg += `Dx = 100mm`; + svg += ""; + + svg += `Sy = 50mm`; + svg += ""; + + svg += `Dy = 100mm`; + svg += ""; + + let c = calibration; + if (c !== undefined) { + svg += ``; + } + + svg += ""; + + let html = html_page_header(); + html += svg; + html += ""; + html += ""; + + let fwindow = frame.contentWindow; + + fwindow.document.open(); + fwindow.document.write(html); + fwindow.document.close(); +} diff --git a/docs/lto-barcode/index.html b/docs/lto-barcode/index.html new file mode 100644 index 00000000..c47798c9 --- /dev/null +++ b/docs/lto-barcode/index.html @@ -0,0 +1,51 @@ + + + + + + Proxmox LTO Barcode Label Generator + + + + + + + + + + + + + + + + + + + diff --git a/docs/lto-barcode/label-list.js b/docs/lto-barcode/label-list.js new file mode 100644 index 00000000..6cf8acfb --- /dev/null +++ b/docs/lto-barcode/label-list.js @@ -0,0 +1,140 @@ +Ext.define('LabelList', { + extend: 'Ext.grid.Panel', + alias: 'widget.labelList', + + plugins: { + ptype: 'cellediting', + clicksToEdit: 1 + }, + + selModel: 'cellmodel', + + store: { + field: [ + 'prefix', + 'tape_type', + { + type: 'integer', + name: 'start', + }, + { + type: 'integer', + name: 'end', + }, + ], + data: [], + }, + + listeners: { + validateedit: function(editor, context) { + console.log(context.field); + console.log(context.value); + context.record.set(context.field, context.value); + context.record.commit(); + return true; + }, + }, + + columns: [ + { + text: 'Prefix', + dataIndex: 'prefix', + flex: 1, + editor: { + xtype: 'prefixfield', + allowBlank: false, + }, + renderer: function (value, metaData, record) { + console.log(record); + if (record.data.mode === 'placeholder') { + return "-"; + } + return value; + }, + }, + { + text: 'Type', + dataIndex: 'tape_type', + flex: 1, + editor: { + xtype: 'ltoTapeType', + allowBlank: false, + }, + renderer: function (value, metaData, record) { + console.log(record); + if (record.data.mode === 'placeholder') { + return "-"; + } + return value; + }, + }, + { + text: 'Mode', + dataIndex: 'mode', + flex: 1, + editor: { + xtype: 'ltoLabelStyle', + allowBlank: false, + }, + }, + { + text: 'Start', + dataIndex: 'start', + flex: 1, + editor: { + xtype: 'numberfield', + allowBlank: false, + }, + }, + { + text: 'End', + dataIndex: 'end', + flex: 1, + editor: { + xtype: 'numberfield', + }, + renderer: function(value) { + if (value === null || value === '' || value === undefined) { + return "Fill"; + } + return value; + }, + }, + { + xtype: 'actioncolumn', + width: 75, + items: [ + { + tooltip: 'Move Up', + iconCls: 'fa fa-arrow-up', + handler: function(grid, rowIndex) { + if (rowIndex < 1) { return; } + let store = grid.getStore(); + let record = store.getAt(rowIndex); + store.removeAt(rowIndex); + store.insert(rowIndex - 1, record); + }, + }, + { + tooltip: 'Move Down', + iconCls: 'fa fa-arrow-down', + handler: function(grid, rowIndex) { + let store = grid.getStore(); + if (rowIndex >= store.getCount()) { return; } + let record = store.getAt(rowIndex); + store.removeAt(rowIndex); + store.insert(rowIndex + 1, record); + }, + }, + { + tooltip: 'Delete', + iconCls: 'fa fa-scissors', + //iconCls: 'fa critical fa-trash-o', + handler: function(grid, rowIndex) { + grid.getStore().removeAt(rowIndex); + }, + } + ], + }, + ], +}); diff --git a/docs/lto-barcode/label-setup.js b/docs/lto-barcode/label-setup.js new file mode 100644 index 00000000..e65cc7a4 --- /dev/null +++ b/docs/lto-barcode/label-setup.js @@ -0,0 +1,107 @@ +Ext.define('LabelSetupPanel', { + extend: 'Ext.panel.Panel', + alias: 'widget.labelSetupPanel', + + layout: { + type: 'hbox', + align: 'stretch', + pack: 'start', + }, + + getValues: function() { + let me = this; + + let values = {}; + + Ext.Array.each(me.query('[isFormField]'), function(field) { + let data = field.getSubmitData(); + Ext.Object.each(data, function(name, val) { + let parsed = parseInt(val, 10); + values[name] = isNaN(parsed) ? val : parsed; + }); + }); + + return values; + }, + + controller: { + xclass: 'Ext.app.ViewController', + + init: function() { + let me = this; + let view = me.getView(); + let list = view.down("labelList"); + let store = list.getStore(); + store.on('datachanged', function(store) { + view.fireEvent("listchanged", store); + }); + store.on('update', function(store) { + view.fireEvent("listchanged", store); + }); + }, + + onAdd: function() { + let list = this.lookupReference('label_list'); + let view = this.getView(); + let params = view.getValues(); + list.getStore().add(params); + }, + }, + + items: [ + { + border: false, + layout: { + type: 'vbox', + align: 'stretch', + pack: 'start', + }, + items: [ + { + xtype: 'prefixfield', + name: 'prefix', + value: 'TEST', + fieldLabel: 'Prefix', + }, + { + xtype: 'ltoTapeType', + name: 'tape_type', + fieldLabel: 'Type', + value: 'L8', + }, + { + xtype: 'ltoLabelStyle', + name: 'mode', + fieldLabel: 'Mode', + value: 'color', + }, + { + xtype: 'numberfield', + name: 'start', + fieldLabel: 'Start', + minValue: 0, + allowBlank: false, + value: 0, + }, + { + xtype: 'numberfield', + name: 'end', + fieldLabel: 'End', + minValue: 0, + emptyText: 'Fill', + }, + { + xtype: 'button', + text: 'Add', + handler: 'onAdd', + }, + ], + }, + { + margin: "0 0 0 10", + xtype: 'labelList', + reference: 'label_list', + flex: 1, + }, + ], +}); diff --git a/docs/lto-barcode/label-style.js b/docs/lto-barcode/label-style.js new file mode 100644 index 00000000..3f0cd669 --- /dev/null +++ b/docs/lto-barcode/label-style.js @@ -0,0 +1,20 @@ +Ext.define('LtoLabelStyle', { + extend: 'Ext.form.field.ComboBox', + alias: 'widget.ltoLabelStyle', + + editable: false, + + displayField: 'text', + valueField: 'value', + queryMode: 'local', + + store: { + field: ['value', 'text'], + data: [ + { value: 'simple', text: "Simple" }, + { value: 'color', text: 'Color (frames with color)' }, + { value: 'frame', text: 'Frame (no color)' }, + { value: 'placeholder', text: 'Placeholder (empty)' }, + ], + }, +}); diff --git a/docs/lto-barcode/lto-barcode.js b/docs/lto-barcode/lto-barcode.js new file mode 100644 index 00000000..cdaa2076 --- /dev/null +++ b/docs/lto-barcode/lto-barcode.js @@ -0,0 +1,214 @@ +// FIXME: HACK! Makes scrolling in number spinner work again. fixed in ExtJS >= 6.1 +if (Ext.isFirefox) { + Ext.$eventNameMap.DOMMouseScroll = 'DOMMouseScroll'; +} + +function draw_labels(target_id, label_list, page_layout, calibration) { + + let max_labels = compute_max_labels(page_layout); + + let count_fixed = 0; + let count_fill = 0; + + for (i = 0; i < label_list.length; i++) { + let item = label_list[i]; + if (item.end === null || item.end === '' || item.end === undefined) { + count_fill += 1; + continue; + } + if (item.end <= item.start) { + count_fixed += 1; + continue; + } + count_fixed += (item.end - item.start) + 1; + } + + let rest = max_labels - count_fixed; + let fill_size = 1; + if (rest >= count_fill) { + fill_size = Math.floor(rest/count_fill); + } + + let list = []; + + let count_fill_2 = 0; + + for (i = 0; i < label_list.length; i++) { + let item = label_list[i]; + let count; + if (item.end === null || item.end === '' || item.end === undefined) { + count_fill_2 += 1; + if (count_fill_2 === count_fill) { + count = rest; + } else { + count = fill_size; + } + rest -= count; + } else { + if (item.end <= item.start) { + count = 1; + } else { + count = (item.end - item.start) + 1; + } + } + + for (j = 0; j < count; j++) { + + let id = item.start + j; + + if (item.prefix.length == 6) { + + list.push({ + label: item.prefix, + tape_type: item.tape_type, + mode: item.mode, + id: id, + }); + rest += count - j - 1; + break; + + } else { + + let pad_len = 6-item.prefix.length; + let label = item.prefix + id.toString().padStart(pad_len, 0); + + if (label.length != 6) { + rest += count - j; + break; + } + + list.push({ + label: label, + tape_type: item.tape_type, + mode: item.mode, + id: id, + }); + } + } + } + + generate_barcode_page(target_id, page_layout, list, calibration); +} + +Ext.define('MainView', { + extend: 'Ext.container.Viewport', + alias: 'widget.mainview', + + layout: { + type: 'vbox', + align: 'stretch', + pack: 'start', + }, + width: 800, + + controller: { + xclass: 'Ext.app.ViewController', + + update_barcode_preview: function() { + let me = this; + let view = me.getView(); + let list_view = view.down("labelList"); + + let store = list_view.getStore(); + let label_list = []; + store.each((record) => { + label_list.push(record.data); + }); + + let page_layout_view = view.down("pageLayoutPanel"); + let page_layout = page_layout_view.getValues(); + + let calibration_view = view.down("pageCalibration"); + let page_calibration = calibration_view.getValues(); + + draw_labels("print_frame", label_list, page_layout, page_calibration); + }, + + update_calibration_preview: function() { + let me = this; + let view = me.getView(); + let page_layout_view = view.down("pageLayoutPanel"); + let page_layout = page_layout_view.getValues(); + + let calibration_view = view.down("pageCalibration"); + let page_calibration = calibration_view.getValues(); + console.log(page_calibration); + generate_calibration_page('print_frame', page_layout, page_calibration); + }, + + control: { + labelSetupPanel: { + listchanged: function(store) { + this.update_barcode_preview(); + }, + activate: function() { + this.update_barcode_preview(); + }, + }, + pageLayoutPanel: { + pagechanged: function(layout) { + this.update_barcode_preview(); + }, + activate: function() { + this.update_barcode_preview(); + }, + }, + pageCalibration: { + calibrationchanged: function() { + this.update_calibration_preview(); + }, + activate: function() { + this.update_calibration_preview(); + }, + }, + }, + }, + + items: [ + { + xtype: 'tabpanel', + items: [ + { + xtype: 'labelSetupPanel', + title: 'Proxmox LTO Barcode Label Generator', + bodyPadding: 10, + }, + { + xtype: 'pageLayoutPanel', + title: 'Page Layout', + bodyPadding: 10, + }, + { + xtype: 'pageCalibration', + title: 'Printer Calibration', + bodyPadding: 10, + }, + ], + }, + { + xtype: 'panel', + layout: "center", + title: 'Print Preview', + bodyStyle: "background-color: grey;", + bodyPadding: 10, + html: '
', + border: false, + flex: 1, + scrollable: true, + tools:[{ + type: 'print', + tooltip: 'Open Print Dialog', + handler: function(event, toolEl, panelHeader) { + printBarcodePage(); + } + }], + }, + ], +}); + +Ext.onReady(function() { + + Ext.create('MainView', { + renderTo: Ext.getBody(), + }); +}); diff --git a/docs/lto-barcode/page-calibration.js b/docs/lto-barcode/page-calibration.js new file mode 100644 index 00000000..42bf209b --- /dev/null +++ b/docs/lto-barcode/page-calibration.js @@ -0,0 +1,142 @@ +Ext.define('PageCalibration', { + extend: 'Ext.panel.Panel', + alias: 'widget.pageCalibration', + + layout: { + type: 'hbox', + align: 'stretch', + pack: 'start', + }, + + getValues: function() { + let me = this; + + let values = {}; + + Ext.Array.each(me.query('[isFormField]'), function(field) { + if (field.isValid()) { + let data = field.getSubmitData(); + Ext.Object.each(data, function(name, val) { + let parsed = parseFloat(val, 10); + values[name] = isNaN(parsed) ? val : parsed; + }); + } + }); + + if (values.d_x === undefined) { return; } + if (values.d_y === undefined) { return; } + if (values.s_x === undefined) { return; } + if (values.s_y === undefined) { return; } + + scalex = 100/values.d_x; + scaley = 100/values.d_y; + + let offsetx = ((50*scalex) - values.s_x)/scalex; + let offsety = ((50*scaley) - values.s_y)/scaley; + + return { + scalex: scalex, + scaley: scaley, + offsetx: offsetx, + offsety: offsety, + }; + }, + + controller: { + xclass: 'Ext.app.ViewController', + + control: { + 'field': { + change: function() { + let view = this.getView(); + let param = view.getValues(); + view.fireEvent("calibrationchanged", param); + }, + }, + }, + }, + + items: [ + { + border: false, + layout: { + type: 'vbox', + align: 'stretch', + pack: 'start', + }, + items: [ + { + xtype: 'displayfield', + value: 'a4', + fieldLabel: 'Start Offset Sx (mm)', + labelWidth: 150, + value: 50, + }, + { + xtype: 'displayfield', + value: 'a4', + fieldLabel: 'Length Dx (mm)', + labelWidth: 150, + value: 100, + }, + { + xtype: 'displayfield', + value: 'a4', + fieldLabel: 'Start Offset Sy (mm)', + labelWidth: 150, + value: 50, + }, + { + xtype: 'displayfield', + value: 'a4', + fieldLabel: 'Length Dy (mm)', + labelWidth: 150, + value: 100, + }, + ], + }, + { + border: false, + margin: '0 0 0 20', + layout: { + type: 'vbox', + align: 'stretch', + pack: 'start', + }, + items: [ + { + xtype: 'numberfield', + value: 'a4', + name: 's_x', + fieldLabel: 'Meassured Start Offset Sx (mm)', + allowBlank: false, + labelWidth: 200, + }, + { + xtype: 'numberfield', + value: 'a4', + name: 'd_x', + fieldLabel: 'Meassured Length Dx (mm)', + allowBlank: false, + labelWidth: 200, + }, + { + xtype: 'numberfield', + value: 'a4', + name: 's_y', + fieldLabel: 'Meassured Start Offset Sy (mm)', + allowBlank: false, + labelWidth: 200, + }, + { + xtype: 'numberfield', + value: 'a4', + name: 'd_y', + fieldLabel: 'Meassured Length Dy (mm)', + allowBlank: false, + labelWidth: 200, + }, + ], + }, + ], +}) diff --git a/docs/lto-barcode/page-layout.js b/docs/lto-barcode/page-layout.js new file mode 100644 index 00000000..df704b15 --- /dev/null +++ b/docs/lto-barcode/page-layout.js @@ -0,0 +1,167 @@ +Ext.define('PageLayoutPanel', { + extend: 'Ext.panel.Panel', + alias: 'widget.pageLayoutPanel', + + layout: { + type: 'hbox', + align: 'stretch', + pack: 'start', + }, + + getValues: function() { + let me = this; + + let values = {}; + + Ext.Array.each(me.query('[isFormField]'), function(field) { + if (field.isValid()) { + let data = field.getSubmitData(); + Ext.Object.each(data, function(name, val) { + values[name] = val; + }); + } + }); + + let paper_size = values.paper_size || 'a4'; + + let param = Ext.apply({}, paper_sizes[paper_size]); + if (param === undefined) { + throw `unknown paper size ${paper_size}`; + } + + param.paper_size = paper_size; + + Ext.Object.each(values, function(name, val) { + let parsed = parseFloat(val, 10); + param[name] = isNaN(parsed) ? val : parsed; + }); + + return param; + }, + + controller: { + xclass: 'Ext.app.ViewController', + + control: { + 'paperSize': { + change: function(field, paper_size) { + let view = this.getView(); + let defaults = paper_sizes[paper_size]; + + let names = [ + 'label_width', + 'label_height', + 'margin_left', + 'margin_top', + 'column_spacing', + 'row_spacing', + ]; + for (i = 0; i < names.length; i++) { + let name = names[i]; + let f = view.down(`field[name=${name}]`); + let v = defaults[name]; + if (v != undefined) { + f.setValue(v); + f.setDisabled(defaults.fixed); + } else { + f.setDisabled(false); + } + } + }, + }, + 'field': { + change: function() { + let view = this.getView(); + let param = view.getValues(); + view.fireEvent("pagechanged", param); + }, + }, + }, + }, + + items: [ + { + border: false, + layout: { + type: 'vbox', + align: 'stretch', + pack: 'start', + }, + items: [ + { + xtype: 'paperSize', + name: 'paper_size', + value: 'a4', + fieldLabel: 'Paper Size', + }, + { + xtype: 'numberfield', + name: 'label_width', + fieldLabel: 'Label width', + minValue: 70, + allowBlank: false, + value: 70, + }, + { + xtype: 'numberfield', + name: 'label_height', + fieldLabel: 'Label height', + minValue: 17, + allowBlank: false, + value: 17, + }, + { + xtype: 'checkbox', + name: 'label_borders', + fieldLabel: 'Label borders', + value: true, + inputValue: true, + }, + ], + }, + { + border: false, + margin: '0 0 0 10', + layout: { + type: 'vbox', + align: 'stretch', + pack: 'start', + }, + items: [ + { + xtype: 'numberfield', + name: 'margin_left', + fieldLabel: 'Left margin', + minValue: 0, + allowBlank: false, + value: 0, + }, + { + xtype: 'numberfield', + name: 'margin_top', + fieldLabel: 'Top margin', + minValue: 0, + allowBlank: false, + value: 4, + }, + { + xtype: 'numberfield', + name: 'column_spacing', + fieldLabel: 'Column spacing', + minValue: 0, + allowBlank: false, + value: 0, + }, + { + xtype: 'numberfield', + name: 'row_spacing', + fieldLabel: 'Row spacing', + minValue: 0, + allowBlank: false, + value: 0, + }, + ], + }, + ], + +}); diff --git a/docs/lto-barcode/paper-size.js b/docs/lto-barcode/paper-size.js new file mode 100644 index 00000000..b084062d --- /dev/null +++ b/docs/lto-barcode/paper-size.js @@ -0,0 +1,49 @@ +let paper_sizes = { + a4: { + comment: 'A4 (plain)', + page_width: 210, + page_height: 297, + }, + letter: { + comment: 'Letter (plain)', + page_width: 215.9, + page_height: 279.4, + }, + avery3420: { + fixed: true, + comment: 'Avery Zweckform 3420', + page_width: 210, + page_height: 297, + label_width: 70, + label_height: 17, + margin_left: 0, + margin_top: 4, + column_spacing: 0, + row_spacing: 0, + }, +} + +function paper_size_combo_data() { + let data = []; + + for (let [key, value] of Object.entries(paper_sizes)) { + data.push({ value: key, text: value.comment }); + } + return data; +} + +Ext.define('PaperSize', { + extend: 'Ext.form.field.ComboBox', + alias: 'widget.paperSize', + + editable: false, + + displayField: 'text', + valueField: 'value', + queryMode: 'local', + + store: { + field: ['value', 'text'], + data: paper_size_combo_data(), + }, +}); diff --git a/docs/lto-barcode/prefix-field.js b/docs/lto-barcode/prefix-field.js new file mode 100644 index 00000000..e12f1bda --- /dev/null +++ b/docs/lto-barcode/prefix-field.js @@ -0,0 +1,15 @@ +Ext.define('PrefixField', { + extend: 'Ext.form.field.Text', + alias: 'widget.prefixfield', + + maxLength: 6, + allowBlank: false, + + maskRe: /([A-Za-z]+)$/, + + listeners: { + change: function(field) { + field.setValue(field.getValue().toUpperCase()); + }, + }, +}); diff --git a/docs/lto-barcode/tape-type.js b/docs/lto-barcode/tape-type.js new file mode 100644 index 00000000..33ec0152 --- /dev/null +++ b/docs/lto-barcode/tape-type.js @@ -0,0 +1,23 @@ +Ext.define('LtoTapeType', { + extend: 'Ext.form.field.ComboBox', + alias: 'widget.ltoTapeType', + + editable: false, + + displayField: 'text', + valueField: 'value', + queryMode: 'local', + + store: { + field: ['value', 'text'], + data: [ + { value: 'L8', text: "LTO-8" }, + { value: 'L7', text: "LTO-7" }, + { value: 'L6', text: "LTO-6" }, + { value: 'L5', text: "LTO-5" }, + { value: 'L4', text: "LTO-4" }, + { value: 'L3', text: "LTO-3" }, + { value: 'CU', text: "Cleaning Unit" }, + ], + }, +});