334 lines
9.2 KiB
HTML
334 lines
9.2 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset=utf-8 />
|
|
<title>Image to ZPL</title>
|
|
<style>
|
|
* {
|
|
font-family:Arial, Helvetica, sans-serif;
|
|
font-size: 12px;
|
|
}
|
|
body, html {
|
|
border: 0;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
#title {
|
|
background-color: #404040;
|
|
color: white;
|
|
padding: .75ex 2ex;
|
|
font-size: 16pt;
|
|
font-weight: bold;
|
|
}
|
|
#drop {
|
|
box-sizing: border-box;
|
|
width: 480px;
|
|
height: 90px;
|
|
text-align: center;
|
|
font-size: 16px;
|
|
line-height:61px; /* 90/2 + 16 */
|
|
color: #999;
|
|
border: 1px dashed #6495ED;
|
|
margin: 10px;
|
|
padding: 10px;
|
|
}
|
|
#drop.droppable {
|
|
background-color: #fcfeff;
|
|
color: #87cefa;
|
|
}
|
|
|
|
/* Original and rendered image text labels */
|
|
.label {
|
|
color: blue;
|
|
font-size: 125%;
|
|
font-weight: bold;
|
|
text-align: left;
|
|
padding-left: 10px;
|
|
padding-bottom: 8px;
|
|
}
|
|
|
|
/* Where the dropped and rendered images are displayed */
|
|
.content {
|
|
margin: 10px;
|
|
padding: 10px;
|
|
max-width: 600px;
|
|
max-height: 480px;
|
|
min-height: 480px;
|
|
overflow: auto;
|
|
text-align: center;
|
|
}
|
|
.content > * {
|
|
box-shadow: 0 0 0 1px blue;
|
|
}
|
|
/* Hidden but copy-able */
|
|
#zpltext {
|
|
opacity: .01;
|
|
position: absolute;
|
|
height: 0px;
|
|
}
|
|
table.fields th {
|
|
padding-left: 4ex;
|
|
text-align: right;
|
|
padding-right: 1.5ex;
|
|
}
|
|
table.fields th, table.fields td {
|
|
padding-bottom: 2ex;
|
|
}
|
|
/* Labels to the right of the input fields */
|
|
span {
|
|
font-weight: bold;
|
|
text-align: left;
|
|
padding-left: 1ex;
|
|
font-style: italic;
|
|
}
|
|
/* Black threshold */
|
|
input[type="number"] {
|
|
width: 4em;
|
|
border: 1px solid #6495ED;
|
|
border-radius: 3px;
|
|
padding: 3px .75ex;
|
|
padding-right: 1px;
|
|
text-align: right;
|
|
}
|
|
</style>
|
|
<link rel="stylesheet" type="text/css" href="copyzpl.css" />
|
|
<script type="text/javascript" src="pako.js"></script>
|
|
<script type="text/javascript" src="zpl-image.js"></script>
|
|
<script type="text/javascript">
|
|
let _filename = ''; // value set on image drop
|
|
window.addEventListener('load', function() {
|
|
document.getElementById('rotN').addEventListener('click', convertImage, false);
|
|
document.getElementById('rotL').addEventListener('click', convertImage, false);
|
|
document.getElementById('rotR').addEventListener('click', convertImage, false);
|
|
document.getElementById('rotI').addEventListener('click', convertImage, false);
|
|
document.getElementById('black').addEventListener('input', convertImage, false);
|
|
document.getElementById('notrim').addEventListener('click', convertImage, false);
|
|
document.getElementById('acscomp').addEventListener('click', convertImage, false);
|
|
document.getElementById('z64comp').addEventListener('click', convertImage, false);
|
|
document.getElementById('copyzpl').addEventListener('click', copyZPL, false);
|
|
|
|
// Setup the drop target
|
|
let drop = document.getElementById('drop');
|
|
|
|
function candrop(ev) {
|
|
ev.target.className = 'droppable';
|
|
ev.preventDefault();
|
|
return false;
|
|
}
|
|
function undrop(ev) {
|
|
ev.target.className = '';
|
|
}
|
|
|
|
drop.addEventListener('dragover', candrop, false);
|
|
drop.addEventListener('dragenter', candrop, false);
|
|
drop.addEventListener('dragleave', undrop, false);
|
|
|
|
drop.addEventListener('drop', function (ev) {
|
|
undrop(ev);
|
|
ev.preventDefault(); // stop the browser from redirecting
|
|
|
|
let xfer = ev.dataTransfer;
|
|
let files = xfer.files;
|
|
if (!files.length) {
|
|
return;
|
|
}
|
|
|
|
let file = files[0];
|
|
let reader = new FileReader();
|
|
|
|
reader.onloadend = function() {
|
|
let bin = this.result;
|
|
let img = document.getElementById('image');
|
|
|
|
_filename = file.name;
|
|
img.src = bin;
|
|
img.onload = convertImage;
|
|
|
|
// Make the img/canvas visible
|
|
let lbls = document.querySelectorAll('div.label');
|
|
for (let i = 0; i < lbls.length; i++) {
|
|
lbls[i].style.visibility = 'visible';
|
|
}
|
|
let divs = document.querySelectorAll('div.content');
|
|
for (let i = 0; i < divs.length; i++) {
|
|
divs[i].style.visibility = 'visible';
|
|
}
|
|
document.getElementById('copyzpl').style.visibility = 'visible';
|
|
}
|
|
reader.readAsDataURL(file);
|
|
return false;
|
|
}, false);
|
|
}, false);
|
|
function convertImage() {
|
|
if (!_filename) {
|
|
return;
|
|
}
|
|
let black = +document.getElementById('black').value || 50;
|
|
let rotrad = document.querySelector('input[name=rot]:checked');
|
|
let comprad = document.querySelector('input[name=compress]:checked');
|
|
let rot = rotrad && rotrad.value || 'N';
|
|
let comp = comprad && comprad.value || 'Z64';
|
|
let notrim = document.getElementById('notrim').checked;
|
|
|
|
// Get the image and convert to Z64
|
|
let img = document.getElementById('image');
|
|
let res;
|
|
let bmp; // actually a canvas object
|
|
if (comp == 'Z64') {
|
|
res = imageToZ64(img, { black:black, rotate:rot, notrim:notrim });
|
|
bmp = z64ToCanvas(res.z64, res.rowlen);
|
|
} else {
|
|
res = imageToACS(img, { black:black, rotate:rot, notrim:notrim });
|
|
bmp = acsToCanvas(res.acs, res.rowlen);
|
|
}
|
|
|
|
// Draw the image to our canvas
|
|
let cvs = document.getElementById('canvas');
|
|
cvs.width = bmp.width;
|
|
cvs.height = bmp.height;
|
|
cvs.getContext('2d').drawImage(bmp, 0, 0);
|
|
|
|
// Create the ZPL with a source comment
|
|
document.getElementById('zpltext').value =
|
|
'^FX ' + _filename + ' (' + res.width + 'x' + res.height + 'px, ' +
|
|
rot + '-Rotate, ' + black + '% Black)^FS\n' +
|
|
'^GFA,' + res.length + ',' + res.length + ',' + res.rowlen + ',' + (res.z64||res.acs) + '\n';
|
|
}
|
|
function copyZPL() {
|
|
let ta = document.getElementById('zpltext');
|
|
ta.select();
|
|
document.execCommand('copy');
|
|
}
|
|
function z64ToCanvas(z64, rowl) {
|
|
// Strip the ':Z64:' prefix and :XXXX crc16 suffix and convert to binary string.
|
|
// We do not validate the CRC.
|
|
let bin = atob(z64.substr(5, z64.length - 10));
|
|
|
|
// pako wants a Uint8Array
|
|
let buf = new Uint8Array(bin.length);
|
|
for (let i = 0, l = bin.length; i < l; i++) {
|
|
buf[i] = bin.charCodeAt(i);
|
|
}
|
|
|
|
let grf = pako.inflate(buf);
|
|
let l = grf.length;
|
|
let w = rowl * 8; // rowl is in bytes
|
|
let h = ~~(l / rowl);
|
|
|
|
// Render the GRF to a canvas
|
|
let cvs = document.createElement('canvas');
|
|
cvs.width = w;
|
|
cvs.height = h;
|
|
|
|
let ctx = cvs.getContext('2d');
|
|
let bmap = ctx.getImageData(0, 0, w, h);
|
|
let data = bmap.data;
|
|
let offs = 0;
|
|
for (let i = 0; i < l; i++) {
|
|
let byte = grf[i];
|
|
for (let bit = 0x80; bit; bit = bit >>> 1, offs += 4) {
|
|
if (bit & byte) {
|
|
data[offs] = 0;
|
|
data[offs+1] = 0;
|
|
data[offs+2] = 0;
|
|
data[offs+3] = 255; // Fully opaque
|
|
}
|
|
}
|
|
}
|
|
ctx.putImageData(bmap, 0, 0);
|
|
return cvs;
|
|
}
|
|
function acsToCanvas(acs, rowl) {
|
|
let hex = acs.replace(/[g-zG-Y]+([0-9a-fA-F])/g, ($0, $1) => {
|
|
let rep = 0;
|
|
for (let i = 0, l = $0.length-1; i < l; i++) {
|
|
let cd = $0.charCodeAt(i);
|
|
if (cd < 90) { // 'Z'
|
|
rep += cd - 70;
|
|
} else {
|
|
rep += (cd - 102) * 20;
|
|
}
|
|
}
|
|
return $1.repeat(rep);
|
|
});
|
|
|
|
let bytes = Array(hex.length/2);
|
|
for (let i = 0, l = hex.length; i < l; i += 2) {
|
|
bytes[i>>1] = parseInt(hex.substr(i,2), 16);
|
|
}
|
|
|
|
let l = bytes.length;
|
|
let w = rowl * 8; // rowl is in bytes
|
|
let h = ~~(l / rowl);
|
|
|
|
// Render the GRF to a canvas
|
|
let cvs = document.createElement('canvas');
|
|
cvs.width = w;
|
|
cvs.height = h;
|
|
|
|
let ctx = cvs.getContext('2d');
|
|
let bmap = ctx.getImageData(0, 0, w, h);
|
|
let data = bmap.data;
|
|
let offs = 0;
|
|
for (let i = 0; i < l; i++) {
|
|
let byte = bytes[i];
|
|
for (let bit = 0x80; bit; bit = bit >>> 1, offs += 4) {
|
|
if (bit & byte) {
|
|
data[offs] = 0;
|
|
data[offs+1] = 0;
|
|
data[offs+2] = 0;
|
|
data[offs+3] = 255; // Fully opaque
|
|
}
|
|
}
|
|
}
|
|
ctx.putImageData(bmap, 0, 0);
|
|
return cvs;
|
|
}
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div id="title">Image to ZPL</div>
|
|
<div>
|
|
<div style="display:inline-block">
|
|
<div id="drop">Drop image here</div>
|
|
</div>
|
|
<table style="display:inline-table" borders=0 class="fields">
|
|
<tr><th>Image Rotation<td colspan=2>
|
|
<label for="rotN"><input type="radio" name="rot" value="N"
|
|
id="rotN" checked>Normal</label>
|
|
<label for="rotR"><input type="radio" name="rot" value="R"
|
|
id="rotR">Right (CW)</label>
|
|
<label for="rotL"><input type="radio" name="rot" value="L"
|
|
id="rotL">Left (CCW)</label>
|
|
<label for="rotI"><input type="radio" name="rot" value="I"
|
|
id="rotI">Inverted</label>
|
|
<td colspan=2 style="position:relative;padding-left:10mm">
|
|
<button id="copyzpl" style="visibility:hidden"></button>
|
|
<tr><th>Black Threshold
|
|
<td><input type="number" id="black" min="1" max="99" step="1" value="50">
|
|
<span>1..99</span>
|
|
<td><label for="notrim">
|
|
<input type="checkbox" id="notrim" value="Y"> No Trim</label>
|
|
<tr><th>ZPL Format<td colspan=2>
|
|
<label for="z64comp"><input type="radio" name="compress" value="Z64"
|
|
id="z64comp" checked>Z64</label>
|
|
<label for="acscomp"><input type="radio" name="compress" value="ACS"
|
|
id="acscomp">ACS</label>
|
|
</table>
|
|
</div>
|
|
<textarea rows=24 cols=112 id="zpltext" readonly></textarea>
|
|
|
|
<div style="display:inline-block">
|
|
<div class="label" style="visibility:hidden">Original Image:</div>
|
|
<div class="content" style="visibility:hidden"><img id="image"></div>
|
|
</div>
|
|
<div style="display:inline-block">
|
|
<div class="label" style="visibility:hidden">Rendered Image:</div>
|
|
<div class="content" style="visibility:hidden">
|
|
<canvas id="canvas" width=10 height=10></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
</body>
|
|
</html>
|