Initial
This commit is contained in:
BIN
assets/custom-qr-sticker-example.jpeg
Normal file
BIN
assets/custom-qr-sticker-example.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
BIN
assets/qr-code-example.jpeg
Normal file
BIN
assets/qr-code-example.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
23
index.html
Normal file
23
index.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Sticker Print Demo</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
|
||||||
|
integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
|
||||||
|
|
||||||
|
<script type="text/javascript" src="lib/zpl-image/pako.js"></script>
|
||||||
|
<script type="text/javascript" src="lib/zpl-image/zpl-image.js"></script>
|
||||||
|
<script src="./sticker-print.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Sticker Print Demo</h1>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
21
lib/zpl-image/LICENSE
Normal file
21
lib/zpl-image/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
zpl-image
|
||||||
|
|
||||||
|
Copyright 2019 Mark Warren
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
235
lib/zpl-image/README.md
Normal file
235
lib/zpl-image/README.md
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
|
||||||
|
# zpl-image
|
||||||
|
|
||||||
|
A pure javascript module that converts images to either Z64-encoded or ACS-encoded GRF bitmaps for use with ZPL.
|
||||||
|
The term ACS (Alternative Compression Scheme) denotes the run-length compression algorithm described in the section
|
||||||
|
of the ZPL Reference Manual titled "Alternative Data Compression Scheme". Z64 typically gives better compression
|
||||||
|
but is not available on all printers (especially older ones). The ACS encoding should work on any printer made
|
||||||
|
since the mid 90s, maybe earlier.
|
||||||
|
|
||||||
|
This module provides the following features:
|
||||||
|
|
||||||
|
- Works in both node.js and modern browsers.
|
||||||
|
- Converts the image to grayscale, then applies a user-supplied blackness
|
||||||
|
threshold to decide which pixels are black.
|
||||||
|
- Optionally removes any empty/white space around the edges of the image.
|
||||||
|
- Optionally rotates the image to one of the orthogonal angles. This step
|
||||||
|
is often necessary as ZPL does not provide the ability to rotate an image
|
||||||
|
during formatting.
|
||||||
|
- Converts the monochrome image to a GRF bitmap.
|
||||||
|
- Converts the GRF bitmap to either Z64 or ACS encoding.
|
||||||
|
- For Z64, zlib in node.js or pako.js in the browser is used for compression.
|
||||||
|
|
||||||
|
The blackness threshold is specified as an integer between 1 and 99 (think of it as a
|
||||||
|
gray percentage). Pixels darker than the gray% are converted to black. The default is 50.
|
||||||
|
|
||||||
|
Rotation is specified as one of the values:
|
||||||
|
|
||||||
|
- `'N'` : No rotation, the default.
|
||||||
|
- `'L'` : Left, 90 degrees counter-clockwise rotation.
|
||||||
|
- `'R'` : Right, 90 degrees clockwise rotation.
|
||||||
|
- `'I'` : Inverted, 180 degrees rotation.
|
||||||
|
- `'B'` : Same as `'L'` but named to match the ZPL notation.
|
||||||
|
|
||||||
|
Blackness and rotation are passed via an options object. For example, to specify
|
||||||
|
a black threshold of 56% and rotation of -90 degrees, you would pass in:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{ black:56, rotate:'L' }
|
||||||
|
```
|
||||||
|
|
||||||
|
Trimming of empty space around the image is enabled by default. To disable, specify
|
||||||
|
the option `notrim:true`.
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
Included with this module is the file `zpl-image.html`. You can run it directly
|
||||||
|
from the browser using the `file://` scheme. It lets you drag and drop an image
|
||||||
|
and then interactively adjust the blackness threshold and rotation.
|
||||||
|
|
||||||
|
When you are satisfied with the results, select either Z64 or ACS encoding and
|
||||||
|
click the clipboard icon to copy the ZPL. The ZPL will have the following format:
|
||||||
|
|
||||||
|
```
|
||||||
|
^FX filename.ext (WxHpx, X-Rotate, XX% Black)^FS
|
||||||
|
^GFA,grflen,grflen,rowlen,...ASCII-armored-encoding...
|
||||||
|
```
|
||||||
|
|
||||||
|
`^FX ... ^FS` is a ZPL comment.
|
||||||
|
|
||||||
|
`^GF` is the ZPL command for use-once image rendering (that is, the image is not
|
||||||
|
saved to the printer for later recall by other label formats).
|
||||||
|
|
||||||
|
The rendered image displayed on the page is the actual data decoded and then drawn
|
||||||
|
to a canvas. If you are interested in that bit of functionality, look for `z64ToCanvas`
|
||||||
|
and `acsToCanvas` in the `zpl-image.html` file.
|
||||||
|
|
||||||
|
## Generic Browser Usage
|
||||||
|
|
||||||
|
To use in the browser, include the following two scripts:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script type="text/javascript" src="url-path-to/pako.js"></script>
|
||||||
|
<script type="text/javascript" src="url-path-to/zpl-image.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
There is a version of pako.js included with this module, but it will not be updated
|
||||||
|
frequently. It is primarily intended for the demo html file but should be sufficient
|
||||||
|
for production use.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Works with <img> and <canvas> elements or any element that is
|
||||||
|
// compatible with CanvasRenderingContext2D.drawImage().
|
||||||
|
let img = document.getElementById('image');
|
||||||
|
let res = imageToZ64(img); // Uses all defaults
|
||||||
|
|
||||||
|
// res.length is the uncompressed GRF length.
|
||||||
|
// res.rowlen is the GRF row length.
|
||||||
|
// res.z64 is the Z64 encoded string.
|
||||||
|
let zpl = `^GFA,${res.length},${res.length},${res.rowlen},${res.z64}`;
|
||||||
|
```
|
||||||
|
|
||||||
|
An alternative for when you already have the pixel values in RGBA format
|
||||||
|
(either in a Uint8Array or Array of integers clamped to 0..255) is
|
||||||
|
`rgbaToZ64()`. This function is the lower-level converter used
|
||||||
|
by both node.js and `imageToZ64()`. See the node.js section for more details.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// `rgba` is an array of RGBA values.
|
||||||
|
// `width` is the width of the image, in pixels.
|
||||||
|
// The return value is the same as above.
|
||||||
|
let res = rgbaToZ64(rgba, width, { black:55, rotate:'I' });
|
||||||
|
```
|
||||||
|
|
||||||
|
The same interfaces exist for ACS encoding, using the functions `imageToACS()` and
|
||||||
|
`rgbaToACS()`. The returned object from each function is identical to the above, with
|
||||||
|
the exception that the encoded text is in the `acs` property instead of `z64`.
|
||||||
|
|
||||||
|
## RequireJS Browser Usage
|
||||||
|
|
||||||
|
This is untested but the module exports are wrapped in a UMD, so in theory you
|
||||||
|
should be able to use this with RequireJS. The exports are the same as with the
|
||||||
|
generic browser usage:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Use the Z64 interface
|
||||||
|
const { imageToZ64, rgbaToZ64 } = require("zpl-image");
|
||||||
|
|
||||||
|
// Or the ACS interface
|
||||||
|
const { imageToACS, rgbaToACS } = require("zpl-image");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Node.js Usage
|
||||||
|
|
||||||
|
The exports from `require("zpl-image")` are the functions `rgbaToZ64()` and
|
||||||
|
`rgbaToACS()`.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// The Z64 interface
|
||||||
|
const rgbaToZ64 = require("zpl-image").rgbaToZ64;
|
||||||
|
|
||||||
|
// The ACS interface
|
||||||
|
const rgbaToACS = require("zpl-image").rgbaToACS;
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Both methods take two or three parameters:
|
||||||
|
|
||||||
|
```
|
||||||
|
rgbaToZ64(rgba, width [, opts])
|
||||||
|
rgbaToACS(rgba, width [, opts])
|
||||||
|
```
|
||||||
|
|
||||||
|
`rgba` is an array-like object with length equal to `width * height * 4`.
|
||||||
|
An array-like object can be a Buffer, Uint8Array, or Array of integers
|
||||||
|
clamped to 0..255. `width` and `height` are the dimensions of the image, in pixels.
|
||||||
|
Each "quad" of the RGBA array is structured as:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
rgba[i] // red 0..255
|
||||||
|
rgba[i+1] // green 0..255
|
||||||
|
rgba[i+2] // blue 0..255
|
||||||
|
rgba[i+3] // alpha (0 == fully transparent, 255 == fully opaque)
|
||||||
|
```
|
||||||
|
|
||||||
|
Because of the varied nature of the node.js ecosystem, zpl-image does not include
|
||||||
|
any dependencies for image modules. You need to decide what types of images to
|
||||||
|
support and which image processing package(s) to use. Below are some simple
|
||||||
|
examples showing three different image modules:
|
||||||
|
|
||||||
|
- [pngjs](https://www.npmjs.com/package/pngjs)
|
||||||
|
- [omggif](https://www.npmjs.com/package/omggif)
|
||||||
|
- [jpeg-js](https://www.npmjs.com/package/jpeg-js)
|
||||||
|
|
||||||
|
All of the following examples show Z64 encoding but can be switched to ACS
|
||||||
|
by simply renaming `Z64` to `ACS`.
|
||||||
|
|
||||||
|
## pngjs (PNG Conversion)
|
||||||
|
|
||||||
|
[pngjs](https://www.npmjs.com/package/pngjs)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Synchronous pngjs usage
|
||||||
|
const fs = require('fs');
|
||||||
|
const PNG = require('pngjs').PNG;
|
||||||
|
const rgbaToZ64 = require('zpl-image').rgbaToZ64;
|
||||||
|
|
||||||
|
let buf = fs.readFileSync('tux.png');
|
||||||
|
let png = PNG.sync.read(buf);
|
||||||
|
let res = rgbaToZ64(png.data, png.width, { black:53 });
|
||||||
|
|
||||||
|
// res.length is the uncompressed GRF length.
|
||||||
|
// res.rowlen is the GRF row length.
|
||||||
|
// res.z64 is the Z64 encoded string.
|
||||||
|
let zpl = `^GFA,${res.length},${res.length},${res.rowlen},${res.z64}`;
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Async pngjs usage
|
||||||
|
const fs = require('fs');
|
||||||
|
const PNG = require('pngjs').PNG;
|
||||||
|
const rgbaToZ64 = require('zpl-image').rgbaToZ64;
|
||||||
|
|
||||||
|
fs.createReadStream('tux.png')
|
||||||
|
.pipe(new PNG({ filterType: 4 }))
|
||||||
|
.on('parsed', function() {
|
||||||
|
// res is the same as above
|
||||||
|
let res = rgbaToZ64(this.data, this.width, { black:52, rotate:'R' });
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## omggif (GIF Conversion)
|
||||||
|
|
||||||
|
[omggif](https://www.npmjs.com/package/omggif)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const fs = require('fs');
|
||||||
|
const GIF = require('omggif');
|
||||||
|
const rgbaToZ64 = require('zpl-image').rgbaToZ64;
|
||||||
|
|
||||||
|
let buf = fs.readFileSync('tux.gif');
|
||||||
|
let gif = new GIF.GifReader(buf);
|
||||||
|
let rgba = Buffer.alloc(gif.width * gif.height * 4);
|
||||||
|
|
||||||
|
// Decode only the first frame
|
||||||
|
gif.decodeAndBlitFrameRGBA(0, rgba);
|
||||||
|
|
||||||
|
let res = rgbaToZ64(rgba, gif.width, { black:47 });
|
||||||
|
```
|
||||||
|
|
||||||
|
## jpeg-js (JPEG Conversion)
|
||||||
|
|
||||||
|
[jpeg-js](https://www.npmjs.com/package/jpeg-js)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const fs = require('fs');
|
||||||
|
const JPG = require('jpeg-js');
|
||||||
|
const rgbaToZ64 = require('zpl-image').rgbaToZ64;
|
||||||
|
|
||||||
|
let buf = fs.readFileSync('tux.jpg');
|
||||||
|
let jpg = JPG.decode(buf);
|
||||||
|
let res = rgbaToZ64(jpg.data, jpg.width, { black:51, rotate:'I' });
|
||||||
|
```
|
||||||
|
|
||||||
|
## MIT License
|
||||||
|
|
||||||
22
lib/zpl-image/copyzpl.css
Normal file
22
lib/zpl-image/copyzpl.css
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
/* Keep the unwieldy data-url out of the main code */
|
||||||
|
#copyzpl {
|
||||||
|
position: absolute;
|
||||||
|
top: -2ex;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #fff;
|
||||||
|
background-position:50% 50%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
#copyzpl:hover {
|
||||||
|
background-color: #99beff;
|
||||||
|
}
|
||||||
|
#copyzpl:active {
|
||||||
|
background-color: #4c8dff;
|
||||||
|
}
|
||||||
|
#copyzpl {
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAnCAYAAAAcsCj6AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsIAAA7CARUoSoAAAAT5SURBVFhHtVdLbBtVFJ1fZvwZx6k99sSf1FVKPlWaBJKS8JVAlBIQqKpEJGDXCgnBogLBPguQKoRYIbFg0xWsK5AQFUWIRSVgUVEVyI8ShygORsqniZ2xx/PhvOfx1FOHJA2TI12/9+5789679557Z8wy+wfb0zOc3iqvntW2t5+s6TVLjkW/b0+nv/nj55+WMW076/xBb2+vEgqHr3Acp2Nosyxro1+TI5Frp554pru+ykdks7l3yQGCIFjRWPwrRVG+5Hm+goMtVe38zFnmD2zbZmOx+HXSTSY7L4+++Koy/tIraiAUukJ0gUBgbmpqiqOL9wDvtDticnJS3CiVnvrw0qUXKtXK2Zquy6Io5reKy8p6YXHI1KtjhmFkOZ4Xfr31W1WWo1a5vLWCRw8Wz4EHx4aDIfkm3GiSmEFF4mbBlRZxLfpUR+agM4LB4Myz586dpA//ByhLs9nsc6VS6QNd149i2OyatkqlErEsa1/uIgiHw3cQgpozJLDa2tqmoX+jUCjMMvF4/GEo/sYEve1hiShKhXgq1c9ZFvNarVZToaSAmxheEOrC7xriHUGeaTwPVztahtH1ago/H3O6Xul3dIwoSaXM0aNvh9vj40TkaOw8Nlh3pvcEiWkymZzgJGkQMtTV1fUyvPePM81omtZDfH4VfWq2qqqfonXjhblBkKOIbouLdhJczkR7BNIAm87lLjQIB1Ld9pKB437Hr1UfMEwkkdiUJOkv3JIEntlDLKTMzUQi0UwYOyjLMw3XomXZkCx/u10qnSYKVU1dLBZXPqGzwMTEhDQ9nx/ZKm9lONt2LyeJYsBkGdswdFLmKFiLNcOR0Hx+fv4Whu6le06cGL89O/sjmM6EQqFFRpYj30FPTU50pt+iqzywcT0Iqk1DSFWhlaVJR8VJs2Z0d/edauQrDvzTE8NkMvU6WeQnegeGH28c2BJDlAyDtrbtixAEQmKEdhx4DhRFXiLt0OjoKCxfAutoGbtfQTFfyh479gjZyzaYZhJ5DzQA0q4sLw8jZxQEmj2IYJvU5tqdPrIXSqNLLAJvWmA9adRM5of2aPQGgqzBUrwFZB39yr2yy9wvinLkBtnLyUEK9DmwVL6GPg2qqqbP0xkfcbz/5KMgDTGEsHSBWOje4DBgWYgiw5AKBCIxFvLZdpO0gcGRkVHcZonc7CCC6rSUyeUoaQSB5qcD2xtDpAsd+0kaQZICMMp97XhJg+uRxk/SmLpOvhZcK1k8dLVcLp8hA5DmQrFYuEz0uVxOwheA90L7BIq4tbi4WEXXPj4w8NjC9PR1WM6g0nhL22GwtLuptMHyBQ7WtrzW/STNPbDxiWG1pIWfpGkGyGMTC5vTggbXT9KILCuQ1kVzDFOZzEWiI5XeDyEYHhk7A6Po/i2vJ5bj6G0Ii/0QAq1S2W70CTwHms7bwk9U66XNBSltd49vKXL/HwL9RKkDlvJgMe9ayQn1SuMnONZsa8QTsJAWpmtXrVp9yOn6hoqmuXviYJsNhuWvtXLpeaKAuVpnKvVFpL1jVjd0ja5ywTNwgISXi2napjcusAMfzFLNMnQbiUh0sEnXSqWu9dXV91AiA0SHdJljYjHlo/r8XYGvfZPmfZVE4nM+FlOL+H/xtGkaCpSHhrAsz4Y7Ot4kDOK6+/pGNtfW3lnb2HjANs37/8u0C1DKmJiizISDwffz+fzcv7cGZ55/jBKaAAAAAElFTkSuQmCC);
|
||||||
|
}
|
||||||
31
lib/zpl-image/package.json
Normal file
31
lib/zpl-image/package.json
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "zpl-image",
|
||||||
|
"version": "0.2.0",
|
||||||
|
"description": "A pure javascript module that converts PNG, JPEG, and GIF to Z64-encoded GRF bitmaps for use with ZPL.",
|
||||||
|
"main": "zpl-image.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/metafloor/zpl-image.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"image",
|
||||||
|
"ZPL",
|
||||||
|
"Z64",
|
||||||
|
"GRF",
|
||||||
|
"PNG",
|
||||||
|
"JPEG",
|
||||||
|
"GIF"
|
||||||
|
],
|
||||||
|
"author": "Mark Warren",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/metafloor/zpl-image/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/metafloor/zpl-image",
|
||||||
|
"directories": {
|
||||||
|
"test": "tests"
|
||||||
|
}
|
||||||
|
}
|
||||||
6441
lib/zpl-image/pako.js
Normal file
6441
lib/zpl-image/pako.js
Normal file
File diff suppressed because it is too large
Load Diff
333
lib/zpl-image/zpl-image.html
Normal file
333
lib/zpl-image/zpl-image.html
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
<!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>
|
||||||
444
lib/zpl-image/zpl-image.js
Normal file
444
lib/zpl-image/zpl-image.js
Normal file
@ -0,0 +1,444 @@
|
|||||||
|
|
||||||
|
(function (root, factory) {
|
||||||
|
if (typeof define === 'function' && define.amd) {
|
||||||
|
define([], factory);
|
||||||
|
} else if (typeof module === 'object' && module.exports) {
|
||||||
|
module.exports = factory();
|
||||||
|
} else {
|
||||||
|
// generic browser usage
|
||||||
|
let ex = factory();
|
||||||
|
for (let id in ex) {
|
||||||
|
root[id] = ex[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(typeof self !== 'undefined' ? self : this, function () {
|
||||||
|
|
||||||
|
const zlib = typeof process == 'object' && typeof process.release == 'object' &&
|
||||||
|
process.release.name == 'node' ? require('zlib') : null;
|
||||||
|
|
||||||
|
const hexmap = (()=> {
|
||||||
|
let arr = Array(256);
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
arr[i] = '0' + i.toString(16);
|
||||||
|
}
|
||||||
|
for (let i = 16; i < 256; i++) {
|
||||||
|
arr[i] = i.toString(16);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
})();
|
||||||
|
|
||||||
|
// DOM-specialized version for browsers.
|
||||||
|
function imageToZ64(img, opts) {
|
||||||
|
// Draw the image to a temp canvas so we can access its RGBA data
|
||||||
|
let cvs = document.createElement('canvas');
|
||||||
|
let ctx = cvs.getContext('2d');
|
||||||
|
|
||||||
|
cvs.width = +img.width || img.offsetWidth;
|
||||||
|
cvs.height = +img.height || img.offsetHeight;
|
||||||
|
ctx.imageSmoothingQuality = 'high'; // in case canvas needs to scale image
|
||||||
|
ctx.drawImage(img, 0, 0, cvs.width, cvs.height);
|
||||||
|
|
||||||
|
let pixels = ctx.getImageData(0, 0, cvs.width, cvs.height);
|
||||||
|
return rgbaToZ64(pixels.data, pixels.width, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DOM-specialized version for browsers.
|
||||||
|
function imageToACS(img, opts) {
|
||||||
|
// Draw the image to a temp canvas so we can access its RGBA data
|
||||||
|
let cvs = document.createElement('canvas');
|
||||||
|
let ctx = cvs.getContext('2d');
|
||||||
|
|
||||||
|
cvs.width = +img.width || img.offsetWidth;
|
||||||
|
cvs.height = +img.height || img.offsetHeight;
|
||||||
|
ctx.imageSmoothingQuality = 'high'; // in case canvas needs to scale image
|
||||||
|
ctx.drawImage(img, 0, 0, cvs.width, cvs.height);
|
||||||
|
|
||||||
|
let pixels = ctx.getImageData(0, 0, cvs.width, cvs.height);
|
||||||
|
return rgbaToACS(pixels.data, pixels.width, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uses zlib on node.js, pako.js in the browser.
|
||||||
|
//
|
||||||
|
// `rgba` can be a Uint8Array or Buffer, or an Array of integers between 0 and 255.
|
||||||
|
// `width` is the image width, in pixels
|
||||||
|
// `opts` is an options object:
|
||||||
|
// `black` is the blackness percent between 1..99, default 50.
|
||||||
|
// `rotate` is one of:
|
||||||
|
// 'N' no rotation (default)
|
||||||
|
// 'L' rotate 90 degrees counter-clockwise
|
||||||
|
// 'R' rotate 90 degrees clockwise
|
||||||
|
// 'I' rotate 180 degrees (inverted)
|
||||||
|
// 'B' same as 'L'
|
||||||
|
function rgbaToZ64(rgba, width, opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
width = width|0;
|
||||||
|
if (!width || width < 0) {
|
||||||
|
throw new Error('Invalid width');
|
||||||
|
}
|
||||||
|
let height = ~~(rgba.length / width / 4);
|
||||||
|
|
||||||
|
// Create a monochome image, cropped to remove padding.
|
||||||
|
// The return is a Uint8Array with extra properties width and height.
|
||||||
|
let mono = monochrome(rgba, width, height, +opts.black || 50, opts.notrim);
|
||||||
|
|
||||||
|
let buf;
|
||||||
|
switch (opts.rotate) {
|
||||||
|
case 'R': buf = right(mono); break;
|
||||||
|
case 'B':
|
||||||
|
case 'L': buf = left(mono); break;
|
||||||
|
case 'I': buf = invert(mono); break;
|
||||||
|
default: buf = normal(mono); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compress and base64 encode
|
||||||
|
let imgw = buf.width;
|
||||||
|
let imgh = buf.height;
|
||||||
|
let rowl = ~~((imgw + 7) / 8);
|
||||||
|
let b64;
|
||||||
|
if (zlib) {
|
||||||
|
b64 = zlib.deflateSync(buf).toString('base64');
|
||||||
|
} else {
|
||||||
|
b64 = u8tob64(pako.deflate(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example usage of the return value `rv`:
|
||||||
|
// '^GFA,' + rv.length + ',' + rv.length + ',' + rv.rowlen + ',' + rv.z64
|
||||||
|
return {
|
||||||
|
length: buf.length, // uncompressed number of bytes
|
||||||
|
rowlen: rowl, // number of packed bytes per row
|
||||||
|
width: imgw, // rotated image width in pixels
|
||||||
|
height: imgh, // rotated image height in pixels
|
||||||
|
z64: ':Z64:' + b64 + ':' + crc16(b64),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements the Alternative Data Compression Scheme as described in the ref manual.
|
||||||
|
//
|
||||||
|
// `rgba` can be a Uint8Array or Buffer, or an Array of integers between 0 and 255.
|
||||||
|
// `width` is the image width, in pixels
|
||||||
|
// `opts` is an options object:
|
||||||
|
// `black` is the blackness percent between 1..99, default 50.
|
||||||
|
// `rotate` is one of:
|
||||||
|
// 'N' no rotation (default)
|
||||||
|
// 'L' rotate 90 degrees counter-clockwise
|
||||||
|
// 'R' rotate 90 degrees clockwise
|
||||||
|
// 'I' rotate 180 degrees (inverted)
|
||||||
|
// 'B' same as 'L'
|
||||||
|
function rgbaToACS(rgba, width, opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
width = width|0;
|
||||||
|
if (!width || width < 0) {
|
||||||
|
throw new Error('Invalid width');
|
||||||
|
}
|
||||||
|
let height = ~~(rgba.length / width / 4);
|
||||||
|
|
||||||
|
// Create a monochome image, cropped to remove padding.
|
||||||
|
// The return is a Uint8Array with extra properties width and height.
|
||||||
|
let mono = monochrome(rgba, width, height, +opts.black || 50, opts.notrim);
|
||||||
|
|
||||||
|
let buf;
|
||||||
|
switch (opts.rotate) {
|
||||||
|
case 'R': buf = right(mono); break;
|
||||||
|
case 'B':
|
||||||
|
case 'L': buf = left(mono); break;
|
||||||
|
case 'I': buf = invert(mono); break;
|
||||||
|
default: buf = normal(mono); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode in hex and apply the "Alternative Data Compression Scheme"
|
||||||
|
//
|
||||||
|
// G H I J K L M N O P Q R S T U V W X Y
|
||||||
|
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
||||||
|
//
|
||||||
|
// g h i j k l m n o p q r s t u v w x y z
|
||||||
|
// 20 40 60 80 100 120 140 160 180 200 220 240 260 280 300 320 340 360 380 400
|
||||||
|
//
|
||||||
|
let imgw = buf.width;
|
||||||
|
let imgh = buf.height;
|
||||||
|
let rowl = ~~((imgw + 7) / 8);
|
||||||
|
|
||||||
|
let hex = '';
|
||||||
|
for (let i = 0, l = buf.length; i < l; i++) {
|
||||||
|
hex += hexmap[buf[i]];
|
||||||
|
}
|
||||||
|
let acs = '';
|
||||||
|
let re = /([0-9a-fA-F])\1{2,}/g;
|
||||||
|
let match = re.exec(hex);
|
||||||
|
let offset = 0;
|
||||||
|
while (match) {
|
||||||
|
acs += hex.substring(offset, match.index);
|
||||||
|
let l = match[0].length;
|
||||||
|
while (l >= 400) {
|
||||||
|
acs += 'z';
|
||||||
|
l -= 400;
|
||||||
|
}
|
||||||
|
if (l >= 20) {
|
||||||
|
acs += '_ghijklmnopqrstuvwxy'[((l / 20)|0)];
|
||||||
|
l = l % 20;
|
||||||
|
}
|
||||||
|
if (l) {
|
||||||
|
acs += '_GHIJKLMNOPQRSTUVWXY'[l];
|
||||||
|
}
|
||||||
|
acs += match[1];
|
||||||
|
offset = re.lastIndex;
|
||||||
|
match = re.exec(hex);
|
||||||
|
}
|
||||||
|
acs += hex.substr(offset);
|
||||||
|
|
||||||
|
// Example usage of the return value `rv`:
|
||||||
|
// '^GFA,' + rv.length + ',' + rv.length + ',' + rv.rowlen + ',' + rv.acs
|
||||||
|
return {
|
||||||
|
length: buf.length, // uncompressed number of bytes
|
||||||
|
rowlen: rowl, // number of packed bytes per row
|
||||||
|
width: imgw, // rotated image width in pixels
|
||||||
|
height: imgh, // rotated image height in pixels
|
||||||
|
acs: acs,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal, unrotated case
|
||||||
|
function normal(mono) {
|
||||||
|
let width = mono.width;
|
||||||
|
let height = mono.height;
|
||||||
|
|
||||||
|
let buf = new Uint8Array(~~((width + 7) / 8) * height);
|
||||||
|
let idx = 0; // index into buf
|
||||||
|
let byte = 0; // current byte of image data
|
||||||
|
let bitx = 0; // bit index
|
||||||
|
for (let i = 0, n = mono.length; i < n; i++) {
|
||||||
|
byte |= mono[i] << (7 - (bitx++ & 7));
|
||||||
|
|
||||||
|
if (bitx == width || !(bitx & 7)) {
|
||||||
|
buf[idx++] = byte;
|
||||||
|
byte = 0;
|
||||||
|
if (bitx == width) {
|
||||||
|
bitx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.width = width;
|
||||||
|
buf.height = height;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inverted 180 degrees
|
||||||
|
function invert(mono) {
|
||||||
|
let width = mono.width;
|
||||||
|
let height = mono.height;
|
||||||
|
|
||||||
|
let buf = new Uint8Array(~~((width + 7) / 8) * height);
|
||||||
|
let idx = 0; // index into buf
|
||||||
|
let byte = 0; // current byte of image data
|
||||||
|
let bitx = 0; // bit index
|
||||||
|
for (let i = mono.length-1; i >= 0; i--) {
|
||||||
|
byte |= mono[i] << (7 - (bitx++ & 7));
|
||||||
|
|
||||||
|
if (bitx == width || !(bitx & 7)) {
|
||||||
|
buf[idx++] = byte;
|
||||||
|
byte = 0;
|
||||||
|
if (bitx == width) {
|
||||||
|
bitx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.width = width;
|
||||||
|
buf.height = height;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate 90 degrees counter-clockwise
|
||||||
|
function left(mono) {
|
||||||
|
let width = mono.width;
|
||||||
|
let height = mono.height;
|
||||||
|
|
||||||
|
let buf = new Uint8Array(~~((height + 7) / 8) * width);
|
||||||
|
let idx = 0; // index into buf
|
||||||
|
let byte = 0; // current byte of image data
|
||||||
|
for (let x = width - 1; x >= 0; x--) {
|
||||||
|
let bitx = 0; // bit index
|
||||||
|
for (let y = 0; y < height; y++) {
|
||||||
|
byte |= mono[y * width + x] << (7 - (bitx++ & 7));
|
||||||
|
|
||||||
|
if (y == height-1 || !(bitx & 7)) {
|
||||||
|
buf[idx++] = byte;
|
||||||
|
byte = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.width = height;
|
||||||
|
buf.height = width;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Rotate 90 degrees clockwise
|
||||||
|
function right(mono) {
|
||||||
|
let width = mono.width;
|
||||||
|
let height = mono.height;
|
||||||
|
|
||||||
|
let buf = new Uint8Array(~~((height + 7) / 8) * width);
|
||||||
|
let idx = 0; // index into buf
|
||||||
|
let byte = 0; // current byte of image data
|
||||||
|
for (let x = 0; x < width; x++) {
|
||||||
|
let bitx = 0; // bit index
|
||||||
|
for (let y = height - 1; y >= 0; y--) {
|
||||||
|
byte |= mono[y * width + x] << (7 - (bitx++ & 7));
|
||||||
|
|
||||||
|
if (y == 0 || !(bitx & 7)) {
|
||||||
|
buf[idx++] = byte;
|
||||||
|
byte = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.width = height;
|
||||||
|
buf.height = width;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the RGBA to monochrome, 1-bit-per-byte. Crops
|
||||||
|
// empty space around the edges of the image if !notrim.
|
||||||
|
function monochrome(rgba, width, height, black, notrim) {
|
||||||
|
// Convert black from percent to 0..255 value
|
||||||
|
black = 255 * black / 100;
|
||||||
|
|
||||||
|
let minx, maxx, miny, maxy;
|
||||||
|
if (notrim) {
|
||||||
|
minx = miny = 0;
|
||||||
|
maxx = width-1;
|
||||||
|
maxy = height-1;
|
||||||
|
} else {
|
||||||
|
// Run through the image and determine bounding box
|
||||||
|
maxx = maxy = 0;
|
||||||
|
minx = width;
|
||||||
|
miny = height;
|
||||||
|
let x = 0, y = 0;
|
||||||
|
for (let i = 0, n = width * height * 4; i < n; i += 4) {
|
||||||
|
// Alpha blend with white.
|
||||||
|
let a = rgba[i+3] / 255;
|
||||||
|
let r = rgba[i] * .3 * a + 255 * (1 - a);
|
||||||
|
let g = rgba[i+1] * .59 * a + 255 * (1 - a);
|
||||||
|
let b = rgba[i+2] * .11 * a + 255 * (1 - a);
|
||||||
|
let gray = r + g + b;
|
||||||
|
|
||||||
|
if (gray <= black) {
|
||||||
|
if (minx > x) minx = x;
|
||||||
|
if (miny > y) miny = y;
|
||||||
|
if (maxx < x) maxx = x;
|
||||||
|
if (maxy < y) maxy = y;
|
||||||
|
}
|
||||||
|
if (++x == width) {
|
||||||
|
x = 0;
|
||||||
|
y++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// One more time through the data, this time we create the cropped image.
|
||||||
|
let cx = maxx - minx + 1;
|
||||||
|
let cy = maxy - miny + 1;
|
||||||
|
let buf = new Uint8Array(cx * cy);
|
||||||
|
let idx = 0;
|
||||||
|
for (y = miny; y <= maxy; y++) {
|
||||||
|
let i = (y * width + minx) * 4;
|
||||||
|
for (x = minx; x <= maxx; x++) {
|
||||||
|
// Alpha blend with white.
|
||||||
|
let a = rgba[i+3] / 255;
|
||||||
|
let r = rgba[i] * .3 * a + 255 * (1 - a);
|
||||||
|
let g = rgba[i+1] * .59 * a + 255 * (1 - a);
|
||||||
|
let b = rgba[i+2] * .11 * a + 255 * (1 - a);
|
||||||
|
let gray = r + g + b;
|
||||||
|
|
||||||
|
buf[idx++] = gray <= black ? 1 : 0;
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the monochrome image
|
||||||
|
buf.width = cx;
|
||||||
|
buf.height = cy;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cannot use btoa() with Uint8Arrays. Used only by the browser.
|
||||||
|
function u8tob64(a) {
|
||||||
|
let s = '';
|
||||||
|
let i = 0;
|
||||||
|
for (let l = a.length & 0xfffffff0; i < l; i += 16) {
|
||||||
|
s += String.fromCharCode(a[i],a[i+1],a[i+2],a[i+3],a[i+4],a[i+5],
|
||||||
|
a[i+6],a[i+7],a[i+8],a[i+9],a[i+10],
|
||||||
|
a[i+11],a[i+12],a[i+13],a[i+14],a[i+15]);
|
||||||
|
}
|
||||||
|
while (i < a.length) {
|
||||||
|
s += String.fromCharCode(a[i++]);
|
||||||
|
}
|
||||||
|
return btoa(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRC16 used by zebra
|
||||||
|
const crcTable = [
|
||||||
|
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5,
|
||||||
|
0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b,
|
||||||
|
0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210,
|
||||||
|
0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
|
||||||
|
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c,
|
||||||
|
0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401,
|
||||||
|
0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b,
|
||||||
|
0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
|
||||||
|
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6,
|
||||||
|
0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738,
|
||||||
|
0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5,
|
||||||
|
0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||||
|
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969,
|
||||||
|
0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96,
|
||||||
|
0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc,
|
||||||
|
0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
|
||||||
|
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03,
|
||||||
|
0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd,
|
||||||
|
0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6,
|
||||||
|
0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
|
||||||
|
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a,
|
||||||
|
0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb,
|
||||||
|
0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1,
|
||||||
|
0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||||
|
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c,
|
||||||
|
0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2,
|
||||||
|
0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb,
|
||||||
|
0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
|
||||||
|
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447,
|
||||||
|
0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8,
|
||||||
|
0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2,
|
||||||
|
0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
|
||||||
|
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9,
|
||||||
|
0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827,
|
||||||
|
0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c,
|
||||||
|
0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
|
||||||
|
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0,
|
||||||
|
0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d,
|
||||||
|
0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07,
|
||||||
|
0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
|
||||||
|
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba,
|
||||||
|
0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74,
|
||||||
|
0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
|
||||||
|
];
|
||||||
|
|
||||||
|
function crc16(s) {
|
||||||
|
// This is not an accumlating crc routine. Normally, the acc is intialized to
|
||||||
|
// 0xffff then inverted on each call. We just start with 0.
|
||||||
|
let crc = 0;
|
||||||
|
let j, i;
|
||||||
|
|
||||||
|
for (i = 0; i < s.length; i++) {
|
||||||
|
c = s.charCodeAt(i);
|
||||||
|
if (c > 255) {
|
||||||
|
throw new RangeError();
|
||||||
|
}
|
||||||
|
j = (c ^ (crc >> 8)) & 0xFF;
|
||||||
|
crc = crcTable[j] ^ (crc << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
crc = (crc & 0xffff).toString(16).toLowerCase();
|
||||||
|
return '0000'.substr(crc.length) + crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return zlib ? { rgbaToZ64, rgbaToACS } : { rgbaToZ64, rgbaToACS, imageToZ64, imageToACS };
|
||||||
|
}));
|
||||||
109
sticker-print.js
Normal file
109
sticker-print.js
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
^XA
|
||||||
|
^FX Template label 70x35mm
|
||||||
|
|
||||||
|
^FX --------- Agenzia Entrata logo
|
||||||
|
^FO20,85^GFA,2409,2409,33,,:::::I01LF8hL0806,I07LF8hK08812,I0MFChK0140B8,I0MFChI02384045E,001MFEhI0275802058,001MFEhI0AC6I0C44,001MFEhI0BFC004246,I0NFhH01768I0189,I0NFhI064K0C9,I07MF8hG0ACCK0E7C,J0MF8gI07V01B48K0422,L07JFCgI0FV02DDL0691,L0KFCgI0FW0EBI0800141,L0KFCgI0FV04FAI08001378,K01FI07EhG0FAL0102,K018J0Eh08F2I04I019,M03FE03J0FEF80FC03EFC03FFE03F8007F8P01E2L01414,L03IFEJ01IFC3FF03FFE03FFE07F801FFCP056O056,K01KFCI03IFC7FF83IF03FFE07F801FFEP01C5L01C42,K07LFI07C3F0F87C1F8F03FFE03F800E0FP0398M0648,K0MF800781E1E01E0F078387C0078J0FP03980041I0268,J03MFE00F00E1E01E0E07818F80078003FFP02B003C0E0017A,J07NF00F00E1IFE0E07801EI07800IFP03A08K0813C,J0OF80F00E1IFE0E07803CI07801IFP0D404J0100BC,I01OFC0F00E1IFE0E078078I07803E0FP0F4M0107C,I03OFC0701E1EJ0E0780F0E00780380FP0F8008L028,I03IF800IFE0783E0F00C1E0781F0E00780381FP0F0404001I039,I07FFCI01IF03FFE0IFE3F9FC3FFE0IFC3IFCO05060300200A91,I0IF8J0IF03FFE07FFE7F9FE3FFE0IFE3IFCN0130E0100201011,I0IFK07FF80FFE03FFC3F8FC3FFE0IFC1IFCO0B1B0200201511,001FFEK03FF8038E007CV038Q031BJ020141D,001FFCK01FFCI0EgS07B2M0D73,001FF8L0FFC001EgS03B1001CI0CCA,001QFC0FFEgS03F1003210104A,003QFC0FFCgR011F3J080124A,003QFE0FF8gS05FE01J0129,003QFE07CgT01EC0400180744,003QFEgX0FCM06D4,003QFEgW04ECM0DB,003QFEP0FU038O03ECM0BA8,003BPFEP0FU03CO01F402J01E9,003BPFEP0FU03CP0FAL017E,0033PFEP0FU03CP07AM058,0013PFEP0FU03CP03CM0E4,I03FCR01F3F003IF007E3F007F801IFC007F8I04DL034C,I03FCL07FF8003IFC07IF80FE7F83FFE03IFE01FFEI06F84I08089C,I03FCL03FF8003IFE0JF81JFC3IF03IFE03IF006241CI019804,00C3FFL03FFC003IFE07IF80JFC3IF01IFC07IF80021BCI01208,00C3FF8K03FFCI0FC1F00FJ01FE38I0F803CI07C0FC02605C004BC082,01E3FFEJ03IFCI0780F00FJ01FCK07803CI0F803C0320325IF001A,01E3IFE00JFEEI0700F00FJ01FJ07FF803CI0F003C0E881804870452,1FF1PFEFF00700F00FJ01EI01IF803CI0JFE03F400509818E4,7FF8PFE7F80700F00FJ01EI03IF803CI0JFE00780318604608,7FFCPFE7F80700F00FJ01EI07IF803CI0JFE014BDI26B433,IFE3OFC7FC0700F00FJ01EI0FC07803CI0FL049DA0D81404,JF1OFCFFC0700F00FJ01EI0F007803CI0FL021C0FEF0018,JF8OF1FFC0F00F00F01E01EI0F01F803C078FC01CI081E606FF6,JFE3MFC3FFC1FC1F80IFE0IFC0JFE03IF87IFEI078DC032,7JF0LFC1IF83FE3FC07FFE1IFE07JF03IF83IFEJ013001B8,7JFE1JF81JF83FE7FC03FFC1IFE03JF01IF01IFCJ03FI0F8,1KF8J03JFE01FC3FC01FE01IFC01FE7C007F8007FEL0E,,:::::^FS
|
||||||
|
|
||||||
|
^FX --------- QR Code
|
||||||
|
^FO300,5
|
||||||
|
^BQN,2,5,M,7
|
||||||
|
^FDQA,
|
||||||
|
https://www.zebra.com/content/dam/zebra_new_ia/en-us/manuals/printers/common/programming/zpl-zbi2-pm-en.pdf
|
||||||
|
^FS
|
||||||
|
|
||||||
|
^FX --------- Company name
|
||||||
|
^CF0,30
|
||||||
|
^FO20,15^FD
|
||||||
|
Vandelay Industries
|
||||||
|
^FS
|
||||||
|
|
||||||
|
^FX --------- External MSID
|
||||||
|
^CFA,20
|
||||||
|
^FO20,55^FD
|
||||||
|
ACS: EXT-MSID
|
||||||
|
^FS
|
||||||
|
|
||||||
|
^FX --------- Machine Model
|
||||||
|
^CFA,20
|
||||||
|
^FO20,180^FD
|
||||||
|
Model XL-123
|
||||||
|
^FS
|
||||||
|
|
||||||
|
^FX --------- MSID label
|
||||||
|
^CFA,20
|
||||||
|
^FO20,210^FD
|
||||||
|
ID ADE:
|
||||||
|
^FS
|
||||||
|
|
||||||
|
^FX --------- MSID, max 22 chars
|
||||||
|
^CFA,20
|
||||||
|
^FO20,240^FD
|
||||||
|
1234567890123456789012
|
||||||
|
^FS
|
||||||
|
|
||||||
|
^XZ
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TEMPLATES = {
|
||||||
|
"sticker-70x35": {
|
||||||
|
name: "Sticker 70x35mm",
|
||||||
|
template: `
|
||||||
|
^XA
|
||||||
|
^FX Template label 70x35mm
|
||||||
|
|
||||||
|
^FX --------- Agenzia Entrata logo
|
||||||
|
^FO20,85^GFA,2409,2409,33,,:::::I01LF8hL0806,I07LF8hK08812,I0MFChK0140B8,I0MFChI02384045E,001MFEhI0275802058,001MFEhI0AC6I0C44,001MFEhI0BFC004246,I0NFhH01768I0189,I0NFhI064K0C9,I07MF8hG0ACCK0E7C,J0MF8gI07V01B48K0422,L07JFCgI0FV02DDL0691,L0KFCgI0FW0EBI0800141,L0KFCgI0FV04FAI08001378,K01FI07EhG0FAL0102,K018J0Eh08F2I04I019,M03FE03J0FEF80FC03EFC03FFE03F8007F8P01E2L01414,L03IFEJ01IFC3FF03FFE03FFE07F801FFCP056O056,K01KFCI03IFC7FF83IF03FFE07F801FFEP01C5L01C42,K07LFI07C3F0F87C1F8F03FFE03F800E0FP0398M0648,K0MF800781E1E01E0F078387C0078J0FP03980041I0268,J03MFE00F00E1E01E0E07818F80078003FFP02B003C0E0017A,J07NF00F00E1IFE0E07801EI07800IFP03A08K0813C,J0OF80F00E1IFE0E07803CI07801IFP0D404J0100BC,I01OFC0F00E1IFE0E078078I07803E0FP0F4M0107C,I03OFC0701E1EJ0E0780F0E00780380FP0F8008L028,I03IF800IFE0783E0F00C1E0781F0E00780381FP0F0404001I039,I07FFCI01IF03FFE0IFE3F9FC3FFE0IFC3IFCO05060300200A91,I0IF8J0IF03FFE07FFE7F9FE3FFE0IFE3IFCN0130E0100201011,I0IFK07FF80FFE03FFC3F8FC3FFE0IFC1IFCO0B1B0200201511,001FFEK03FF8038E007CV038Q031BJ020141D,001FFCK01FFCI0EgS07B2M0D73,001FF8L0FFC001EgS03B1001CI0CCA,001QFC0FFEgS03F1003210104A,003QFC0FFCgR011F3J080124A,003QFE0FF8gS05FE01J0129,003QFE07CgT01EC0400180744,003QFEgX0FCM06D4,003QFEgW04ECM0DB,003QFEP0FU038O03ECM0BA8,003BPFEP0FU03CO01F402J01E9,003BPFEP0FU03CP0FAL017E,0033PFEP0FU03CP07AM058,0013PFEP0FU03CP03CM0E4,I03FCR01F3F003IF007E3F007F801IFC007F8I04DL034C,I03FCL07FF8003IFC07IF80FE7F83FFE03IFE01FFEI06F84I08089C,I03FCL03FF8003IFE0JF81JFC3IF03IFE03IF006241CI019804,00C3FFL03FFC003IFE07IF80JFC3IF01IFC07IF80021BCI01208,00C3FF8K03FFCI0FC1F00FJ01FE38I0F803CI07C0FC02605C004BC082,01E3FFEJ03IFCI0780F00FJ01FCK07803CI0F803C0320325IF001A,01E3IFE00JFEEI0700F00FJ01FJ07FF803CI0F003C0E881804870452,1FF1PFEFF00700F00FJ01EI01IF803CI0JFE03F400509818E4,7FF8PFE7F80700F00FJ01EI03IF803CI0JFE00780318604608,7FFCPFE7F80700F00FJ01EI07IF803CI0JFE014BDI26B433,IFE3OFC7FC0700F00FJ01EI0FC07803CI0FL049DA0D81404,JF1OFCFFC0700F00FJ01EI0F007803CI0FL021C0FEF0018,JF8OF1FFC0F00F00F01E01EI0F01F803C078FC01CI081E606FF6,JFE3MFC3FFC1FC1F80IFE0IFC0JFE03IF87IFEI078DC032,7JF0LFC1IF83FE3FC07FFE1IFE07JF03IF83IFEJ013001B8,7JFE1JF81JF83FE7FC03FFC1IFE03JF01IF01IFCJ03FI0F8,1KF8J03JFE01FC3FC01FE01IFC01FE7C007F8007FEL0E,,:::::^FS
|
||||||
|
|
||||||
|
^FX --------- QR Code
|
||||||
|
^FO300,5
|
||||||
|
^BQN,2,5,M,7
|
||||||
|
^FDQA,{qrCodeUrl}^FS
|
||||||
|
|
||||||
|
^FX --------- Company name
|
||||||
|
^CF0,30
|
||||||
|
^FO20,15^FD{companyName}^FS
|
||||||
|
|
||||||
|
^FX --------- External MSID
|
||||||
|
^CFA,20
|
||||||
|
^FO20,55^FDACS: {externalMasterSystemId}^FS
|
||||||
|
|
||||||
|
^FX --------- Machine Model
|
||||||
|
^CFA,20
|
||||||
|
^FO20,180^FD{machineModel}^FS
|
||||||
|
|
||||||
|
^FX --------- MSID label
|
||||||
|
^CFA,20
|
||||||
|
^FO20,210^FDID ADE:^FS
|
||||||
|
|
||||||
|
^FX --------- MSID, max 22 chars
|
||||||
|
^CFA,20
|
||||||
|
^FO20,240^FD{masterSystemId}^FS
|
||||||
|
|
||||||
|
^XZ
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function createInternalStickerZPL(template, content) {
|
||||||
|
return template
|
||||||
|
.replace("{qrCodeUrl}", content.qrCodeUrl)
|
||||||
|
.replace("{companyName}", content.companyName)
|
||||||
|
.replace("{machineModel}", content.machineModel)
|
||||||
|
.replace("{masterSystemId}", content.masterSystemId)
|
||||||
|
.replace("{externalMasterSystemId}", content.externalMasterSystemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function imageToGRF() {}
|
||||||
|
|
||||||
|
const internalTicketZPL = createInternalStickerZPL(TEMPLATES["sticker-70x35"].template, {
|
||||||
|
qrCodeUrl: "https://en.wikipedia.org/wiki/Zebra_Programming_Language",
|
||||||
|
companyName: "Vandelay Industries",
|
||||||
|
machineModel: "Model XL-123",
|
||||||
|
masterSystemId: "master-system-id",
|
||||||
|
externalMasterSystemId: "ext-msid",
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(internalTicketZPL);
|
||||||
Reference in New Issue
Block a user