# 82label documentation (v0.2.0) > Design label templates on 82label.app and print them from your site: roll labels, Formtec A4 sheets, courier waybills, barcodes and QR codes, mm-accurate browser printing and silent printing through the Windows print agent. Summary: https://82label.app/llms.txt --- # Getting started > Make a template and print your first label from your site. Source: https://82label.app/en/docs/getting-started/ 82label is a label template format (JSON) and a library that prints it. Draw the template in millimetres in the [designer](https://82label.app/en/designer/), then print from your site with one call: `Label82.print(template, data)`. The library has no dependencies and is MIT licensed. ```js const template = { v: 1, name: 'Product barcode 50×30', paper: { kind: 'roll', dpi: 203 }, label: { width: 50, height: 30 }, elements: [ { type: 'text', x: 2.5, y: 2, w: 45, h: 6, text: '{{name}}', size: 9, bold: true, fit: 'shrink' }, { type: 'text', x: 2.5, y: 8, w: 45, h: 4, text: '{{price}}', size: 8 }, { type: 'barcode', x: 2.5, y: 13, w: 45, h: 15, format: 'code128', value: '{{sku}}' }, ], }; const rows = [ { name: 'Wireless earbuds Pro', price: '₩39,000', sku: 'EP-2026-0412' }, { name: 'Fast charging cable 1m', price: '₩9,900', sku: 'CB-1M-0007' }, ]; Label82.print(template, rows); ``` The label above is drawn by the real library. **Print this example** opens the browser print dialog on 50×30 mm paper. Two data rows print two labels. ## 1. Make a template 1. Open the [designer](https://82label.app/en/designer/) and pick the paper: a roll size (say 50×30), a [Formtec sheet](https://82label.app/en/templates/) or a courier waybill. 2. Place text, barcodes, QR codes, images, lines and boxes. Values that change are written in double braces, like `{{name}}` ([fields](https://82label.app/en/docs/fields/)). 3. In **Get code**, copy code with the template JSON inside, or sign in, **Save**, and load it by id (`lbl_…`). ## 2. Add it to your site A page without a build step needs one script tag. It defines the global `Label82` and registers the `` tag. ```html ``` With npm: ```sh npm i @82label/core ``` ```js import { print } from '@82label/core'; import template from './labels/product.json'; // template JSON from the designer async function printLabels(orders) { const rows = orders.map((o) => ({ name: o.productName, price: o.priceText, sku: o.sku })); const result = await print(template, rows); // result.via: 'agent' (print agent) or 'browser' return result; } ``` ## 3. How it prints By default (`mode: 'auto'`) `print()` sends the labels straight to the label printer when the [82label print agent](https://82label.app/en/docs/agent/) runs on the PC, with no dialog, and opens the browser print dialog otherwise. Both print the same picture at the same size in millimetres. > In the browser dialog keep **scale at 100% (actual size)**, **no margins**, and paper at the label size (roll) or A4 (sheets). 82label sets the `@page` size, so the defaults usually fit. ## Next - [Template JSON](https://82label.app/en/docs/template/): element types and coordinates - [Roll labels](https://82label.app/en/docs/roll/) · [Formtec sheets](https://82label.app/en/docs/sheets/) · [Courier waybills](https://82label.app/en/docs/couriers/) - [Barcodes](https://82label.app/en/docs/barcodes/) · [QR codes](https://82label.app/en/docs/qr/) - [Printing](https://82label.app/en/docs/printing/) · [Installing and linking the print agent](https://82label.app/en/docs/agent/) --- # CDN > Script tags: pinned URLs, the files, and the global Label82. Source: https://82label.app/en/docs/cdn/ 82label.app serves the files itself. Versioned URLs never change, so browsers cache them for a year. Use them on production sites. | File | What it is | | --- | --- | | `/cdn/v0.2.0/82label.min.js` | For a script tag. Defines the global `Label82` and registers ``. | | `/cdn/v0.2.0/82label.esm.js` | An ES module to import from ` ``` ## ES module ```html ``` ## What the global Label82 holds Every export of the npm package: `print`, `printers`, `agentStatus`, `loadTemplate`, `renderToSVG`, `barcodeSVG`, `qrSVG`, `layoutPages`, `FORMTEC`, `ROLL_SIZES`, `presetTemplate`, `defineLabelElement`, `VERSION` and the rest. The full list is in the [API reference](https://82label.app/en/docs/api/). ## Template JSON Schema The JSON Schema of the template format is at [https://82label.app/schema/template.json](https://82label.app/schema/template.json) (pinned: `/schema/v0.2.0/template.json`, npm: `@82label/core/schema.json`). Point your editor at it for completion and checks. ```json { "$schema": "https://82label.app/schema/template.json", "v": 1, "name": "Product barcode", "paper": { "kind": "roll" }, "label": { "width": 50, "height": 30 }, "elements": [] } ``` > You may download the files and host them yourself; the MIT license allows changes and redistribution. Usage reporting sends the domain and version once a day and never runs on intranet hosts; `` turns it off. --- # npm > Installing @82label/core, imports, bundlers and TypeScript. Source: https://82label.app/en/docs/npm/ ```sh npm i @82label/core ``` One ES module with no dependencies. TypeScript types ship with it (`Template`, `Row`, `PrintOptions`, `PrintResult` and more). ```js import { print } from '@82label/core'; import template from './labels/product.json'; // template JSON from the designer async function printLabels(orders) { const rows = orders.map((o) => ({ name: o.productName, price: o.priceText, sku: o.sku })); const result = await print(template, rows); // result.via: 'agent' (print agent) or 'browser' return result; } ``` ## Keeping templates as files Put the file from the designer's **Download JSON** in your project and import it. The template is versioned with your code and works without the 82label server. ```ts import type { Template } from '@82label/core'; import raw from './labels/product.json'; const template = raw as Template; ``` ## With the web component ```js import { defineLabelElement } from '@82label/core'; import '@82label/core/styles.css'; defineLabelElement(); // registers ``` ## Rendering on a server `renderToSVG` builds an SVG string without a DOM: label previews in Node or Cloudflare Workers, or in email. Printing (`print`) runs in the browser only. ```js import { renderToSVG } from '@82label/core'; const svg = renderToSVG(template, { name: 'Wireless earbuds Pro', price: '₩39,000', sku: 'EP-2026-0412' }); ``` > Dedicated React and Vue packages are coming. Until then use `@82label/core` directly ([React and Vue examples](https://82label.app/en/docs/frameworks/)). --- # Template JSON > Paper, label size and the element list. Every length is in millimetres. Source: https://82label.app/en/docs/template/ A template is one JSON object. The designer, the library and the print agent all use this format and nothing else. Positions and sizes are in **millimetres** from the label's top left corner; only font sizes are in **points**. Elements are drawn in order (later ones on top). ```js const template = { v: 1, name: 'Shipping label', paper: { kind: 'roll', dpi: 203 }, label: { width: 60, height: 40 }, elements: [ { type: 'rect', x: 1, y: 1, w: 58, h: 38, stroke: 0.3, radius: 2 }, { type: 'text', x: 4, y: 3, w: 36, h: 6, text: '{{orderNo}}', size: 9, bold: true }, { type: 'text', x: 4, y: 10, w: 36, h: 12, text: '{{items}}', size: 8, fit: 'wrap' }, { type: 'line', x1: 4, y1: 24, x2: 56, y2: 24, width: 0.3 }, { type: 'text', x: 4, y: 26, w: 36, h: 10, text: '{{customer}}', size: 11, bold: true, valign: 'middle', fit: 'shrink' }, { type: 'qr', x: 41, y: 4, size: 16, value: '{{orderUrl}}' }, ], fields: [ { name: 'orderNo', label: 'Order number', example: 'A-20261022-0012' }, { name: 'items', label: 'Items', example: 'Wireless earbuds Pro and 2 more' }, { name: 'customer', label: 'Customer', example: 'Haneul Kim' }, { name: 'orderUrl', label: 'URL', example: 'https://shop.example.com/o/12' }, ], }; Label82.print(template); ``` ## Top-level keys | Key | Meaning | | --- | --- | | `v` | Format version: `1`. | | `name` | Template name (shown in your template list). | | `paper` | `{ kind: 'roll' }` or `{ kind: 'sheet', … }`. See [roll labels](https://82label.app/en/docs/roll/) and [Formtec sheets](https://82label.app/en/docs/sheets/). | | `label` | One label (one slot on a sheet): `width`, `height`, corner `radius`. | | `elements` | The elements, table below. | | `fields` | Optional list of `{ name, label, example }` for the designer preview and example values. | ## Elements | type | Required | Optional | | --- | --- | --- | | `text` | `x y w h text size` | `bold`, `align` (left, center, right), `valign` (top, middle, bottom), `fit` (shrink, wrap, clip), `font`, `rotate` (0, 90, 180, 270). `size` is in points. | | `barcode` | `value` | `x y w h` (left out: centred, 80% wide), `format` (default code128), `showText`, `codeSet`, `rotate` | | `qr` | `value` | `x y size` (left out: a centred square), `ecc` (L, M, Q, H; default M) | | `image` | `x y w h src` | `src` is a data: URL, an https URL or a `{{field}}`. `fit` (contain, cover) | | `line` | `x1 y1 x2 y2 width` | Line `width` in mm | | `rect` | `x y w h` | `stroke` (border width in mm, default 0.3, 0 for none), `fill` (none, black), `radius` | ## Black and white only Most label printers are thermal (black only), so there are no colors. For white text on black, use an image instead of text over a `fill: 'black'` box. ## Pre-printed frames (layer: 'frame') Mark parts that are already printed on the stock, such as box lines and captions like "To", with `layer: 'frame'`. They are drawn on screen and on blank paper; on pre-printed stock, `print(t, rows, { frames: false })` leaves them out and prints only the values. > JSON Schema of the format: [/schema/template.json](https://82label.app/schema/template.json). When saving, the server (82label.app) checks only `v`, the paper kind, the label size and the element count (at most 2,000). --- # Fields ({{ }}) > Write changing values as {{name}}; each data row prints one label. Source: https://82label.app/en/docs/fields/ Put `{{name}}` anywhere in text, barcode values, QR values and image sources. At print time it is replaced with the value of the same name in your data. Mix it with fixed text freely: `'Order {{orderNo}} · {{qty}} pcs'`. ```js const template = { v: 1, name: 'Price tag', paper: { kind: 'roll' }, label: { width: 40, height: 30 }, elements: [ { type: 'text', x: 2, y: 2, w: 36, h: 8, text: '{{name}}', size: 9, bold: true, fit: 'wrap' }, { type: 'text', x: 2, y: 10, w: 36, h: 7, text: '₩{{price}}', size: 14, bold: true, align: 'right' }, { type: 'barcode', x: 2, y: 18, w: 36, h: 10, format: 'ean13', value: '{{ean}}' }, ], }; const rows = [ { name: 'Organic milk 900ml', price: '3,480', ean: '880123456789' }, { name: 'Whole wheat bread', price: '4,200', ean: '880123456790' }, ]; Label82.print(template, rows); ``` ## Data - `rows` in `print(template, rows)` is one object or an array. Each array item prints one label. - Values are strings or numbers. `null`, `undefined` and missing names print as empty. - Values go in as text only (never parsed as HTML or SVG), so customer-typed addresses are safe to pass as they are. - The same label several times: `{ copies: 3 }` prints three of each row. A different count per row: `{ qtyField: 'qty' }` (that column's number). ## Field list and example values The designer collects the `{{ }}` in a template into a field table and stores example values in `fields`. Previews without data use them. ```js import { extractFields, exampleRow, fill } from '@82label/core'; extractFields(template); // ['name', 'price', 'ean'] exampleRow(template); // { name: '…', price: '…', ean: '…' } (fields[].example, else the name) fill('{{name}} ₩{{price}}', { name: 'Milk', price: '3,480' }); // 'Milk ₩3,480' ``` Formats such as `{{price|won}}` and `{{date|date:YYYY.MM.DD}}`, serial numbers `{{seq:0001}}` and dates `{{today+365}}` are in [Value formats, serial numbers, dates](https://82label.app/en/docs/formats/). To print from a spreadsheet without code, see [Bulk printing](https://82label.app/en/docs/bulk/). > Field names may be Korean (`{{상품명}}`), but ASCII names are easier to handle in code. --- # Value formats, serial numbers, dates > {{price|won}}, {{date|date:YYYY.MM.DD}}, {{seq:0001}}, {{today+365}}: change how values print without touching the data. Source: https://82label.app/en/docs/formats/ Add `|format` after a field name to change how its value prints. The data stays as it is. In the designer, pick one from the "Format" list in the field table or in text and barcode properties. | Write | Value | Prints | | --- | --- | --- | | `{{price\|money}}` | 12900 | 12,900 | | `{{price\|won}}` | 12900 | 12,900원 | | `{{date\|date}}` | 20261022, 2026-10-22, 45950 (Excel date) | 2026-10-22 | | `{{date\|date:YYYY.MM.DD}}` | 2026-10-22 | 2026.10.22 | | `{{date\|date:D/M/YY}}` | 2026-10-22 | 22/10/26 | | `{{no\|pad:6}}` | 42 | 000042 | | `{{code\|upper}}` · `\|lower` · `\|trim` | ab-1 | AB-1 · ab-1 · ab-1 | Date tokens: `YYYY` `YY` `MM` `M` `DD` `D` `HH` `mm` `ss`. An unknown format name leaves the value unchanged. ## Serial numbers and today - `{{seq}}`: the label's position in this print (1, 2, 3…). With copies per row, every printed label counts. - `{{seq:0001}}`: the digits give the start and the width. `0001` starts at 0001 with 4 digits, `0100` at 0100. - Change the start with `print(t, rows, { serialStart: 501 })`, the same as "First number" on the bulk print page. - `{{today}}`, `{{today+365}}` (best before), `{{today-1}}`: the print date in your time zone; `|date:FMT` works too. - `{{now}}`: the print time as `YYYY-MM-DD HH:mm`. ```js const template = { v: 1, name: 'Asset tag', paper: { kind: 'roll' }, label: { width: 50, height: 25 }, elements: [ { type: 'text', x: 2, y: 2, w: 30, h: 5, text: '{{name}}', size: 8, bold: true }, { type: 'text', x: 30, y: 2, w: 18, h: 5, text: '{{price|money}}', size: 8, align: 'right' }, { type: 'barcode', x: 2, y: 8, w: 46, h: 15, format: 'code128', value: 'A-{{seq:0001}}', showText: true }, ], }; const rows = [ { name: 'Laptop', price: 1290000 }, { name: 'Monitor', price: 329000 }, ]; Label82.print(template, rows); ``` > `extractFields(t)` returns data fields only: formats are stripped and `seq`, `today` and `now` are left out. --- # Roll labels > Thermal label printer rolls: size, dpi and direction. Source: https://82label.app/en/docs/roll/ On a roll each label is one printed page: ten data rows print ten labels in a row. Any size works; just give it in millimetres. ```js { v: 1, name: 'Product label', paper: { kind: 'roll', dpi: 203, gap: 2 }, label: { width: 50, height: 30 }, elements: [ /* … */ ], } ``` | Key | Meaning | | --- | --- | | `dpi` | Printer resolution, 203, 300 or 600. Barcode bars are snapped to whole dots so they scan. If unsure, 203 (most thermal label printers). | | `gap` | Gap between labels in mm. Informational; the printer driver handles the real gap. | | `direction` | 0, 90, 180 or 270, to match how labels come out of the printer. 90 and 270 swap the page width and height. | ## Common sizes The designer's quick picks (`ROLL_SIZES`). Type any other size by hand. | Width × height (mm) | Typical use | | --- | --- | | `30×10` | | | `30×15` | | | `30×20` | | | `35×15` | | | `40×20` | | | `40×30` | | | `45×25` | | | `45×50` | | | `50×30` | | | `50×50` | | | `50×80` | | | `60×40` | | | `60×60` | | | `70×30` | | | `70×50` | | | `70×60` | | | `80×40` | | | `80×50` | | | `80×120` | | | `90×60` | | | `90×120` | | | `100×30` | | | `100×50` | | | `100×80` | | | `100×100` | | | `100×150` | Courier waybill | | `102×152` | 4×6 inch waybill (overseas) | ```js import { presetTemplate, ROLL_SIZES } from '@82label/core'; const t = presetTemplate('roll', 50, 30); // an empty 50×30 template t.elements.push({ type: 'barcode', value: '{{sku}}' }); // no position: centred ``` ## Printer setup - Set the driver's paper size to the label size. The print agent sets it for every job. - Calibrate label sensing (gap or black mark) once on the printer, usually by holding the feed button or from the driver. - With browser printing, pick the label printer and paper in the dialog and keep the scale at 100%. --- # Formtec sheets > 52 Formtec A4 sheet labels: grid, size, margins and the start slot (startAt). Source: https://82label.app/en/docs/sheets/ 82label knows the slot layout of 52 Formtec A4 label sheets, checked against Formtec's official templates, each with its source. Sheets fill from the top left slot to the right, row by row, and continue on a new page when the slots run out. ```js const template = { v: 1, name: 'Address label', paper: Label82.sheetPaper('formtec-3108'), // LS-3108, 2 across × 7 down label: { width: 99.1, height: 38.1 }, elements: [ { type: 'text', x: 6, y: 5, w: 86, h: 14, text: '{{address}}', size: 10, fit: 'wrap' }, { type: 'text', x: 6, y: 22, w: 86, h: 10, text: '{{name}}', size: 14, bold: true }, ], }; const rows = [ { address: '12F, 152 Teheran-ro, Gangnam-gu, Seoul', name: 'Haneul Kim' }, { address: '3F, 79 Centum jungang-ro, Haeundae-gu, Busan', name: 'Doyun Lee' }, { address: '201, Bldg 5, 99 Daehak-ro, Yuseong-gu, Daejeon', name: 'Seoyeon Park' }, ]; Label82.print(template, rows, { startAt: 3 }); ``` This example uses `startAt: 3`, so filling begins at the third slot: handy for a sheet that already has some labels used. ```js Label82.print(template, rows, { startAt: 3 }); ``` ## A template from a sheet ```js import { presetTemplate, sheetPaper } from '@82label/core'; const t = presetTemplate('formtec', 'LS-3114'); // 'formtec-3114' and '3114' work too // or just the paper: paper: sheetPaper('formtec-3114') ``` ## Your own sheet For a sheet not on the list, give the grid and margins. `pitchX` and `pitchY` run from one slot's left (top) edge to the next one's (label size plus gap). ```js paper: { kind: 'sheet', page: 'A4', cols: 3, rows: 8, marginTop: 12.5, marginLeft: 6.5, pitchX: 66.5, pitchY: 34, } ``` ## Included sheets (52) "Unconfirmed" means at least one value could not be checked against an official template; test on plain paper first. To open one in the designer, use [Templates](https://82label.app/en/templates/). | Code | Name | Grid | Label (mm) | Values | | --- | --- | --- | --- | --- | | `LS-3105` | 주소용 라벨 21칸 | 3×7 | 63.5×38.1 | checked | | `LS-3106` | 주소용 라벨 24칸 | 3×8 | 64×34 | checked | | `LS-3107` | 주소용 라벨 16칸 | 2×8 | 99.1×33.9 | checked | | `LS-3108` | 주소용 라벨 14칸 | 2×7 | 99.1×38.1 | checked | | `LS-3109` | 주소용 라벨 18칸 | 2×9 | 100×30 | checked | | `LS-3212` | 주소용 라벨 12칸 | 2×6 | 100×45 | checked | | `LS-3218` | 주소용 라벨 18칸 | 3×6 | 63.5×45 | checked | | `LS-3219` | 주소용 라벨 22칸 | 2×11 | 100×24.5 | checked | | `LS-3620` | 주소용 라벨 6칸 | 1×6 | 119.3×42.8 | checked | | `LS-3667` | 주소용 라벨 12칸 | 2×6 | 76×44.4 | checked | | `LS-3112` | 물류관리용 라벨 12칸 | 3×4 | 63.5×70 | checked | | `LS-3114` | 물류관리용 라벨 8칸 | 2×4 | 99.1×67.7 | checked | | `LS-3116` | 물류관리용 라벨 6칸 | 2×3 | 99.1×93.1 | checked | | `LS-3118` | 물류관리용 라벨 4칸 | 2×2 | 99.1×140 | checked | | `LS-3120` | 물류관리용 라벨 2칸 | 1×2 | 200×140 | checked | | `LS-3203` | 물류관리용 라벨 3칸 | 1×3 | 200×93 | checked | | `LS-3660` | 물류관리용 라벨 8칸 | 1×8 | 124×30 | checked | | `LS-3100` | 바코드용 라벨 65칸 | 5×13 | 38.1×21.2 | checked | | `LS-3101` | 바코드용 라벨 60칸 | 5×12 | 38.1×19.2 | checked | | `LS-3102` | 바코드용 라벨 40칸 | 4×10 | 47×26.9 | checked | | `LS-3104` | 바코드용 라벨 27칸 | 3×9 | 62.7×30.1 | checked | | `LS-3110` | 분류표기용 라벨 54칸 | 6×9 | 26×29.5 | checked | | `LS-3136` | 분류표기용 라벨 136칸 | 4×34 | 46×8 | checked | | `LS-3142` | 분류표기용 라벨 42칸 | 2×21 | 72×13 | checked | | `LS-3145` | 분류표기용 라벨 145칸 | 5×29 | 30×9 | checked | | `LS-3154` | 분류표기용 라벨 54칸 | 3×18 | 62.7×15 | checked | | `LS-3181` | 분류표기용 라벨 81칸 | 3×27 | 53×10 | checked | | `LS-3189` | 분류표기용 라벨 189칸 | 7×27 | 25.4×10 | checked | | `LS-3216` | 분류표기용 라벨 216칸 | 8×27 | 20×10 | checked | | `LS-3622` | 분류표기용 라벨 96칸 | 6×16 | 30×14 | checked | | `LS-3623` | 분류표기용 라벨 84칸 | 4×21 | 46×11 | checked | | `LS-3676` | 분류표기용 라벨 76칸 | 4×19 | 40×14 | checked | | `LS-3677` | 분류표기용 라벨 78칸 | 6×13 | 30×21 | checked | | `LS-3510` | 분류표기용 라벨 10칸 | 2×5 | 88.9×52 | checked | | `LS-3615` | 분류표기용 라벨 10칸 | 2×5 | 69.8×52 | checked | | `LS-3610` | 분류표기·QR 라벨 8칸 | 2×4 | 69.8×69.8 | checked | | `LS-3111` | 분류표기·QR 라벨 104칸 | 8×13 | 20×20 | checked | | `LS-3990` | QR 전용 라벨 28칸 | 4×7 | 40×40 | checked | | `LS-3639` | 원형 라벨 Ø38 20칸 | 4×5 | 38×38 | checked | | `LS-3640` | 원형 라벨 Ø63 12칸 | 3×4 | 63×63 | checked | | `LS-3641` | 원형 라벨 Ø20 108칸 | 9×12 | 20×20 | checked | | `CL-3642P` | CD/DVD 라벨 2칸 | 1×2 | 117.8×117.8 | **unconfirmed** | | `LS-3621` | 화일인덱스 라벨 36칸 | 2×18 | 95×14 | checked | | `LS-3624` | 화일인덱스 라벨 9칸 | 1×9 | 150×28 | checked | | `LS-3625` | 화일인덱스 라벨 7칸 | 1×7 | 160×38 | checked | | `LS-3626` | 화일인덱스 라벨 4칸 | 1×4 | 160×60 | checked | | `LS-3627` | 화일인덱스(등) 라벨 8칸 | 4×2 | 48×130 | checked | | `LS-3628` | 화일인덱스(등) 라벨 30칸 | 15×2 | 12×130 | checked | | `LS-3661` | 화일인덱스 라벨 48칸 | 1×48 | 114×5.5 | checked | | `LS-3662` | 화일인덱스 라벨 13칸 | 1×13 | 146×17.8 | checked | | `LS-2308B` | 이름표 라벨 8칸 | 2×4 | 90×60 | checked | | `LS-3130` | 전지 라벨 1칸 (분류표기용) | 1×1 | 210×297 | checked | > Turn off "fit to page" and print at actual size (100%). Home inkjets may shift the top or bottom by a few millimetres; if so, adjust `marginTop` and `marginLeft` in 0.5 mm steps rather than moving every element. --- # Courier waybills > Courier waybill templates (subscribers) with tracking number and sort code fields. Source: https://82label.app/en/docs/couriers/ Templates fitted to the waybill paper of the major Korean couriers: CJ Logistics, Hanjin, Lotte, Korea Post, Logen, Kyungdong, Daesin, Ilyang, Hapdong and a common vertical 100×150. The receiver, sender, addresses, phones, tracking barcode, sort code, items and memo are laid out, and every value is a `{{field}}`. > Courier templates are for **subscribers**. Free accounts see names and sizes in [Templates](https://82label.app/en/templates/). See [Pricing](https://82label.app/en/pricing/). ## What 82label does and does not do - It does: print the tracking number, sort code and addresses you got from the courier in the right places. - It does not: issue tracking numbers, book pickups or talk to courier APIs. That needs a courier contract and their API. - No courier trademarks or logos (names as text only). ## Loading one Click a courier card in [Templates](https://82label.app/en/templates/) to open it in the designer. Save your edits to make it your template and load that by id. Code can also fetch a preset directly (only in a browser signed in to 82label.app). ```js import { loadCourierTemplate, print } from '@82label/core'; const t = await loadCourierTemplate('cj-standard'); await print(t, [{ trackingNo: '609123456781', receiverNameMasked: 'H*** Kim', receiverAddress: '152 Teheran-ro, Gangnam-gu, Seoul', /* … */ }], { frames: false }); ``` ## Fitting the waybill stock - Sizes and box positions follow public material and real stock, but vary a little by courier and contract. Print one on the supplied stock and compare first. - On stock with the boxes and captions pre-printed, print values only with `frames: false` ([layer: 'frame'](https://82label.app/en/docs/template/)). - The courier decides the barcode format of tracking numbers. Couriers using ITF (an even number of digits), such as Hanjin and Lotte, need `format: 'itf'` ([Barcodes](https://82label.app/en/docs/barcodes/)). --- # Barcodes > Code128, EAN-13 and 8, UPC-A, Code39, ITF, ITF-14: check digits and scannable sizes. Source: https://82label.app/en/docs/barcodes/ A barcode is one element. The value alone is enough: without a position it goes in the middle of the label, 80% wide. ```js { type: 'barcode', value: '{{sku}}' } // Code 128, centred { type: 'barcode', x: 3, y: 14, w: 44, h: 13, format: 'ean13', value: '{{ean}}', showText: true } ``` ```js Label82.barcodeSVG('880123456789', { format: 'ean13', height: 18 }) ``` ## Formats | format | Value | Used for | | --- | --- | --- | | `code128` | Digits, letters, symbols, any length | The default. SKUs, order numbers, waybills. Long digit runs switch to the dense C set automatically. | | `ean13` | 12 digits (+1 check) | Retail product barcodes. Give 12 digits and the check digit is added; give 13 and it is verified. | | `ean8` | 7 digits (+1 check) | Small products. | | `upca` | 11 digits (+1 check) | North American retail. | | `code39` | Upper-case letters, digits, `- . space $ / + %` | Older in-house systems, asset tags. | | `itf14` | 13 digits (+1 check) | Shipping cartons (GTIN-14). | | `itf` | An even number of digits | Interleaved 2 of 5 with no check digit. Hanjin and Lotte tracking numbers. | ## Invalid values A value that does not fit the format (wrong EAN length, lower case in Code39 …) is drawn as a crossed box and reported in the print result's `errors`. To check ahead, call `encodeBarcode`. ```js import { encodeBarcode, eanCheckDigit, BarcodeError } from '@82label/core'; eanCheckDigit('880123456789'); // 3 → 8801234567893 try { encodeBarcode('ean13', '88012345'); } catch (e) { if (e instanceof BarcodeError) console.log(e.message); } const r = await Label82.print(template, rows); r.errors; // [{ element: 2, code: 'barcode_invalid', message: '…' }] ``` ## Sizes that scan - The narrow bar (module) is snapped to whole printer dots (`paper.dpi`). At 203 dpi a dot is 0.125 mm. - Keep a quiet zone of ten modules on each side; do not run the barcode to the label edge. - Make it at least 10 mm tall; 15 mm or more for barcodes read from a distance, like waybills. - A long value in a narrow box pushes modules below 0.19 mm and scanners struggle. The designer warns you. ## A barcode on its own Need only the barcode picture? `barcodeSVG` returns an SVG string for a screen or a PDF. ```js Label82.barcodeSVG('EP-2026-0412', { height: 15, showText: true }); Label82.barcodeSVG('880123456789', { format: 'ean13', moduleWidth: 0.33 }); ``` --- # QR codes > The QR element, error correction levels, size and length. Source: https://82label.app/en/docs/qr/ A QR code is a square element. `size` is its side in mm; without a position it goes in the middle. Use it for long values such as order lookup URLs, product pages or stock location codes. ```js { type: 'qr', x: 36, y: 4, size: 20, value: 'https://shop.example.com/o/{{orderNo}}', ecc: 'M' } ``` ```js const template = { v: 1, name: 'Stock location', paper: { kind: 'roll', dpi: 203 }, label: { width: 60, height: 30 }, elements: [ { type: 'qr', x: 3, y: 3, size: 24, value: 'LOC:{{loc}}', ecc: 'Q' }, { type: 'text', x: 30, y: 5, w: 28, h: 10, text: '{{loc}}', size: 16, bold: true, fit: 'shrink' }, { type: 'text', x: 30, y: 16, w: 28, h: 10, text: '{{desc}}', size: 8, fit: 'wrap' }, ], }; const rows = [{ loc: 'A-03-12', desc: 'Floor 2, zone A, shelf 3, bin 12' }]; Label82.print(template, rows); ``` ## Error correction (ecc) | ecc | Recovers | Use | | --- | --- | --- | | `L` | about 7% | Clean indoor labels, long values | | `M` | about 15% | The default | | `Q` | about 25% | Warehouses, labels that get scratched | | `H` | about 30% | Dirty environments | ## Size and length - Modules of 0.25 mm or more read well with phones. Longer values need more modules, so raise `size`. - The quiet zone (two modules) is included. - A value that is too long raises `QrError` (`qr_too_long`) and draws a crossed box. Shorten URLs. ```js Label82.qrSVG('https://82label.app', { size: 30, ecc: 'M' }); // just the QR picture ``` --- # Printing (browser, agent, silent) > The three modes of print(), the result, picking a printer, and browser dialog settings. Source: https://82label.app/en/docs/printing/ One call prints: `print(template, rows, options)`. `template` is a template object or a template id (`'lbl_…'`). ```js const r = await Label82.print(template, rows, { mode: 'auto', // 'auto' | 'agent' | 'browser' printer: 'TSC TE210', // agent printer name (default printer if left out) copies: 1, // labels per row startAt: 1, // sheets: first slot frames: true, // false: skip layer 'frame' elements }); ``` ## Three modes | mode | Behaviour | | --- | --- | | `auto` (default) | If the print agent runs on the PC and is linked to a subscribed account, print through it with no dialog (silent). Otherwise the browser print dialog. | | `agent` | Agent only. Throws `AgentError` when it is missing or not linked (never falls back). | | `browser` | Always the browser dialog; the agent is not looked for. | ## Silent printing Through the agent there is no dialog. The library renders the labels as PNGs at the printer's dpi, sends them to the agent on the PC (`http://127.0.0.1:18282`), and the agent prints them through the Windows driver at the paper size (no margins, 100%). - The first request from a site makes the agent ask "Allow this site to print?". Meanwhile `print()` prints nothing and returns `status: 'pending_permission'`; after allowing, the next click prints at once. - If the agent runs but is not linked to a subscription, the browser prints and the result carries `notice: 'agent_license_required'`. - Without an agent the library decides within 0.4 seconds and falls back to the browser. ## The result ```js { via: 'agent' | 'browser', printed: 2, // labels sent (copies included) status: 'done' | 'pending_permission' | 'cancelled', errors: [], // bad barcode values, overflowing text… (printing goes on) notice: 'agent_license_required', // only when set } ``` ```js const r = await Label82.print(template, rows); if (r.status === 'pending_permission') showToast('Allow this site in the print agent window.'); else if (r.notice === 'agent_license_required') showToast('Link the print agent to your account to print directly.'); if (r.errors.length) console.warn(r.errors); ``` ## Printers ```js const list = await Label82.printers(); // null without the agent // [{ name: 'TSC TE210', isDefault: true, dpi: 203 }, { name: 'Canon G3010', isDefault: false }] const status = await Label82.agentStatus(); // { version, allowed, licensed } or null ``` ## Browser dialog settings - Scale: **100%** (actual size). Turn off "fit to page". - Margins: **none**. 82label sets `@page { size: Wmm Hmm; margin: 0 }`. - Turn off headers and footers (date, URL). Chrome drops them when there are no margins. - On a roll label printer, pick the label size as the printer's paper size. > Browser printing cannot tell whether the user cancelled the dialog, so `status` is `'done'`. Use the agent when you need an exact print log. --- # Print agent: install and link > Install the Windows print agent, link it with an account key, allowed sites, auto update. Source: https://82label.app/en/docs/agent/ The 82label print agent is a small Windows tray program. While it runs, `print()` sends labels straight to the label printer with no dialog. It is **free for 30 days** from the first link, then **free for subscribers**; it works once linked with an account key. ## Install 1. Get `82label-Setup-version.exe` from [Download](https://82label.app/en/download/) (Windows 10 and 11, 64-bit). 2. It installs without admin rights into your user folder (`%LOCALAPPDATA%\Programs\82label`) and shows a tray icon. 3. It starts with Windows (switch that off in the tray menu). > If "Windows protected your PC" appears, choose **More info → Run anyway**. SmartScreen can warn about new programs until they have enough downloads. The installer's SHA-256 is on the download page. ## Link your account 1. Sign in to 82label.app and press **New key** in [My templates › Print agent](https://82label.app/en/my/agent/). The key (`l82a_…`) is shown only once. 2. Paste it in tray icon › **Link account…**. 3. When the subscription checks out, the tray shows "Linked". The link is re-checked every 12 hours and keeps printing for 7 days offline. ## Allowed sites So that no website can print behind your back, the agent checks the requesting site (Origin). For a new site it asks above the tray, "Allow https://shop.example.com to print labels?", and remembers the answer. 82label.app is allowed from the start. Remove sites in tray › Allowed sites. ## Auto update One minute after start and every six hours it looks for a new version. It installs quietly and restarts only when 82label's update signature checks out and the file from 82label.app matches its size and SHA-256. Turn it off or press "Check for updates" in the tray menu. ## Checking ```js const s = await Label82.agentStatus(); // null → no agent, or it is not running // { version: '0.1.0', allowed: true, licensed: true } → prints directly ``` | Symptom | Check | | --- | --- | | The browser dialog keeps opening | Is the tray icon there and "Linked"? Does security software block `127.0.0.1:18282`? | | Nothing prints | With `status: 'pending_permission'`, allow the site in the tray prompt. | | Positions are off | The driver's paper size and the printer's label sensing calibration. Jobs are always sent at 100%. | --- # Bulk printing from Excel > No code: upload Excel or CSV, match columns, print one label per row. Copies column, start slot, PDF. Source: https://82label.app/en/docs/bulk/ On the [bulk print](https://82label.app/en/print/) page, pick a template and upload a spreadsheet: one label per row. It works without signing in, with samples, the template you were making in the designer, or a JSON template file. The file is read in the browser and never uploaded. - **Template**: samples (Coupang Rocket Growth barcodes and more), your saved templates, the designer draft, a JSON file, a blank roll label. Open one by URL: `/en/print/?id=…`, `?sample=coupang-50x30`, `?preset=formtec-3108`, `?draft=1`. - **File**: xlsx (first sheet), csv (UTF-8 or EUC-KR; comma, semicolon or tab detected), tab-separated txt. You can also copy a table in Excel and press Ctrl+V. Save old xls files as xlsx first. - **Match columns**: automatic by exact name, by name without spaces, case and brackets, and by synonyms (barcode ↔ sku, name ↔ product, qty ↔ quantity, and the Korean column names). Your choices are remembered per template in this browser. - **Copies column**: prints the same label as many times as the number says (0 skips the row, at most 9,999). Edit it per row in the table too. - **Pick rows**: search, select all or none, a checkbox per row. - **Preview**: real pages with your values. Sheets start at the start slot and show empty slots dashed. - **Print**: silent with the print agent, else the browser dialog. [Save PDF](https://82label.app/en/docs/pdf/) works too. ## Doing the same in code ```js import { print } from '@82label/core'; // rows: [{ name, sku, qty }, …] await print('lbl_…', rows, { qtyField: 'qty', startAt: 3, serialStart: 1 }); ``` > If labels come out a few mm off, set it once on the [calibration](https://82label.app/en/calibrate/) page. --- # Print calibration > Measure a printer's offset and scale once; it applies to every print after that. Source: https://82label.app/en/docs/calibration/ Printers grab paper differently, so the same template can land 1 or 2 mm off. On the [calibration](https://82label.app/en/calibrate/) page, measure one test print with a ruler and save X and Y (0.1 mm steps) and a scale (%). - The test print has a border, edge ticks every 1 cm, a centre cross and the size. - Too far right: negative X. Too low: negative Y. If 10 cm prints as 9.8 cm, scale 102%. - Saved in this browser per path: one for browser printing, one per printer with the print agent. - Applies to bulk printing, designer printing and `print()` on your own site (same browser, same site address). ## In code ```js import { print, calibrationKey, getCalibration, setCalibration, calibrationTemplate } from '@82label/core'; setCalibration(calibrationKey('agent', 'TSC TE210'), { offsetX: -1.2, offsetY: 0.5, scale: 1.01 }); await print(t, rows); // saved calibration applied await print(t, rows, { calibration: { offsetX: 0.8 } }); // this value, this time await print(t, rows, { calibration: false }); // no calibration await print(calibrationTemplate(t)); // test print ``` > Calibration moves and scales what is inside each label (about its top-left corner). If a whole Formtec sheet is off, first check 100% scale and no margins in the print dialog. --- # Save as PDF > A PDF at the exact size in mm; barcodes, QR codes and lines are vectors. Source: https://82label.app/en/docs/pdf/ Save a PDF when another PC prints, or for a print shop. Pages keep the paper size (one label for rolls, A4 for sheets); barcode bars, QR codes, lines and boxes are vectors, text and images are embedded at 600 dpi. ```js import { toPDF, downloadPDF } from '@82label/core'; const blob = await toPDF(t, rows, { startAt: 3, qtyField: 'qty' }); // Blob (application/pdf) await downloadPDF(t, rows, 'labels.pdf'); // download it ``` - Options: `startAt`, `frames`, `serialStart`, `now`, `qtyField`, `textDpi` (default 600). - Browser only (text is drawn with canvas); on a server it throws `PdfUnsupported`. - The "Save PDF" buttons in [bulk print](https://82label.app/en/print/) and the designer use the same function. > When printing the PDF, keep the dialog at 100% (actual size). --- # Web component > A preview and a print button from one tag: attributes, properties, events. Source: https://82label.app/en/docs/element/ The CDN script registers ``. It previews the first row and prints every row when its button is pressed. A good fit for server-rendered pages without a framework (PHP, JSP, ASP). ```html ``` ## Attributes | Attribute | Meaning | | --- | --- | | `template` | A template id (`lbl_…`) or template JSON. | | `data` | A JSON object or array; each array item prints one label. | | `printer` | Agent printer name. | | `mode` | `auto` (default), `agent`, `browser` | | `start-at` | Sheets: first slot. | | `button` | Button text (default "인쇄"). | | `preview` | `false` hides the preview and leaves the button. | | `frames` | `false` skips pre-printed frames (`layer: 'frame'`). | | `base` | Server for template ids (default https://82label.app). | ## From script ```js const el = document.querySelector('label-82'); el.template = template; // an object as is el.data = orders.map(toRow); // an array el.addEventListener('printed', (e) => console.log(e.detail)); // PrintResult el.addEventListener('error', (e) => console.error(e.detail)); await el.print(); // print without the button ``` ## Styling `82label.css` styles the preview frame and button; change them with CSS variables. The label picture itself is paper, so it stays white. ```css label-82 { --l82-accent: #d9431f; /* button */ --l82-accent-ink: #fff; --l82-bg: #fafaf9; /* preview frame */ --l82-border: #e4e4e0; --l82-radius: 8px; --l82-font: Pretendard, sans-serif; } ``` Messages (waiting for permission, subscription needed …) appear below the button in `.l82-notice`. For another tag name: `Label82.defineLabelElement('shop-label')`. --- # React and Vue > Using @82label/core from React and Vue. Source: https://82label.app/en/docs/frameworks/ `print()` is a plain function: call it from any framework. Dedicated React and Vue packages are coming; the core is enough, as below. ## React ```js import { useState } from 'react'; import { print } from '@82label/core'; import template from './labels/product.json'; export function PrintLabelButton({ items }) { const [busy, setBusy] = useState(false); const onClick = async () => { setBusy(true); try { const rows = items.map((it) => ({ name: it.name, price: it.price, sku: it.sku })); const r = await print(template, rows); if (r.status === 'pending_permission') alert('Allow this site in the print agent.'); } finally { setBusy(false); } }; return ; } ``` ## Preview (React) ```js import { renderToSVG } from '@82label/core'; export function LabelPreview({ template, row }) { // renderToSVG escapes every value, so the markup is safe to insert return
; } ``` ## Vue ```vue ``` ## As a web component `` is a standard custom element, so React 19 and Vue 3 can use the tag directly. In Vue, add `label-82` to `compilerOptions.isCustomElement`. ```js import { defineLabelElement } from '@82label/core'; import '@82label/core/styles.css'; defineLabelElement(); // React 19 ``` > With SSR (Next.js, Nuxt) call `print()` only from browser events. `renderToSVG` works on the server too. --- # API reference > Every function and value exported by @82label/core (the global Label82). Source: https://82label.app/en/docs/api/ Everything below is exported both by `import { … } from '@82label/core'` and on the CDN global `Label82`. ## Printing | Name | What it does | | --- | --- | | `print(template, rows?, options?)` | Print a template (object or id) with data. `Promise`. See [Printing](https://82label.app/en/docs/printing/). | | `printers()` | The agent's printers `{ name, isDefault, dpi? }[]`, or `null` without an agent. | | `agentStatus()` | `{ version, allowed, licensed }` or `null`. | | `printViaBrowser(svgs, size)` | Print page SVGs through the browser dialog (`size` = `{ width, height }` in mm). | | `rasterize(svg, widthMm, heightMm, dpi)` | An SVG as a PNG `Blob` at the given dpi. | | `AgentError` | `mode: 'agent'` failures. `code`: unavailable, denied, license_required, failed. | | `toPDF(template, rows?, options?)` | Labels as a PDF `Blob`; barcodes, QR and lines as vectors. Browser only. See [Save as PDF](https://82label.app/en/docs/pdf/). | | `downloadPDF(template, rows?, filename?, options?)` | Make the PDF and download it. | | `calibrationKey(via, printer?)` | Calibration key: `'browser'` or `'agent:printer'`. | | `getCalibration(key) / setCalibration(key, cal \| null)` | Calibration `{ offsetX, offsetY, scale }` saved in this browser. See [Print calibration](https://82label.app/en/docs/calibration/). | | `calibrationTemplate(template)` | A test print for the same paper (border, 1 cm ticks, cross, size). | | `AGENT_PORTS` | Ports the agent listens on: `[18282, 18283, 18284]`. | ## Saved templates | Name | What it does | | --- | --- | | `loadTemplate(id, base?)` | Fetch a template from `https://82label.app/api/t/`; each id is fetched once per page. | | `loadCourierTemplate(id, base?)` | A courier waybill preset (subscribers, needs the 82label.app sign-in cookie). | | `clearTemplateCache()` | Forget fetched templates. | | `TemplateNotFound` | Unknown id, deleted template, network error or invalid JSON. | | `PaymentRequired` | 402: an unpaid saved template, or a courier preset that needs a subscription. | ## Rendering | Name | What it does | | --- | --- | | `renderToSVG(template, row?, options?)` | One label as an SVG string (example values without `row`). No DOM needed. | | `renderLabelSVG(template, row?, options?)` | `{ svg, errors }`. | | `renderPageSVG(template, page, rows, options?)` | One page (a sheet, or a roll label) as `{ svg, errors }`. | | `barcodeSVG(value, { format, height, moduleWidth, showText, codeSet })` | A barcode picture on its own. | | `qrSVG(value, { ecc, size })` | A QR picture on its own. | | `measureText(text, sizeMm, font?, bold?)` | Text width in mm (canvas in browsers). | | `layoutPages(template, count, startAt?)` | Pages and slot positions for `count` labels: `Page[]`. | | `pageSize(template)` | Printed page size `{ width, height }` in mm. | ## Barcodes and QR | Name | What it does | | --- | --- | | `encodeBarcode(format, value, { codeSet? })` | Bar modules and the human-readable text; `BarcodeError` for invalid values. | | `eanCheckDigit(digits)` | EAN/UPC check digit. | | `mod7Check(digits)` | Remainder mod 7 (some courier tracking numbers). | | `encodeQR(value, ecc?)` | QR module matrix; `QrError` when too long. | | `BarcodeError, QrError` | Value errors. | ## Fields | Name | What it does | | --- | --- | | `extractFields(template)` | Field names used by a template. | | `fill(text, row, ctx?)` | Text with `{{name}}`, `{{name\|format}}`, `{{seq}}` and `{{today}}` replaced. `ctx` = `{ index, serialStart, now }`. | | `FIELD_FORMATS` | Value formats `{ id, label, example }[]`. See [Value formats](https://82label.app/en/docs/formats/). | | `exampleRow(template)` | An example row from `fields[].example`. | | `escapeXml(text)` | Escape for SVG/HTML. | ## Paper and units | Name | What it does | | --- | --- | | `FORMTEC` | Formtec A4 sheets `SheetSpec[]` (code, grid, size, margins, verified, source). | | `ROLL_SIZES` | Common roll sizes `{ w, h, label, use }[]`. | | `COURIERS` | Courier template info (empty in the open-source package; the site fills it). | | `presetTemplate('roll', w, h) / presetTemplate('formtec', id)` | An empty template. | | `SAMPLES / sampleTemplate(id)` | Free starter templates: Coupang Rocket Growth barcodes (50×30, 60×40, 70×40, A4 21 and 40), product notice, price tag, asset tag. | | `sheetPaper(id)` | The `paper` object of a Formtec sheet. | | `mmToPx(mm, dpi?), mmToDots(mm, dpi), ptToMm(pt), MM_PER_PT` | Unit conversion. | ## Other | Name | What it does | | --- | --- | | `defineLabelElement(tag?)` | Register `` (or another name); automatic in the CDN build. | | `VERSION` | Library version string. | ## Types ```ts type Template = { v: 1; name: string; paper: RollPaper | SheetPaper; label: { width; height; radius? }; elements: Element[]; fields?: FieldDef[] }; type RollPaper = { kind: 'roll'; gap?; dpi?: 203 | 300 | 600; direction?: 0 | 90 | 180 | 270 }; type SheetPaper = { kind: 'sheet'; preset?; page: 'A4' | 'Letter' | { width; height }; cols; rows; marginTop; marginLeft; pitchX; pitchY; shape?: 'rect' | 'circle' | 'ring'; innerDiameter? }; type Row = Record; type PrintOptions = { printer?; copies?; startAt?; mode?: 'auto' | 'agent' | 'browser'; dpi?; frames?; base?; serialStart?; now?: Date; calibration?: Calibration | false; qtyField? }; type Calibration = { offsetX?; offsetY?; scale? }; // mm, mm, 1 = 100% type PrintResult = { via: 'agent' | 'browser'; printed; status: 'done' | 'pending_permission' | 'cancelled'; errors: RenderError[]; notice?: 'agent_license_required' }; type RenderError = { element: number; code: 'barcode_invalid' | 'qr_too_long' | 'overflow' | 'image_failed'; message: string }; ``` --- # Saved templates and pricing > Free when you host the template yourself; 3 saved templates free, then ₩5,000 per template per month. Source: https://82label.app/en/docs/pricing/ There are two ways to use 82label. Either way the library, the designer and browser printing are free. | | Template in your code | Saved on 82label, loaded by id | | --- | --- | --- | | How | Download the JSON from the designer and keep it in your code. | Save in the designer and call `print('lbl_…', rows)`. | | Edits | Change your code and redeploy | Edit on 82label.app; applies at once | | 82label server | Not needed | Serves the template at print time | | Price | Free | 3 templates free, then ₩5,000 per template per month | ## What a subscription adds - Saved templates beyond 3 (₩5,000 each per month, VAT included) - [Courier waybill templates](https://82label.app/en/docs/couriers/) - The [Windows print agent](https://82label.app/en/docs/agent/) (silent printing) ## Billing - Monthly card billing through Toss Payments, charged automatically on the same day of the month you signed up. - The amount is **paid templates × ₩5,000** (VAT included), at least one template while subscribed. - Templates added mid-month count from the next charge (no proration). - Card statements read **82label 서식 보관 (월 정기)**. - Cancel any time in [My templates › Billing](https://82label.app/en/my/billing/); you keep access until the paid period ends. - A failed charge is retried for three days. After that, templates beyond the first three stop loading by id (nothing is deleted; paying restores them). - Refunds: [Terms › Subscriptions and refunds](https://82label.app/en/terms/#refund). ## Suspended templates When payment stops, the first three templates (by creation) keep working and the others answer `GET /api/t/:id` with 402 `payment_required`. The library throws `PaymentRequired` and `` shows that a subscription is needed. > Until the official launch everything is free and the payment screens are closed. --- # FAQ > Pricing, printers, sheet alignment, the print agent, security. Source: https://82label.app/en/docs/faq/ ## Is it really free? The library (MIT), the designer, JSON export, browser printing and the roll and Formtec templates are free. Keeping more than 3 templates on the 82label server, courier waybill templates and the Windows print agent need a subscription (₩5,000 per template per month, VAT included). Everything is free until the official launch. ## Which printers work? Any printer with a Windows driver: thermal label printers (TSC, Zebra, Bixolon, Sewoo and others) and ordinary laser or inkjet printers for Formtec sheets all print the same picture at its size in millimetres. Printer languages such as ZPL are not used yet. ## Can it print without the dialog? Install the Windows print agent and link it to a subscribed account, and print() prints straight away with no dialog. Without the agent the same code opens the browser print dialog. ## Labels are off on Formtec sheets. Set the print dialog to 100% (actual size), turn off "fit to page" and set margins to none. If a printer still shifts by 1 or 2 mm, measure one test print on the [calibration](https://82label.app/en/calibrate/) page; it applies to every print after that. ## Can I print from Excel without code? Yes. On [bulk print](https://82label.app/en/print/), pick a template, upload Excel or CSV and match the columns. No sign-in needed, and the file is never uploaded. ## The scanner does not read the barcode. Make it at least 10 mm tall, keep clear space on both sides and set the template dpi to the printer's (usually 203). For long values widen the barcode, or use digits only so Code 128 can use its compact C set. ## Can another site print on my printer without asking? No. The agent checks which site sent the request, asks before a new site may print, and prints only for allowed sites. It accepts requests only from your own PC (127.0.0.1). ## Can someone edit my template if they know its id? No. An id only lets others read it; editing and deleting need the signed-in owner. Keep secrets out of templates and pass values as data at print time. ## Does 82label issue courier tracking numbers? No. Tracking numbers and pickups need a courier contract and their API. 82label prints the number and addresses you received in the right places on the waybill. ## Does it work on Mac or Linux? Browser printing works on any system. The print agent for silent printing is Windows 10 and 11 (64-bit) only for now. --- # Changelog --- ## 0.2.0 (2026-10-22): Bulk printing from Excel, print calibration, PDF export, Coupang templates, value formats and serial numbers, React, Vue, Svelte and Angular - Bulk print (/en/print/): pick a template → upload xlsx or CSV (UTF-8 or EUC-KR) or paste → columns matched automatically (synonyms too, remembered per template) → pick rows and copies → preview → print or PDF. No sign-in; the file never leaves the browser - Print calibration (/en/calibrate/): measure a test print and save X/Y (0.1 mm) and scale, per browser path and per printer, applied to every print() - Value formats: {{price|won}}, {{price|money}}, {{date|date:YYYY.MM.DD}} (Excel date numbers too), {{no|pad:6}}, upper, lower, trim. Serial numbers {{seq}} and {{seq:0001}} (serialStart), dates {{today}}, {{today+365}}, {{now}} - print(…, { qtyField }): prints each row as many times as that column says. toPDF and downloadPDF: PDFs at the size in mm with vector barcodes and QR codes - Sample templates: Coupang Rocket Growth barcodes 50×30, 60×40, 70×40, A4 21 (LS-3105) and 40 (LS-3102) labels, product notice 50×70, price tag 40×30, asset tag 50×25 - Designer: multi-select (Shift, marquee, Ctrl+A), align and distribute, select below (Alt+click), smart placement, resizing rotated elements, value format menus, Save PDF, links to bulk print and calibration - Framework wrappers 0.2.0: @82label/react, @82label/vue, @82label/svelte and @82label/angular --- ## 0.1.0 (2026-10-22): First release: label designer, roll, Formtec and courier templates, barcodes and QR, browser printing and the Windows print agent - @82label/core: a dependency-free library that draws and prints labels from template JSON (v1, millimetres). CDN (global Label82), npm and the web component - print(template or id, rows, { mode, printer, copies, startAt, frames }): silent through the print agent when it runs, else the browser dialog (@page size, no margins) - Elements: text (wrapping, shrink to fit), barcode, QR, image, line, box. {{field}} placeholders filled from data, inserted as text only - Barcodes: Code128 (automatic code sets), EAN-13, EAN-8 and UPC-A (check digits computed), Code39, ITF, ITF-14, bars snapped to printer dots. QR (L, M, Q, H) - Roll labels (any size, dpi, direction), 52 Formtec A4 sheets (checked against official templates, start slot), 10 courier waybill templates (subscribers) - renderToSVG, barcodeSVG and qrSVG without a DOM; a JSON Schema of the template format - 82label.app: designer, templates gallery, my templates (save, duplicate, rename, delete), templates by id (/api/t/:id) - Pricing: free to self-host, 3 saved templates free, then 1,000 KRW per template per month (VAT included, Toss Payments). Everything free until launch - Windows print agent (subscribers): tray app, per-site permission, silent printing, account key link, signed auto updates --- # 82label 문서 (한국어) --- # 시작하기 > 서식을 만들고 내 사이트에서 첫 라벨을 인쇄합니다. Source: https://82label.app/docs/getting-started/ 82label 은 라벨 서식(JSON)과 그 서식을 인쇄하는 라이브러리입니다. 서식은 [디자이너](https://82label.app/designer/)에서 mm 단위로 그리고, 내 사이트에서는 `Label82.print(서식, 데이터)` 한 줄로 인쇄합니다. 라이브러리는 의존성이 없고 MIT 라이선스입니다. ```js const template = { v: 1, name: '상품 바코드 50×30', paper: { kind: 'roll', dpi: 203 }, label: { width: 50, height: 30 }, elements: [ { type: 'text', x: 2.5, y: 2, w: 45, h: 6, text: '{{name}}', size: 9, bold: true, fit: 'shrink' }, { type: 'text', x: 2.5, y: 8, w: 45, h: 4, text: '{{price}}', size: 8 }, { type: 'barcode', x: 2.5, y: 13, w: 45, h: 15, format: 'code128', value: '{{sku}}' }, ], }; const rows = [ { name: '무선 이어폰 프로', price: '39,000원', sku: 'EP-2026-0412' }, { name: '고속 충전 케이블 1m', price: '9,900원', sku: 'CB-1M-0007' }, ]; Label82.print(template, rows); ``` 위 라벨은 실제 라이브러리로 그린 것입니다. **이 예제 인쇄**를 누르면 브라우저 인쇄 창이 50×30mm 용지로 열립니다. 데이터 두 줄이 라벨 두 장이 됩니다. ## 1. 서식 만들기 1. [디자이너](https://82label.app/designer/)를 열고 용지를 고릅니다: 롤 크기(예: 50×30), [폼텍 시트](https://82label.app/templates/), 택배 송장. 2. 글자·바코드·QR·이미지·선·박스를 놓습니다. 바뀌는 값은 `{{name}}` 처럼 중괄호 두 개로 적습니다([필드](https://82label.app/docs/fields/)). 3. **코드 받기**에서 서식 JSON 이 들어간 코드를 복사하거나, 로그인해서 **저장**하고 서식 ID(`lbl_…`)로 부릅니다. ## 2. 내 사이트에 붙이기 빌드 도구가 없는 화면이라면 스크립트 한 줄이면 됩니다. 전역 `Label82` 가 생기고 `` 태그도 등록됩니다. ```html ``` npm 을 쓰는 프로젝트라면: ```sh npm i @82label/core ``` ```js import { print } from '@82label/core'; import template from './labels/product.json'; // 디자이너에서 받은 서식 JSON async function printLabels(orders) { const rows = orders.map((o) => ({ name: o.productName, price: o.priceText, sku: o.sku })); const result = await print(template, rows); // result.via: 'agent'(출력 프로그램) 또는 'browser' return result; } ``` ## 3. 인쇄 방식 `print()` 는 기본(`mode: 'auto'`)으로 PC 에 [82label 출력 프로그램](https://82label.app/docs/agent/)이 있으면 인쇄 창 없이 바로 라벨프린터로 보내고, 없으면 브라우저 인쇄 창을 엽니다. 두 경우 모두 같은 그림이 같은 mm 크기로 나옵니다. > 브라우저 인쇄 창에서는 **배율 100%(실제 크기)**, **여백 없음**, 용지는 라벨 크기(롤) 또는 A4(시트)로 두세요. 82label 이 `@page` 크기를 지정하므로 대부분 기본값 그대로 맞습니다. ## 다음 단계 - [서식 JSON](https://82label.app/docs/template/): 요소 종류와 좌표 - [롤 라벨](https://82label.app/docs/roll/) · [폼텍 시트](https://82label.app/docs/sheets/) · [택배 송장](https://82label.app/docs/couriers/) - [바코드](https://82label.app/docs/barcodes/) · [QR](https://82label.app/docs/qr/) - [인쇄](https://82label.app/docs/printing/) · [출력 프로그램 설치와 연결](https://82label.app/docs/agent/) --- # CDN > 스크립트 태그로 붙이기: 버전 고정 주소, 파일 종류, 전역 Label82. Source: https://82label.app/docs/cdn/ 82label.app 이 파일을 직접 내려줍니다. 버전이 들어간 주소는 내용이 바뀌지 않아 브라우저가 1년 동안 캐시합니다. 운영 사이트에는 버전 주소를 쓰세요. | 파일 | 설명 | | --- | --- | | `/cdn/v0.2.0/82label.min.js` | 스크립트 태그용. 전역 `Label82` 를 만들고 `` 를 등록합니다. | | `/cdn/v0.2.0/82label.esm.js` | ` ``` ## ES 모듈 ```html ``` ## 전역 Label82 에 들어 있는 것 `print`, `printers`, `agentStatus`, `loadTemplate`, `renderToSVG`, `barcodeSVG`, `qrSVG`, `layoutPages`, `FORMTEC`, `ROLL_SIZES`, `presetTemplate`, `defineLabelElement`, `VERSION` 등 npm 패키지의 모든 내보내기와 같습니다. 전체 목록은 [API 레퍼런스](https://82label.app/docs/api/)에 있습니다. ## 서식 JSON Schema 서식 형식의 JSON Schema 가 [https://82label.app/schema/template.json](https://82label.app/schema/template.json) 에 있습니다(버전 고정: `/schema/v0.2.0/template.json`, npm: `@82label/core/schema.json`). 편집기에서 서식 파일을 자동 완성·검사할 때 씁니다. ```json { "$schema": "https://82label.app/schema/template.json", "v": 1, "name": "상품 바코드", "paper": { "kind": "roll" }, "label": { "width": 50, "height": 30 }, "elements": [] } ``` > 파일을 내려받아 내 서버에 두어도 됩니다. MIT 라이선스라 고쳐 쓰고 다시 배포해도 됩니다. 사용 통계는 하루 한 번 도메인과 버전만 보내며, 사내 주소에서는 보내지 않습니다. `` 로 끌 수 있습니다. --- # npm > @82label/core 설치, import, 번들러와 TypeScript. Source: https://82label.app/docs/npm/ ```sh npm i @82label/core ``` 패키지는 ES 모듈 하나이고 의존성이 없습니다. TypeScript 타입(`Template`, `Row`, `PrintOptions`, `PrintResult` …)이 함께 들어 있습니다. ```js import { print } from '@82label/core'; import template from './labels/product.json'; // 디자이너에서 받은 서식 JSON async function printLabels(orders) { const rows = orders.map((o) => ({ name: o.productName, price: o.priceText, sku: o.sku })); const result = await print(template, rows); // result.via: 'agent'(출력 프로그램) 또는 'browser' return result; } ``` ## 서식을 파일로 두기 디자이너의 **JSON 내려받기**로 받은 파일을 프로젝트에 넣고 import 하세요. 서식이 코드와 함께 버전 관리되고, 82label 서버 없이 동작합니다. ```ts import type { Template } from '@82label/core'; import raw from './labels/product.json'; const template = raw as Template; ``` ## 웹 컴포넌트를 쓸 때 ```js import { defineLabelElement } from '@82label/core'; import '@82label/core/styles.css'; defineLabelElement(); // 등록 ``` ## 서버에서 그리기 `renderToSVG` 는 DOM 없이 SVG 문자열을 만듭니다. Node, Cloudflare Workers 에서 라벨 미리보기 이미지를 만들거나 메일에 넣을 때 씁니다. 인쇄(`print`)는 브라우저에서만 됩니다. ```js import { renderToSVG } from '@82label/core'; const svg = renderToSVG(template, { name: '무선 이어폰 프로', price: '39,000원', sku: 'EP-2026-0412' }); ``` > React·Vue 전용 패키지는 준비 중입니다. 지금은 `@82label/core` 를 그대로 쓰면 됩니다([React·Vue 예제](https://82label.app/docs/frameworks/)). --- # 서식 JSON > 용지, 라벨 크기, 요소 목록. 모든 길이는 mm 입니다. Source: https://82label.app/docs/template/ 서식은 JSON 하나입니다. 디자이너, 라이브러리, 출력 프로그램이 모두 이 형식만 씁니다. 좌표와 크기는 라벨 왼쪽 위를 0 으로 하는 **mm**, 글자 크기만 **pt** 입니다. 요소는 적힌 순서대로 그려집니다(뒤에 적은 것이 위). ```js const template = { v: 1, name: '출고 라벨', paper: { kind: 'roll', dpi: 203 }, label: { width: 60, height: 40 }, elements: [ { type: 'rect', x: 1, y: 1, w: 58, h: 38, stroke: 0.3, radius: 2 }, { type: 'text', x: 4, y: 3, w: 36, h: 6, text: '{{orderNo}}', size: 9, bold: true }, { type: 'text', x: 4, y: 10, w: 36, h: 12, text: '{{items}}', size: 8, fit: 'wrap' }, { type: 'line', x1: 4, y1: 24, x2: 56, y2: 24, width: 0.3 }, { type: 'text', x: 4, y: 26, w: 36, h: 10, text: '{{customer}}', size: 11, bold: true, valign: 'middle', fit: 'shrink' }, { type: 'qr', x: 41, y: 4, size: 16, value: '{{orderUrl}}' }, ], fields: [ { name: 'orderNo', label: '주문번호', example: 'A-20261022-0012' }, { name: 'items', label: '품목', example: '무선 이어폰 프로 외 2건' }, { name: 'customer', label: '고객', example: '김하늘' }, { name: 'orderUrl', label: 'URL', example: 'https://shop.example.com/o/12' }, ], }; Label82.print(template); ``` ## 맨 위 항목 | 항목 | 설명 | | --- | --- | | `v` | 형식 버전. 지금은 `1`. | | `name` | 서식 이름(내 서식 목록, 인쇄 기록에 보입니다). | | `paper` | `{ kind: 'roll' }` 또는 `{ kind: 'sheet', … }`. [롤 라벨](https://82label.app/docs/roll/), [폼텍 시트](https://82label.app/docs/sheets/) 참고. | | `label` | 라벨 한 장(시트는 한 칸)의 `width`, `height`, 둥근 모서리 `radius`. | | `elements` | 요소 목록. 아래 표. | | `fields` | 선택. `{ name, label, example }` 목록. 디자이너 미리보기와 예시값에 씁니다. | ## 요소 | type | 꼭 필요한 값 | 선택 | | --- | --- | --- | | `text` | `x y w h text size` | `bold`, `align`(left·center·right), `valign`(top·middle·bottom), `fit`(shrink·wrap·clip), `font`, `rotate`(0·90·180·270). `size` 는 pt. | | `barcode` | `value` | `x y w h`(빼면 가운데, 너비 80%), `format`(기본 code128), `showText`, `codeSet`, `rotate` | | `qr` | `value` | `x y size`(빼면 가운데 정사각형), `ecc`(L·M·Q·H, 기본 M) | | `image` | `x y w h src` | `src` 는 data: URL, https 주소 또는 `{{필드}}`. `fit`(contain·cover) | | `line` | `x1 y1 x2 y2 width` | 선 두께 `width` 는 mm | | `rect` | `x y w h` | `stroke`(테두리 두께 mm, 기본 0.3, 0 이면 없음), `fill`(none·black), `radius` | ## 흑백만 라벨프린터는 대부분 감열(흑백)이라 색은 없습니다. 검은 바탕에 흰 글자가 필요하면 `fill: 'black'` 박스 위에 글자를 두지 말고 이미지를 쓰세요. ## 미리 인쇄된 틀 (layer: 'frame') 칸 선, "받는 분" 같은 이름표처럼 용지에 이미 인쇄된 부분은 `layer: 'frame'` 을 붙입니다. 화면과 빈 용지에는 그리고, 틀이 인쇄된 용지에 찍을 때는 `print(t, rows, { frames: false })` 로 빼고 값만 찍습니다. > 서식 JSON 의 JSON Schema: [/schema/template.json](https://82label.app/schema/template.json). 서버(82label.app)는 저장할 때 `v`, 용지 종류, 라벨 크기, 요소 개수(최대 2,000)만 확인합니다. --- # 필드 ({{ }}) > 바뀌는 값을 {{이름}} 으로 적고, 데이터 한 줄이 라벨 한 장이 됩니다. Source: https://82label.app/docs/fields/ 글자, 바코드 값, QR 값, 이미지 주소 어디에나 `{{이름}}` 을 넣을 수 있습니다. 인쇄할 때 넘긴 데이터의 같은 이름 값으로 바뀝니다. 고정 글자와 섞어 써도 됩니다: `'주문 {{orderNo}} · {{qty}}개'`. ```js const template = { v: 1, name: '가격표', paper: { kind: 'roll' }, label: { width: 40, height: 30 }, elements: [ { type: 'text', x: 2, y: 2, w: 36, h: 8, text: '{{name}}', size: 9, bold: true, fit: 'wrap' }, { type: 'text', x: 2, y: 10, w: 36, h: 7, text: '{{price}}원', size: 14, bold: true, align: 'right' }, { type: 'barcode', x: 2, y: 18, w: 36, h: 10, format: 'ean13', value: '{{ean}}' }, ], }; const rows = [ { name: '유기농 우유 900ml', price: '3,480', ean: '880123456789' }, { name: '통밀 식빵', price: '4,200', ean: '880123456790' }, ]; Label82.print(template, rows); ``` ## 데이터 - `print(template, rows)` 의 `rows` 는 객체 하나 또는 배열입니다. 배열의 한 항목이 라벨 한 장입니다. - 값은 문자열이나 숫자. `null`·`undefined`·없는 이름은 빈 칸이 됩니다. - 값은 글자로만 들어갑니다(HTML·SVG 로 해석하지 않음). 고객이 입력한 주소를 그대로 넣어도 안전합니다. - 같은 라벨을 여러 장: `{ copies: 3 }`. 행마다 3장씩 나옵니다. 행마다 다르게 찍으려면 `{ qtyField: '수량' }`(그 열의 숫자만큼). ## 필드 목록과 예시값 디자이너는 서식 안의 `{{ }}` 를 모아 필드 표를 만들고, 예시값을 `fields` 에 저장합니다. 데이터 없이 미리보기할 때 이 예시값이 쓰입니다. ```js import { extractFields, exampleRow, fill } from '@82label/core'; extractFields(template); // ['name', 'price', 'ean'] exampleRow(template); // { name: '…', price: '…', ean: '…' } (fields[].example, 없으면 이름) fill('{{name}} {{price}}원', { name: '우유', price: '3,480' }); // '우유 3,480원' ``` 찍히는 모양을 바꾸는 `{{가격|won}}`, `{{날짜|date:YYYY.MM.DD}}`, 일련번호 `{{seq:0001}}`, 오늘 날짜 `{{today+365}}` 는 [값 서식·일련번호·날짜](https://82label.app/docs/formats/)에 있습니다. 엑셀 파일로 코드 없이 찍으려면 [대량 출력](https://82label.app/docs/bulk/). > 필드 이름에는 한글도 됩니다(`{{상품명}}`). 다만 코드에서 다루기 쉽게 영문 이름을 권합니다. --- # 값 서식·일련번호·날짜 > {{가격|won}}, {{날짜|date:YYYY.MM.DD}}, {{seq:0001}}, {{today+365}}. 데이터를 고치지 않고 찍히는 모양만 바꿉니다. Source: https://82label.app/docs/formats/ 필드 이름 뒤에 `|서식` 을 붙이면 값이 찍히는 모양이 바뀝니다. 데이터는 그대로 두고 서식만 고치면 됩니다. 디자이너의 필드 표와 글자·바코드 속성에 있는 "서식" 목록에서 골라도 됩니다. | 쓰는 법 | 값 | 찍히는 글자 | | --- | --- | --- | | `{{가격\|money}}` | 12900 | 12,900 | | `{{가격\|won}}` | 12900 | 12,900원 | | `{{날짜\|date}}` | 20261022, 2026-10-22, 45950(엑셀 날짜) | 2026-10-22 | | `{{날짜\|date:YYYY.MM.DD}}` | 2026-10-22 | 2026.10.22 | | `{{날짜\|date:YY년 M월 D일}}` | 2026-10-22 | 26년 10월 22일 | | `{{번호\|pad:6}}` | 42 | 000042 | | `{{코드\|upper}}` · `\|lower` · `\|trim` | ab-1 | AB-1 · ab-1 · ab-1 | 날짜 글자: `YYYY` `YY` `MM` `M` `DD` `D` `HH` `mm` `ss`. 모르는 서식 이름은 값을 그대로 둡니다. ## 일련번호와 오늘 날짜 - `{{seq}}`: 이번 인쇄에서 몇 번째 라벨인지(1, 2, 3…). 수량 열로 여러 장 찍으면 장마다 늘어납니다. - `{{seq:0001}}`: 숫자 자릿수가 시작 값과 폭입니다. `0001` 은 0001부터 4자리, `0100` 은 0100부터. - 시작 번호를 바꾸려면 `print(t, rows, { serialStart: 501 })`. 대량 출력 화면의 "시작 번호" 칸과 같습니다. - `{{today}}`, `{{today+365}}`(유통기한), `{{today-1}}`: 인쇄하는 날 기준, 내 PC 시간대. `|date:FMT` 를 붙일 수 있습니다. - `{{now}}`: 인쇄 시각 `YYYY-MM-DD HH:mm`. ```js const template = { v: 1, name: '자산 번호', paper: { kind: 'roll' }, label: { width: 50, height: 25 }, elements: [ { type: 'text', x: 2, y: 2, w: 30, h: 5, text: '{{이름}}', size: 8, bold: true }, { type: 'text', x: 30, y: 2, w: 18, h: 5, text: '{{가격|won}}', size: 8, align: 'right' }, { type: 'barcode', x: 2, y: 8, w: 46, h: 15, format: 'code128', value: 'A-{{seq:0001}}', showText: true }, ], }; const rows = [ { 이름: '노트북', 가격: 1290000 }, { 이름: '모니터', 가격: 329000 }, ]; Label82.print(template, rows); ``` > `extractFields(t)` 는 데이터 필드만 돌려줍니다: `|서식` 을 떼고, `seq`·`today`·`now` 는 뺍니다. --- # 롤 라벨 > 감열 라벨프린터의 롤 용지: 크기, dpi, 방향. Source: https://82label.app/docs/roll/ 롤 라벨은 라벨 한 장이 인쇄 한 페이지입니다. 데이터가 10줄이면 10장이 이어서 나옵니다. 크기는 아무 값이나 mm 로 적으면 됩니다. ```js { v: 1, name: '상품 라벨', paper: { kind: 'roll', dpi: 203, gap: 2 }, label: { width: 50, height: 30 }, elements: [ /* … */ ], } ``` | 항목 | 설명 | | --- | --- | | `dpi` | 프린터 해상도 203·300·600. 바코드 막대를 도트 단위로 맞춰 스캔이 잘 되게 합니다. 모르면 203(대부분의 감열 라벨프린터). | | `gap` | 라벨 사이 간격(mm). 참고용이고 실제 간격은 프린터 드라이버가 처리합니다. | | `direction` | 0·90·180·270. 프린터에서 나오는 방향에 맞춰 돌립니다. 90·270 이면 인쇄 페이지의 가로·세로가 바뀝니다. | ## 자주 쓰는 크기 디자이너의 빠른 선택 목록입니다(`ROLL_SIZES`). 목록에 없는 크기는 직접 입력하세요. | 가로×세로 (mm) | 주 용도 | | --- | --- | | `30×10` | 바코드·가격 | | `30×15` | 바코드·가격 | | `30×20` | 바코드·가격 | | `35×15` | 바코드·가격 | | `40×20` | 바코드·가격 | | `40×30` | 상품 바코드·가격표 | | `45×25` | 바코드·가격 | | `45×50` | 상품 | | `50×30` | 상품 바코드·재고 | | `50×50` | 상품 | | `50×80` | 상품 | | `60×40` | 상품·식품 표시 | | `60×60` | 상품 | | `70×30` | 상품 | | `70×50` | 상품·식품 표시 | | `70×60` | 상품 | | `80×40` | 물류 | | `80×50` | 상품 정보·소형 송장 | | `80×120` | 물류 | | `90×60` | 물류 | | `90×120` | 물류 | | `100×30` | 물류 | | `100×50` | 상품 정보·소형 송장 | | `100×80` | 물류 | | `100×100` | 박스·물류 | | `100×150` | 택배 송장 | | `102×152` | 4×6인치 송장 (해외 배송) | ```js import { presetTemplate, ROLL_SIZES } from '@82label/core'; const t = presetTemplate('roll', 50, 30); // 빈 50×30 서식 t.elements.push({ type: 'barcode', value: '{{sku}}' }); // 좌표를 빼면 가운데 ``` ## 프린터 설정 - 드라이버의 용지 크기를 라벨 크기와 같게 두세요. 출력 프로그램은 작업마다 용지 크기를 지정합니다. - 라벨 감지(갭·블랙마크)는 프린터에서 한 번 맞춰 두면 됩니다(보통 전원 버튼 길게 누르기 또는 드라이버의 보정). - 브라우저 인쇄라면 인쇄 창에서 해당 라벨프린터와 용지를 고르고, 배율은 100%. --- # 폼텍 시트 > A4 시트 라벨 52종의 칸 수·크기·여백과 시작 칸(startAt). Source: https://82label.app/docs/sheets/ 폼텍(Formtec) A4 라벨 52종의 칸 배치가 들어 있습니다. 값은 폼텍 공식 템플릿에서 확인한 것이고, 각 규격마다 출처를 남겼습니다. 시트는 데이터를 왼쪽 위 칸부터 오른쪽으로, 줄을 바꿔 채우고 칸이 모자라면 다음 장으로 넘어갑니다. ```js const template = { v: 1, name: '주소 라벨', paper: Label82.sheetPaper('formtec-3108'), // LS-3108, 2칸 × 7줄 label: { width: 99.1, height: 38.1 }, elements: [ { type: 'text', x: 6, y: 5, w: 86, h: 14, text: '{{address}}', size: 10, fit: 'wrap' }, { type: 'text', x: 6, y: 22, w: 86, h: 10, text: '{{name}} 님', size: 14, bold: true }, ], }; const rows = [ { address: '서울특별시 강남구 테헤란로 152, 12층', name: '김하늘' }, { address: '부산광역시 해운대구 센텀중앙로 79, 3층', name: '이도윤' }, { address: '대전광역시 유성구 대학로 99, 5동 201호', name: '박서연' }, ]; Label82.print(template, rows, { startAt: 3 }); ``` 위 예제는 `startAt: 3` 이라 세 번째 칸부터 채웁니다. 이미 몇 칸을 쓴 시트를 다시 넣을 때 씁니다. ```js Label82.print(template, rows, { startAt: 3 }); ``` ## 규격으로 서식 만들기 ```js import { presetTemplate, sheetPaper } from '@82label/core'; const t = presetTemplate('formtec', 'LS-3114'); // 'formtec-3114', '3114' 도 됩니다 // 또는 용지만: paper: sheetPaper('formtec-3114') ``` ## 직접 만든 시트 목록에 없는 시트는 칸 수와 여백을 직접 적습니다. `pitchX`·`pitchY` 는 한 칸의 왼쪽(위) 끝에서 다음 칸의 왼쪽(위) 끝까지 거리입니다(칸 크기 + 간격). ```js paper: { kind: 'sheet', page: 'A4', cols: 3, rows: 8, marginTop: 12.5, marginLeft: 6.5, pitchX: 66.5, pitchY: 34, } ``` ## 들어 있는 규격 (52종) "미확인" 은 공식 템플릿에서 한 값 이상을 확인하지 못한 규격입니다. 처음 한 장은 보통 용지에 찍어 겹쳐 보세요. 디자이너에서 바로 열려면 [서식 모음](https://82label.app/templates/)을 쓰세요. | 규격 | 이름 | 칸 | 라벨 크기 (mm) | 치수 | | --- | --- | --- | --- | --- | | `LS-3105` | 주소용 라벨 21칸 | 3×7 | 63.5×38.1 | 확인 | | `LS-3106` | 주소용 라벨 24칸 | 3×8 | 64×34 | 확인 | | `LS-3107` | 주소용 라벨 16칸 | 2×8 | 99.1×33.9 | 확인 | | `LS-3108` | 주소용 라벨 14칸 | 2×7 | 99.1×38.1 | 확인 | | `LS-3109` | 주소용 라벨 18칸 | 2×9 | 100×30 | 확인 | | `LS-3212` | 주소용 라벨 12칸 | 2×6 | 100×45 | 확인 | | `LS-3218` | 주소용 라벨 18칸 | 3×6 | 63.5×45 | 확인 | | `LS-3219` | 주소용 라벨 22칸 | 2×11 | 100×24.5 | 확인 | | `LS-3620` | 주소용 라벨 6칸 | 1×6 | 119.3×42.8 | 확인 | | `LS-3667` | 주소용 라벨 12칸 | 2×6 | 76×44.4 | 확인 | | `LS-3112` | 물류관리용 라벨 12칸 | 3×4 | 63.5×70 | 확인 | | `LS-3114` | 물류관리용 라벨 8칸 | 2×4 | 99.1×67.7 | 확인 | | `LS-3116` | 물류관리용 라벨 6칸 | 2×3 | 99.1×93.1 | 확인 | | `LS-3118` | 물류관리용 라벨 4칸 | 2×2 | 99.1×140 | 확인 | | `LS-3120` | 물류관리용 라벨 2칸 | 1×2 | 200×140 | 확인 | | `LS-3203` | 물류관리용 라벨 3칸 | 1×3 | 200×93 | 확인 | | `LS-3660` | 물류관리용 라벨 8칸 | 1×8 | 124×30 | 확인 | | `LS-3100` | 바코드용 라벨 65칸 | 5×13 | 38.1×21.2 | 확인 | | `LS-3101` | 바코드용 라벨 60칸 | 5×12 | 38.1×19.2 | 확인 | | `LS-3102` | 바코드용 라벨 40칸 | 4×10 | 47×26.9 | 확인 | | `LS-3104` | 바코드용 라벨 27칸 | 3×9 | 62.7×30.1 | 확인 | | `LS-3110` | 분류표기용 라벨 54칸 | 6×9 | 26×29.5 | 확인 | | `LS-3136` | 분류표기용 라벨 136칸 | 4×34 | 46×8 | 확인 | | `LS-3142` | 분류표기용 라벨 42칸 | 2×21 | 72×13 | 확인 | | `LS-3145` | 분류표기용 라벨 145칸 | 5×29 | 30×9 | 확인 | | `LS-3154` | 분류표기용 라벨 54칸 | 3×18 | 62.7×15 | 확인 | | `LS-3181` | 분류표기용 라벨 81칸 | 3×27 | 53×10 | 확인 | | `LS-3189` | 분류표기용 라벨 189칸 | 7×27 | 25.4×10 | 확인 | | `LS-3216` | 분류표기용 라벨 216칸 | 8×27 | 20×10 | 확인 | | `LS-3622` | 분류표기용 라벨 96칸 | 6×16 | 30×14 | 확인 | | `LS-3623` | 분류표기용 라벨 84칸 | 4×21 | 46×11 | 확인 | | `LS-3676` | 분류표기용 라벨 76칸 | 4×19 | 40×14 | 확인 | | `LS-3677` | 분류표기용 라벨 78칸 | 6×13 | 30×21 | 확인 | | `LS-3510` | 분류표기용 라벨 10칸 | 2×5 | 88.9×52 | 확인 | | `LS-3615` | 분류표기용 라벨 10칸 | 2×5 | 69.8×52 | 확인 | | `LS-3610` | 분류표기·QR 라벨 8칸 | 2×4 | 69.8×69.8 | 확인 | | `LS-3111` | 분류표기·QR 라벨 104칸 | 8×13 | 20×20 | 확인 | | `LS-3990` | QR 전용 라벨 28칸 | 4×7 | 40×40 | 확인 | | `LS-3639` | 원형 라벨 Ø38 20칸 | 4×5 | 38×38 | 확인 | | `LS-3640` | 원형 라벨 Ø63 12칸 | 3×4 | 63×63 | 확인 | | `LS-3641` | 원형 라벨 Ø20 108칸 | 9×12 | 20×20 | 확인 | | `CL-3642P` | CD/DVD 라벨 2칸 | 1×2 | 117.8×117.8 | **미확인** | | `LS-3621` | 화일인덱스 라벨 36칸 | 2×18 | 95×14 | 확인 | | `LS-3624` | 화일인덱스 라벨 9칸 | 1×9 | 150×28 | 확인 | | `LS-3625` | 화일인덱스 라벨 7칸 | 1×7 | 160×38 | 확인 | | `LS-3626` | 화일인덱스 라벨 4칸 | 1×4 | 160×60 | 확인 | | `LS-3627` | 화일인덱스(등) 라벨 8칸 | 4×2 | 48×130 | 확인 | | `LS-3628` | 화일인덱스(등) 라벨 30칸 | 15×2 | 12×130 | 확인 | | `LS-3661` | 화일인덱스 라벨 48칸 | 1×48 | 114×5.5 | 확인 | | `LS-3662` | 화일인덱스 라벨 13칸 | 1×13 | 146×17.8 | 확인 | | `LS-2308B` | 이름표 라벨 8칸 | 2×4 | 90×60 | 확인 | | `LS-3130` | 전지 라벨 1칸 (분류표기용) | 1×1 | 210×297 | 확인 | > 인쇄할 때 프린터의 "용지에 맞춤"을 끄고 실제 크기(100%)로 두세요. 가정용 잉크젯은 위·아래 여백이 몇 mm 씩 밀릴 수 있습니다. 밀리면 서식 전체를 옮기기보다 `marginTop`·`marginLeft` 를 0.5mm 단위로 고치세요. --- # 택배 송장 > 택배사별 송장 기본서식(구독자 전용)과 송장번호·분류코드 필드. Source: https://82label.app/docs/couriers/ 주요 택배사 송장 용지에 맞춘 기본서식이 있습니다: CJ대한통운, 한진, 롯데, 우체국, 로젠, 경동, 대신, 일양, 합동, 공통 세로 100×150. 받는 분·보내는 분·주소·전화·송장번호 바코드·분류코드·품목·메모 자리가 잡혀 있고, 값은 모두 `{{필드}}` 입니다. > 택배 송장 서식은 **구독자 전용**입니다. 무료 계정은 [서식 모음](https://82label.app/templates/)에서 이름과 크기만 보입니다. [요금](https://82label.app/pricing/) 참고. ## 82label 이 하는 일과 하지 않는 일 - 하는 일: 택배사에서 받은 송장번호·분류코드·주소를 정해진 위치에 찍습니다. - 하지 않는 일: 송장번호 발급, 집하 접수, 택배사 API 연동. 이건 택배사 계약과 각 사의 API 가 필요합니다. - 택배사 상표·로고는 넣지 않습니다(이름 글자만). ## 불러오기 디자이너에서 [서식 모음](https://82label.app/templates/)의 택배 카드를 누르면 열립니다. 고쳐서 저장하면 내 서식이 되고, ID 로 부르면 됩니다. 코드에서 기본서식을 직접 받을 수도 있습니다(82label.app 로그인 쿠키가 있는 브라우저에서만). ```js import { loadCourierTemplate, print } from '@82label/core'; const t = await loadCourierTemplate('cj-standard'); await print(t, [{ trackingNo: '609123456781', receiverNameMasked: '김*늘', receiverAddress: '서울특별시 강남구 테헤란로 152', /* … */ }], { frames: false }); ``` ## 송장 용지에 맞추기 - 용지 크기와 칸 위치는 공개 자료와 실물 기준이지만 택배사·계약마다 조금씩 다릅니다. 처음에는 지급 용지에 한 장 찍어 겹쳐 보세요. - 칸 선과 이름표가 이미 인쇄된 용지라면 `frames: false` 로 값만 찍습니다([layer: 'frame'](https://82label.app/docs/template/)). - 송장번호 바코드 형식은 택배사가 정합니다. 한진·롯데처럼 ITF(짝수 자리 숫자)를 쓰는 곳은 `format: 'itf'` 입니다([바코드](https://82label.app/docs/barcodes/)). --- # 바코드 > Code128, EAN-13·8, UPC-A, Code39, ITF, ITF-14. 체크 숫자와 스캔 크기. Source: https://82label.app/docs/barcodes/ 바코드는 요소 하나입니다. 값만 적어도 됩니다: 좌표를 빼면 라벨 가운데에 너비 80% 로 들어갑니다. ```js { type: 'barcode', value: '{{sku}}' } // Code 128, 가운데 { type: 'barcode', x: 3, y: 14, w: 44, h: 13, format: 'ean13', value: '{{ean}}', showText: true } ``` ```js Label82.barcodeSVG('880123456789', { format: 'ean13', height: 18 }) ``` ## 형식 | format | 값 | 쓰는 곳 | | --- | --- | --- | | `code128` | 숫자·영문·기호 아무 길이 | 기본값. 상품 코드(SKU), 주문번호, 택배 송장. 숫자만 길면 자동으로 촘촘한 C 세트. | | `ean13` | 숫자 12자리(+체크 1) | 국내 유통 상품 바코드(880…). 12자리만 넣으면 체크 숫자를 계산해 붙입니다. 13자리면 체크 숫자를 검사합니다. | | `ean8` | 숫자 7자리(+체크 1) | 작은 상품. | | `upca` | 숫자 11자리(+체크 1) | 북미 상품 바코드. | | `code39` | 대문자 영문·숫자·`- . 공백 $ / + %` | 오래된 사내 시스템, 자산 관리. | | `itf14` | 숫자 13자리(+체크 1) | 물류 박스(GTIN-14). | | `itf` | 짝수 자리 숫자 | Interleaved 2 of 5, 체크 숫자 없음. 한진·롯데 송장번호. | ## 잘못된 값 값이 형식에 맞지 않으면(EAN 자릿수가 틀림, Code39 에 소문자 등) 그 자리에 X 표시 상자를 그리고 인쇄 결과의 `errors` 에 남깁니다. 미리 검사하려면 `encodeBarcode` 를 부르세요. ```js import { encodeBarcode, eanCheckDigit, BarcodeError } from '@82label/core'; eanCheckDigit('880123456789'); // 3 → 8801234567893 try { encodeBarcode('ean13', '88012345'); } catch (e) { if (e instanceof BarcodeError) console.log(e.message); } const r = await Label82.print(template, rows); r.errors; // [{ element: 2, code: 'barcode_invalid', message: '…' }] ``` ## 스캔이 잘 되는 크기 - 막대 하나(모듈)의 폭은 프린터 도트의 정수배로 맞춥니다(`paper.dpi`). 203dpi 에서 한 도트는 0.125mm. - 좌우 여백(quiet zone)은 모듈 10개분을 비워 둡니다. 라벨 가장자리에 붙이지 마세요. - 높이는 10mm 이상을 권합니다. 송장처럼 멀리서 읽는 바코드는 15mm 이상. - 긴 값을 좁은 칸에 넣으면 모듈이 0.19mm 아래로 내려가 읽기 어렵습니다. 디자이너가 경고합니다. ## 바코드만 따로 라벨 없이 바코드 그림만 필요하면 `barcodeSVG` 를 쓰세요. 화면 표시나 PDF 에 넣을 SVG 문자열을 돌려줍니다. ```js Label82.barcodeSVG('EP-2026-0412', { height: 15, showText: true }); Label82.barcodeSVG('880123456789', { format: 'ean13', moduleWidth: 0.33 }); ``` --- # QR 코드 > QR 요소, 오류 정정 단계, 크기와 길이. Source: https://82label.app/docs/qr/ QR 은 정사각형 요소입니다. `size` 는 한 변의 mm 이고, 좌표를 빼면 가운데에 들어갑니다. 주문 조회 주소, 상품 페이지, 재고 위치 코드처럼 긴 값에 씁니다. ```js { type: 'qr', x: 36, y: 4, size: 20, value: 'https://shop.example.com/o/{{orderNo}}', ecc: 'M' } ``` ```js const template = { v: 1, name: '재고 위치', paper: { kind: 'roll', dpi: 203 }, label: { width: 60, height: 30 }, elements: [ { type: 'qr', x: 3, y: 3, size: 24, value: 'LOC:{{loc}}', ecc: 'Q' }, { type: 'text', x: 30, y: 5, w: 28, h: 10, text: '{{loc}}', size: 16, bold: true, fit: 'shrink' }, { type: 'text', x: 30, y: 16, w: 28, h: 10, text: '{{desc}}', size: 8, fit: 'wrap' }, ], }; const rows = [{ loc: 'A-03-12', desc: '2층 A구역 3번 선반 12칸' }]; Label82.print(template, rows); ``` ## 오류 정정 (ecc) | ecc | 복구 | 쓰는 곳 | | --- | --- | --- | | `L` | 약 7% | 깨끗한 실내 라벨, 긴 값 | | `M` | 약 15% | 기본값 | | `Q` | 약 25% | 물류 현장, 긁히기 쉬운 곳 | | `H` | 약 30% | 오염이 심한 곳 | ## 크기와 길이 - 한 칸(모듈)이 0.25mm 이상이면 휴대폰으로 잘 읽힙니다. 값이 길수록 칸이 많아지니 `size` 를 키우세요. - 바깥 여백(칸 2개분)은 자동으로 들어가 있습니다. - 값이 너무 길면 `QrError`(`qr_too_long`)가 나고 그 자리에 X 상자를 그립니다. 주소는 짧은 URL 로 바꾸세요. ```js Label82.qrSVG('https://82label.app', { size: 30, ecc: 'M' }); // QR 그림만 ``` --- # 인쇄 (브라우저·출력 프로그램·사일런트) > print() 의 세 가지 방식, 결과 값, 프린터 고르기, 브라우저 인쇄 창 설정. Source: https://82label.app/docs/printing/ `print(template, rows, options)` 하나로 인쇄합니다. `template` 은 서식 객체 또는 서식 ID(`'lbl_…'`)입니다. ```js const r = await Label82.print(template, rows, { mode: 'auto', // 'auto' | 'agent' | 'browser' printer: 'TSC TE210', // 출력 프로그램의 프린터 이름 (빼면 기본 프린터) copies: 1, // 행마다 몇 장 startAt: 1, // 시트: 몇 번째 칸부터 frames: true, // false: layer 'frame' 요소를 빼고 인쇄 }); ``` ## 세 가지 방식 | mode | 동작 | | --- | --- | | `auto` (기본) | PC 에 출력 프로그램이 켜져 있고 구독 계정에 연결돼 있으면 출력 프로그램으로 바로(사일런트). 아니면 브라우저 인쇄 창. | | `agent` | 출력 프로그램으로만. 없거나 연결이 안 됐으면 `AgentError` 를 던집니다(브라우저로 넘어가지 않음). | | `browser` | 항상 브라우저 인쇄 창. 출력 프로그램을 찾지 않습니다. | ## 사일런트 인쇄 출력 프로그램 경로에서는 인쇄 창이 뜨지 않습니다. 라이브러리가 라벨을 프린터 해상도(dpi)의 PNG 로 만들어 PC 의 출력 프로그램(`http://127.0.0.1:18282`)에 보내고, 출력 프로그램이 Windows 프린터 드라이버로 용지 크기 그대로(여백 0, 배율 100%) 찍습니다. - 처음 보는 사이트에서 요청이 오면 출력 프로그램이 "이 사이트의 출력을 허용할까요?" 를 묻습니다. 그동안 `print()` 는 아무것도 찍지 않고 `status: 'pending_permission'` 을 돌려줍니다. 허용한 뒤 다시 누르면 바로 나갑니다. - 출력 프로그램은 켜져 있지만 구독 계정에 연결되지 않았으면 브라우저로 인쇄하고 `notice: 'agent_license_required'` 를 붙입니다. - 출력 프로그램이 없으면 0.4초 안에 판단하고 브라우저로 넘어갑니다. ## 결과 ```js { via: 'agent' | 'browser', printed: 2, // 보낸 라벨 수 (copies 포함) status: 'done' | 'pending_permission' | 'cancelled', errors: [], // 바코드 값 오류, 글자 넘침 등 (인쇄는 계속됨) notice: 'agent_license_required', // 있을 때만 } ``` ```js const r = await Label82.print(template, rows); if (r.status === 'pending_permission') showToast('출력 프로그램 창에서 이 사이트를 허용해 주세요.'); else if (r.notice === 'agent_license_required') showToast('바로 인쇄하려면 출력 프로그램을 계정에 연결하세요.'); if (r.errors.length) console.warn(r.errors); ``` ## 프린터 목록 ```js const list = await Label82.printers(); // 출력 프로그램이 없으면 null // [{ name: 'TSC TE210', isDefault: true, dpi: 203 }, { name: 'Canon G3010', isDefault: false }] const status = await Label82.agentStatus(); // { version, allowed, licensed } 또는 null ``` ## 브라우저 인쇄 창 설정 - 배율: **100%**(실제 크기). "용지에 맞춤"은 끄세요. - 여백: **없음**. 82label 이 `@page { size: 가로mm 세로mm; margin: 0 }` 을 넣습니다. - 머리글·바닥글(날짜, 주소)은 끄세요. 크롬은 여백이 없으면 저절로 빠집니다. - 롤 라벨프린터라면 프린터의 용지 크기를 라벨 크기로 고릅니다. > 브라우저 인쇄는 사용자가 인쇄 창에서 취소해도 알 수 없어 `status` 는 `'done'` 입니다. 정확한 출력 기록이 필요하면 출력 프로그램을 쓰세요. --- # 출력 프로그램 설치·연결 > Windows 출력 프로그램 설치, 계정 키 연결, 허용 사이트, 자동 업데이트. Source: https://82label.app/docs/agent/ 82label 출력 프로그램은 Windows 트레이에서 도는 작은 프로그램입니다. 켜져 있으면 `print()` 가 인쇄 창 없이 바로 라벨프린터로 보냅니다. 처음 연결한 날부터 **30일 무료**이고, 그 뒤에는 **구독자에게 무료**입니다. 계정 키로 연결해야 동작합니다. ## 설치 1. [다운로드](https://82label.app/download/)에서 `82label-Setup-버전.exe` 를 받습니다(Windows 10·11, 64비트). 2. 실행하면 관리자 권한 없이 사용자 폴더(`%LOCALAPPDATA%\Programs\82label`)에 설치되고 트레이에 아이콘이 뜹니다. 3. Windows 시작 시 자동으로 켜집니다(트레이 메뉴에서 끌 수 있음). > "Windows 의 PC 보호" 창이 뜨면 **추가 정보 → 실행**을 누르세요. 새 프로그램은 다운로드 수가 쌓일 때까지 SmartScreen 이 경고할 수 있습니다. 설치 파일의 SHA-256 은 다운로드 페이지에 있습니다. ## 계정 연결 1. 82label.app 에 로그인해 [내 서식 › 출력 프로그램](https://82label.app/my/agent/)에서 **새 키 만들기**를 누릅니다. 키(`l82a_…`)는 이때 한 번만 보입니다. 2. 트레이 아이콘 › **계정 연결…** 에 키를 붙여 넣습니다. 3. 구독이 확인되면 트레이에 "연결됨" 이 보입니다. 연결은 12시간마다 다시 확인하고, 인터넷이 끊겨도 7일 동안은 그대로 인쇄됩니다. ## 허용 사이트 아무 웹사이트나 몰래 인쇄하지 못하도록, 출력 프로그램은 요청한 사이트(Origin)를 확인합니다. 처음 보는 사이트는 트레이 위에 "https://shop.example.com 에서 라벨 출력을 허용할까요?" 를 묻고, 허용하면 목록에 저장합니다. 82label.app 은 처음부터 허용돼 있습니다. 목록은 트레이 › 허용 사이트에서 지울 수 있습니다. ## 자동 업데이트 시작 1분 뒤와 6시간마다 새 버전을 확인합니다. 82label 의 업데이트 서명이 맞고 82label.app 에서 받은 파일의 크기·SHA-256 이 맞을 때만 조용히 설치하고 다시 켭니다. 트레이 메뉴에서 끄거나 "업데이트 확인" 을 누를 수 있습니다. ## 확인하기 ```js const s = await Label82.agentStatus(); // null → 출력 프로그램이 없거나 꺼져 있음 // { version: '0.1.0', allowed: true, licensed: true } → 바로 인쇄됨 ``` | 증상 | 확인할 것 | | --- | --- | | 계속 브라우저 창이 뜸 | 트레이에 아이콘이 있는지, "연결됨" 인지. 보안 프로그램이 `127.0.0.1:18282` 를 막는지. | | 아무것도 안 나옴 | `status: 'pending_permission'` 이면 트레이의 허용 창에서 허용. | | 위치가 밀림 | 드라이버의 용지 크기, 프린터의 라벨 감지 보정. 배율은 항상 100% 로 보냅니다. | --- # 엑셀로 대량 출력 > 코드 없이 엑셀·CSV 를 올리고 칸을 맞춰 행마다 라벨을 찍습니다. 수량 열, 시작 칸, PDF 저장. Source: https://82label.app/docs/bulk/ [대량 출력](https://82label.app/print/) 화면에서 서식을 고르고 엑셀 파일을 올리면 행마다 라벨이 나옵니다. 로그인 없이 샘플 서식, 디자이너에서 만들던 서식, JSON 서식 파일로도 됩니다. 파일은 브라우저 안에서만 읽고 서버로 보내지 않습니다. - **서식**: 샘플(쿠팡 로켓그로스 바코드 등), 내 서식, 디자이너에서 만들던 서식, JSON 파일, 빈 롤 라벨. 주소로 바로 열기: `/print/?id=…`, `?sample=coupang-50x30`, `?preset=formtec-3108`, `?draft=1`. - **파일**: xlsx(첫 시트), csv(UTF-8 이나 EUC-KR, 쉼표·세미콜론·탭 자동 구분), 탭으로 나뉜 txt. 엑셀에서 표를 복사해 Ctrl+V 로 붙여넣어도 됩니다. 예전 xls 는 엑셀에서 xlsx 로 저장해 주세요. - **칸 맞추기**: 같은 이름, 띄어쓰기·대소문자·괄호를 뺀 이름, 비슷한 말(바코드↔barcode·sku·상품바코드, 상품명↔name·product·노출상품명, 수량↔qty·quantity)로 자동으로 맞춥니다. 고른 짝은 서식마다 이 브라우저에 기억합니다. - **수량 열**: 이 열의 숫자만큼 같은 라벨을 찍습니다(0이면 건너뜀, 최대 9,999). 행 표에서 행마다 고칠 수도 있습니다. - **행 고르기**: 찾기, 전체 선택·해제, 행마다 체크. - **미리보기**: 실제 값으로 그린 페이지. 시트는 시작 칸부터 채우고 빈 칸을 점선으로 보여 줍니다. - **인쇄**: 출력 프로그램이 있으면 인쇄 창 없이, 없으면 브라우저 인쇄. [PDF 저장](https://82label.app/docs/pdf/)도 됩니다. ## 쿠팡 로켓그로스 바코드 라벨 쿠팡 Wing 에서 받은 상품 바코드 엑셀의 `상품명`·`옵션명`·`바코드` 열이 샘플 서식 `coupang-50x30`(50×30 롤), `coupang-a4-20`·`coupang-a4-40`(A4 시트)의 필드 이름과 같아 올리면 바로 맞습니다. 입고 수량 열을 수량 열로 고르세요. ## 코드로 같은 일 하기 ```js import { print } from '@82label/core'; // rows: [{ 상품명, 옵션명, 바코드, 수량 }, …] await print('lbl_…', rows, { qtyField: '수량', startAt: 3, serialStart: 1 }); ``` > 라벨이 몇 mm 씩 밀려 나오면 [인쇄 보정](https://82label.app/calibrate/)에서 한 번 맞춰 두세요. --- # 인쇄 보정 > 프린터마다 몇 mm 씩 밀리는 위치와 배율을 한 번 재서 저장하면 인쇄할 때마다 적용됩니다. Source: https://82label.app/docs/calibration/ 같은 서식도 프린터마다 종이를 무는 위치가 달라 1~2 mm 밀릴 수 있습니다. [인쇄 보정](https://82label.app/calibrate/) 화면에서 시험 인쇄 한 장을 자로 재서 X·Y(0.1 mm 단위)와 배율(%)을 저장하세요. - 시험 인쇄에는 테두리, 가장자리 눈금(1 cm 마다), 가운데 십자, 크기 글자가 찍힙니다. - 오른쪽으로 밀려 있으면 X 를 −, 아래로 밀려 있으면 Y 를 − 로. 10 cm 가 9.8 cm 로 나오면 배율 102%. - 보정은 이 브라우저에 경로마다 저장됩니다: 브라우저 인쇄 하나, 출력 프로그램은 프린터마다 하나. - 저장한 보정은 대량 출력, 디자이너 인쇄, 내 사이트에서 부르는 `print()` 에 모두 적용됩니다(같은 브라우저·같은 사이트 주소). ## 코드에서 ```js import { print, calibrationKey, getCalibration, setCalibration, calibrationTemplate } from '@82label/core'; setCalibration(calibrationKey('agent', 'TSC TE210'), { offsetX: -1.2, offsetY: 0.5, scale: 1.01 }); await print(t, rows); // 저장한 보정 적용 await print(t, rows, { calibration: { offsetX: 0.8 } }); // 이번만 이 값 await print(t, rows, { calibration: false }); // 보정 없이 await print(calibrationTemplate(t)); // 시험 인쇄 ``` > 보정은 라벨 한 장 안의 내용을 옮기고 키웁니다(라벨 왼쪽 위 기준). 폼텍 시트 전체가 밀린다면 먼저 인쇄 창의 배율 100%·여백 없음을 확인하세요. --- # PDF 저장 > mm 크기 그대로의 PDF. 바코드·QR·선은 벡터라 확대해도 선명합니다. Source: https://82label.app/docs/pdf/ 인쇄할 PC 가 따로 있거나 인쇄소에 맡길 때는 PDF 로 저장하세요. 페이지 크기는 서식 용지 그대로(롤은 라벨 한 장, 시트는 A4)이고, 바코드 막대·QR·선·박스는 벡터, 글자와 그림은 600dpi 그림으로 들어갑니다. ```js import { toPDF, downloadPDF } from '@82label/core'; const blob = await toPDF(t, rows, { startAt: 3, qtyField: '수량' }); // Blob (application/pdf) await downloadPDF(t, rows, '라벨.pdf'); // 바로 내려받기 ``` - 옵션: `startAt`, `frames`, `serialStart`, `now`, `qtyField`, `textDpi`(기본 600). - 브라우저에서만 됩니다(글자를 canvas 로 그립니다). 서버에서 부르면 `PdfUnsupported` 를 던집니다. - [대량 출력](https://82label.app/print/)과 디자이너의 "PDF 저장" 버튼이 같은 함수를 씁니다. > PDF 를 인쇄할 때도 인쇄 창의 배율은 100%(실제 크기)로 두세요. --- # 웹 컴포넌트 > 태그 하나로 미리보기와 인쇄 버튼. 속성, 프로퍼티, 이벤트. Source: https://82label.app/docs/element/ CDN 스크립트를 넣으면 `` 가 등록됩니다. 첫 번째 행으로 미리보기를 그리고, 버튼을 누르면 모든 행을 인쇄합니다. 프레임워크 없이 서버에서 HTML 을 만드는 화면(PHP, JSP, ASP)에 잘 맞습니다. ```html ``` ## 속성 | 속성 | 설명 | | --- | --- | | `template` | 서식 ID(`lbl_…`) 또는 서식 JSON 문자열. | | `data` | JSON 객체 하나 또는 배열. 배열의 항목마다 라벨 한 장. | | `printer` | 출력 프로그램의 프린터 이름. | | `mode` | `auto`(기본) · `agent` · `browser` | | `start-at` | 시트: 몇 번째 칸부터. | | `button` | 버튼 글자. 기본 "인쇄". | | `preview` | `false` 면 미리보기를 숨기고 버튼만. | | `frames` | `false` 면 미리 인쇄된 틀(`layer: 'frame'`)을 빼고 인쇄. | | `base` | 서식 ID 를 받을 서버(기본 https://82label.app). | ## 스크립트에서 ```js const el = document.querySelector('label-82'); el.template = template; // 객체를 그대로 el.data = orders.map(toRow); // 배열 el.addEventListener('printed', (e) => console.log(e.detail)); // PrintResult el.addEventListener('error', (e) => console.error(e.detail)); await el.print(); // 버튼 없이 바로 ``` ## 모양 바꾸기 `82label.css` 가 미리보기 틀과 버튼을 꾸밉니다. CSS 변수로 바꾸세요. 라벨 그림 자체는 종이라서 늘 흰 바탕입니다. ```css label-82 { --l82-accent: #d9431f; /* 버튼 */ --l82-accent-ink: #fff; --l82-bg: #fafaf9; /* 미리보기 틀 */ --l82-border: #e4e4e0; --l82-radius: 8px; --l82-font: Pretendard, sans-serif; } ``` 안내 문구(허용 대기, 구독 필요 등)는 버튼 아래 `.l82-notice` 에 나옵니다. 다른 이름의 태그가 필요하면 `Label82.defineLabelElement('shop-label')`. --- # React · Vue > @82label/core 를 React·Vue 에서 쓰는 예제. Source: https://82label.app/docs/frameworks/ `print()` 는 그냥 함수라 어느 프레임워크에서도 그대로 부르면 됩니다. React·Vue 전용 패키지는 준비 중이고, 아래처럼 코어만으로 충분합니다. ## React ```js import { useState } from 'react'; import { print } from '@82label/core'; import template from './labels/product.json'; export function PrintLabelButton({ items }) { const [busy, setBusy] = useState(false); const onClick = async () => { setBusy(true); try { const rows = items.map((it) => ({ name: it.name, price: it.price, sku: it.sku })); const r = await print(template, rows); if (r.status === 'pending_permission') alert('출력 프로그램에서 이 사이트를 허용해 주세요.'); } finally { setBusy(false); } }; return ; } ``` ## 미리보기 (React) ```js import { renderToSVG } from '@82label/core'; export function LabelPreview({ template, row }) { // renderToSVG 는 모든 값을 이스케이프하므로 그대로 넣어도 안전합니다 return
; } ``` ## Vue ```vue ``` ## 웹 컴포넌트로 `` 는 표준 웹 컴포넌트라 React 19·Vue 3 에서 태그로 써도 됩니다. Vue 는 `compilerOptions.isCustomElement` 에 `label-82` 를 알려 주세요. ```js import { defineLabelElement } from '@82label/core'; import '@82label/core/styles.css'; defineLabelElement(); // React 19 ``` > SSR(Next.js, Nuxt)에서는 `print()` 를 브라우저 이벤트 안에서만 부르세요. `renderToSVG` 는 서버에서도 됩니다. --- # API 레퍼런스 > @82label/core(전역 Label82)가 내보내는 모든 함수와 값. Source: https://82label.app/docs/api/ 아래는 `import { … } from '@82label/core'` 와 CDN 의 전역 `Label82` 에 똑같이 들어 있습니다. ## 인쇄 | 이름 | 설명 | | --- | --- | | `print(template, rows?, options?)` | 서식(객체 또는 ID)과 데이터로 인쇄. `Promise`. [인쇄](https://82label.app/docs/printing/) 참고. | | `printers()` | 출력 프로그램의 프린터 목록 `{ name, isDefault, dpi? }[]`, 없으면 `null`. | | `agentStatus()` | `{ version, allowed, licensed }` 또는 `null`. | | `printViaBrowser(svgs, size)` | 페이지 SVG 목록을 브라우저 인쇄 창으로(`size` = `{ width, height }` mm). | | `rasterize(svg, widthMm, heightMm, dpi)` | SVG 를 dpi 해상도 PNG `Blob` 으로. | | `AgentError` | `mode: 'agent'` 실패. `code`: unavailable · denied · license_required · failed. | | `toPDF(template, rows?, options?)` | 라벨 PDF `Blob`. 바코드·QR·선은 벡터. 브라우저 전용. [PDF 저장](https://82label.app/docs/pdf/) 참고. | | `downloadPDF(template, rows?, filename?, options?)` | PDF 를 만들어 바로 내려받습니다. | | `calibrationKey(via, printer?)` | 보정 저장 키: `'browser'` 또는 `'agent:프린터'`. | | `getCalibration(key) / setCalibration(key, cal \| null)` | 이 브라우저에 저장한 보정 `{ offsetX, offsetY, scale }`. [인쇄 보정](https://82label.app/docs/calibration/) 참고. | | `calibrationTemplate(template)` | 같은 용지의 시험 인쇄 서식(테두리, 1 cm 눈금, 십자, 크기). | | `AGENT_PORTS` | 출력 프로그램이 듣는 포트 후보 `[18282, 18283, 18284]`. | ## 저장한 서식 | 이름 | 설명 | | --- | --- | | `loadTemplate(id, base?)` | `https://82label.app/api/t/` 에서 서식을 받습니다. 같은 ID 는 페이지에서 한 번만 받습니다. | | `loadCourierTemplate(id, base?)` | 택배 송장 기본서식(구독자, 82label.app 로그인 쿠키 필요). | | `clearTemplateCache()` | 받아 둔 서식을 비웁니다. | | `TemplateNotFound` | 없는 ID, 지운 서식, 네트워크 오류, 잘못된 JSON. | | `PaymentRequired` | 402: 결제가 멈춘 유료 서식 또는 구독이 필요한 택배 서식. | ## 그리기 | 이름 | 설명 | | --- | --- | | `renderToSVG(template, row?, options?)` | 라벨 한 장의 SVG 문자열(`row` 가 없으면 예시값). DOM 불필요. | | `renderLabelSVG(template, row?, options?)` | `{ svg, errors }`. | | `renderPageSVG(template, page, rows, options?)` | 한 페이지(시트 한 장 또는 롤 한 장)의 `{ svg, errors }`. | | `barcodeSVG(value, { format, height, moduleWidth, showText, codeSet })` | 라벨 없이 바코드 그림만. | | `qrSVG(value, { ecc, size })` | 라벨 없이 QR 그림만. | | `measureText(text, sizeMm, font?, bold?)` | 글자 폭(mm). 브라우저에서는 canvas 로 잽니다. | | `layoutPages(template, count, startAt?)` | 라벨 `count` 장의 페이지와 칸 좌표 `Page[]`. | | `pageSize(template)` | 인쇄 페이지 크기 `{ width, height }` mm. | ## 바코드·QR | 이름 | 설명 | | --- | --- | | `encodeBarcode(format, value, { codeSet? })` | 막대 모듈 배열과 사람이 읽는 글자. 잘못된 값이면 `BarcodeError`. | | `eanCheckDigit(digits)` | EAN·UPC 체크 숫자. | | `mod7Check(digits)` | 7로 나눈 나머지(일부 택배사 송장번호 검사). | | `encodeQR(value, ecc?)` | QR 모듈 행렬. 너무 길면 `QrError`. | | `BarcodeError, QrError` | 값 오류. | ## 필드 | 이름 | 설명 | | --- | --- | | `extractFields(template)` | 서식에 쓰인 `{{이름}}` 목록. | | `fill(text, row, ctx?)` | `{{이름}}`·`{{이름\|서식}}`·`{{seq}}`·`{{today}}` 을 값으로 바꾼 글자. `ctx` = `{ index, serialStart, now }`. | | `FIELD_FORMATS` | 값 서식 목록 `{ id, label, example }[]`. [값 서식](https://82label.app/docs/formats/) 참고. | | `exampleRow(template)` | `fields[].example` 로 만든 예시 행. | | `escapeXml(text)` | SVG·HTML 에 넣을 수 있게 이스케이프. | ## 용지와 단위 | 이름 | 설명 | | --- | --- | | `FORMTEC` | 폼텍 A4 시트 규격 목록 `SheetSpec[]`(코드, 칸, 크기, 여백, 확인 여부, 출처). | | `ROLL_SIZES` | 자주 쓰는 롤 크기 `{ w, h, label, use }[]`. | | `COURIERS` | 택배 서식 정보(공개 패키지에서는 빈 목록, 사이트가 채움). | | `presetTemplate('roll', w, h) / presetTemplate('formtec', id)` | 빈 서식. | | `SAMPLES / sampleTemplate(id)` | 무료 시작 서식: 쿠팡 로켓그로스 바코드(50×30, 60×40, 70×40, A4 21·40칸), 표시사항, 가격표, 자산 번호. | | `sheetPaper(id)` | 폼텍 규격의 `paper` 객체. | | `mmToPx(mm, dpi?), mmToDots(mm, dpi), ptToMm(pt), MM_PER_PT` | 단위 변환. | ## 기타 | 이름 | 설명 | | --- | --- | | `defineLabelElement(tag?)` | ``(또는 다른 이름) 등록. CDN 빌드는 자동. | | `VERSION` | 라이브러리 버전 문자열. | ## 타입 ```ts type Template = { v: 1; name: string; paper: RollPaper | SheetPaper; label: { width; height; radius? }; elements: Element[]; fields?: FieldDef[] }; type RollPaper = { kind: 'roll'; gap?; dpi?: 203 | 300 | 600; direction?: 0 | 90 | 180 | 270 }; type SheetPaper = { kind: 'sheet'; preset?; page: 'A4' | 'Letter' | { width; height }; cols; rows; marginTop; marginLeft; pitchX; pitchY; shape?: 'rect' | 'circle' | 'ring'; innerDiameter? }; type Row = Record; type PrintOptions = { printer?; copies?; startAt?; mode?: 'auto' | 'agent' | 'browser'; dpi?; frames?; base?; serialStart?; now?: Date; calibration?: Calibration | false; qtyField? }; type Calibration = { offsetX?; offsetY?; scale? }; // mm, mm, 1 = 100% type PrintResult = { via: 'agent' | 'browser'; printed; status: 'done' | 'pending_permission' | 'cancelled'; errors: RenderError[]; notice?: 'agent_license_required' }; type RenderError = { element: number; code: 'barcode_invalid' | 'qr_too_long' | 'overflow' | 'image_failed'; message: string }; ``` --- # 서버 보관과 요금 > 직접 쓰면 무료, 82label 에 보관하는 서식은 3개까지 무료, 그 뒤로 서식당 월 5,000원. Source: https://82label.app/docs/pricing/ 82label 을 쓰는 방법은 두 가지입니다. 어느 쪽이든 라이브러리, 디자이너, 브라우저 인쇄는 무료입니다. | | 서식을 코드에 넣기 | 82label 에 보관하고 ID 로 부르기 | | --- | --- | --- | | 방법 | 디자이너에서 JSON 을 받아 내 코드·파일에 둡니다. | 디자이너에서 저장하고 `print('lbl_…', rows)` 로 부릅니다. | | 고칠 때 | 내 코드를 고쳐 다시 배포 | 82label.app 에서 고치면 바로 반영 | | 82label 서버 | 필요 없음 | 인쇄할 때 서식을 받아 옴 | | 요금 | 무료 | 서식 3개까지 무료, 넘는 서식은 서식당 월 5,000원 | ## 구독하면 더 쓸 수 있는 것 - 보관 서식 3개 초과분(서식당 월 5,000원, 부가세 포함) - [택배 송장 기본서식](https://82label.app/docs/couriers/) - [Windows 출력 프로그램](https://82label.app/docs/agent/)(사일런트 인쇄) ## 결제 - 토스페이먼츠 카드 정기결제. 처음 등록한 날과 같은 날짜에 매달 자동으로 결제됩니다. - 금액은 **유료 서식 수 × 5,000원**(부가세 포함). 유료 서식이 없어도 구독 중이면 최소 1개분입니다. - 달 중간에 서식을 늘리면 그달은 그대로, 다음 결제부터 반영됩니다(일할 계산 없음). - 카드 명세서에는 **82label 서식 보관 (월 정기)** 로 찍힙니다. - 해지는 [내 서식 › 결제](https://82label.app/my/billing/)에서 언제든 할 수 있고, 이미 낸 기간이 끝날 때까지 그대로 씁니다. - 결제가 실패하면 3일 동안 다시 시도합니다. 그래도 안 되면 처음 3개를 뺀 서식의 ID 호출이 멈춥니다(지우지 않음, 결제하면 다시 동작). - 환불 기준은 [이용약관 › 정기결제와 환불](https://82label.app/terms/#refund)에 있습니다. ## 멈춘 서식 결제가 멈추면 처음 만든 3개(만든 순서)는 계속 동작하고, 나머지는 `GET /api/t/:id` 가 402 `payment_required` 를 돌려줍니다. 라이브러리는 `PaymentRequired` 를 던지고, `` 는 "이 서식은 82label 구독이 필요해요" 를 보여 줍니다. > 정식 출시 전에는 모든 기능이 무료이고 결제 화면이 닫혀 있습니다. --- # 자주 묻는 질문 > 요금, 프린터, 폼텍 칸 밀림, 출력 프로그램, 보안. Source: https://82label.app/docs/faq/ ## 정말 무료인가요? 라이브러리(MIT), 디자이너, JSON 내보내기, 브라우저 인쇄, 롤·폼텍 서식은 무료입니다. 82label 서버에 서식을 3개 넘게 보관하거나 택배 송장 서식·Windows 출력 프로그램을 쓰려면 구독(서식당 월 5,000원, 부가세 포함)이 필요합니다. 정식 출시 전에는 모두 무료입니다. ## 어떤 프린터를 쓸 수 있나요? Windows 에 드라이버가 설치된 프린터는 모두 됩니다. 감열 라벨프린터(TSC, Zebra, 빅솔론, 세우테크 등), 일반 레이저·잉크젯(폼텍 시트) 모두 같은 그림을 mm 크기 그대로 찍습니다. ZPL 같은 프린터 명령어는 아직 쓰지 않습니다. ## 인쇄 창 없이 바로 찍을 수 있나요? Windows 출력 프로그램을 설치하고 구독 계정에 연결하면 `print()` 가 인쇄 창 없이 바로 찍습니다. 출력 프로그램이 없으면 같은 코드가 브라우저 인쇄 창을 엽니다. ## 폼텍 시트에서 칸이 밀려요. 인쇄 창의 배율을 100%(실제 크기)로, "용지에 맞춤"을 끄고, 여백을 없음으로 두세요. 그래도 프린터마다 1~2mm 밀린다면 [인쇄 보정](https://82label.app/calibrate/)에서 시험 인쇄 한 장을 재서 맞춰 두세요. 다음 인쇄부터 자동으로 적용됩니다. ## 코드 없이 엑셀로 찍을 수 있나요? 네. [대량 출력](https://82label.app/print/)에서 서식을 고르고 엑셀·CSV 를 올린 뒤 칸을 맞추면 됩니다. 쿠팡 로켓그로스 바코드 엑셀은 샘플 서식과 열 이름이 같아 바로 맞습니다. 로그인 없이 되고, 파일은 서버로 보내지 않습니다. ## 바코드가 스캐너로 안 읽혀요. 높이를 10mm 이상, 양옆 여백을 충분히 두고, 서식의 dpi 를 프린터와 맞추세요(대부분 203). 값이 길면 바코드 칸을 넓히거나 Code 128 숫자 전용(C 세트)이 되도록 숫자만 쓰세요. ## 다른 사이트가 몰래 내 프린터로 인쇄할 수 있나요? 아니요. 출력 프로그램은 요청한 사이트를 확인해 처음 보는 사이트는 허용 여부를 묻고, 허용한 사이트만 인쇄합니다. 출력 프로그램은 내 PC(127.0.0.1)에서만 요청을 받습니다. ## 서식 ID 를 알면 남이 제 서식을 고칠 수 있나요? 아니요. ID 로는 읽기만 됩니다. 고치기와 지우기는 로그인한 주인만 할 수 있습니다. 서식에 비밀 정보를 넣지 마세요. 값은 인쇄할 때 데이터로 넘기면 됩니다. ## 택배사 송장번호도 발급해 주나요? 아니요. 송장번호 발급과 집하 접수는 택배사 계약과 각 사의 API 가 필요합니다. 82label 은 받은 송장번호와 주소를 송장 용지의 정해진 위치에 찍어 줍니다. ## Mac 이나 리눅스에서도 되나요? 브라우저 인쇄는 어느 운영체제에서나 됩니다. 사일런트 인쇄용 출력 프로그램은 지금은 Windows 10·11(64비트)만 있습니다.