karsh-barkod
- Language
- TypeScript
- Stars
- 0
- Forks
- 0
- Updated
- License
- MIT
Topics: barcodetypescriptzpl
Open on GitHub ↗README
karsh-barkod
Code 128, EAN-13, EAN-8 ve UPC-A'yı elle kodlayan tek bir TypeScript dosyası: modül genişlikleri, GS1 kontrol hanesi, etiket yerleşimi, SVG geometrisi ve TSPL/ZPL yazıcı komutları.
Code 128, EAN-13, EAN-8 and UPC-A encoded by hand, with the GS1 check digit, whole-dot label geometry, an SVG writer and the two label-printer dialects.
930 satır · sıfır import · DOM yok · MIT 930 lines · zero imports · no DOM · MIT
Türkçe
Ne işe yarar
Bir değeri alır, basılacak simgeyi verir — ve simgenin etikete kadar giden yolundaki her şeyi:
- Dört simgeleme. Code 128 (A/B/C kümeleri arasında en kısa yolu bulan bir kodlayıcıyla), EAN-13, EAN-8, UPC-A. Her çubuk modül cinsinden, koruma çubukları işaretli, okunur rakam satırı yerleştirilmiş halde.
- GS1 kontrol hanesi. GTIN-8/12/13/14 ve SSCC için mod-10; eksikse hesaplar, tamsa doğrular, hesabı adım adım saklar.
- Tam sayı geometri. Her çubuk nokta (dot) cinsinden, sessiz bölgeler dahil. Yarım nokta genişliğinde çubuk olmaz: yazıcı onu ya bir nokta basar ya hiç.
- SVG. Tüm çubuklar tek bir yol (path);
viewBoxnokta cinsinden, en ve boy milimetre cinsinden — sayfa programı simgeyi yazıcının basacağı boyutta yerleştirsin diye. - PNG çözünürlüğü. Var olan bir PNG'ye
pHYschunk'ı yazar, böylece Word ve baskı iletişim kutusu dosyayı 96 dpi sanmaz. - Yazıcı lehçeleri. TSPL (TSC) ve ZPL (Zebra) komutları; yerleşim, ortalama, taşma denetimi ve her lehçenin kendi kaçışlarıyla.
Bir kütüphanenin gizleyeceği şey tam da burada bilinmesi gereken şey: simge kaç modül genişliğinde, hangi çubuk nerede, hangi karakterde yazıcı boğulur. Etiket o genişlikten ortalanır ve önizleme, yazıcının basacağı çubukları çizer.
Kurulum
Paket TypeScript kaynağı olarak dağıtılır; derleme adımı yoktur.
npm i github:HasanKarsi/karsh-barkod
Kaynak TypeScript olduğu için tüketen tarafın TS'i çözebilmesi gerekir:
Vite, esbuild, webpack ya da Next gibi bir paketleyici. Next'te ayrıca
next.config dosyana transpilePackages: ["karsh-barkod"] eklemen gerekir: Next
node_modules içindeki ham TypeScript'i kendiliğinden derlemez. Düz node
paketi olduğu gibi çalıştıramaz.
Ya da src/barkod.ts dosyasını projene kopyala: tek dosya, hiç import
satırı yok, lisansı MIT.
Kullanım
Bir EAN-13 kodla. On iki hane ver, kontrol hanesini o hesaplasın:
import { buildBarcode } from "karsh-barkod";
const result = buildBarcode("ean13", "869123456789");
if (result.ok) {
result.note; // { code: "computed", digit: 0 }
result.barcode.data; // "8691234567890" — kontrol hanesi dahil
result.barcode.modules; // 95
result.barcode.quiet; // [11, 7] — soldaki ve sağdaki sessiz bölge
result.barcode.bars; // 30 çubuk, modül cinsinden
}
On üç hane verilirse kontrol hanesi doğrulanır; tutmuyorsa hata döner ve düzeltilmez: yanlış bir kontrol hanesi genellikle başka bir yerdeki yanlış haneyi işaret eder, sonuncuyu "düzeltmek" yakalanmış bir hatayı başkasına ait geçerli görünen bir numaraya çevirirdi.
buildBarcode("ean13", "8691234567891");
// { ok: false, problem: { code: "checkDigit", given: 1, expected: 0, body: 12 } }
Geometri ve SVG. Nokta cinsinden yerleşim, milimetre cinsinden ölçü:
import { layoutBarcode, barcodeSvg, DOTS_PER_MM } from "karsh-barkod";
const geometry = layoutBarcode(result.barcode, {
module: 3, // modül genişliği, tam nokta
height: 120, // çubuk boyu, nokta
showText: true,
});
geometry.width; // 339
geometry.height; // 147 — rakam satırı dahil
geometry.left; // 33 — ilk çubuğa kadarki sessiz bölge
barcodeSvg(geometry, DOTS_PER_MM[300]).split("\n")[0];
// <svg ... width="28.25mm" height="12.25mm" viewBox="0 0 339 147" shape-rendering="crispEdges">
Etiket komutu üret. Code 128 değerlerinden bir Zebra işi:
import { buildBarcode, recommendedModule, toZpl } from "karsh-barkod";
const built = ["KRS-0001", "KRS-0002"].map((v) => buildBarcode("code128", v));
const barcodes = built.flatMap((r) => (r.ok ? [r.barcode] : []));
const zpl = toZpl(barcodes, {
widthMm: 50,
heightMm: 30,
gapMm: 2,
dpi: 203,
quantity: 1,
flip: false,
module: recommendedModule("code128", 203), // 2 nokta
barHeightMm: 15,
showText: true,
position: null, // ortala
});
zpl.skipped; // [] — etikete sığmayan ya da lehçeye yazılamayan değerler
zpl.code ilk etiket için şunu verir:
^XA
^PW400
^LL240
^LH0,0
^PON
^FO88,49^BY2^BCN,120,Y,N,N,A^FDKRS-0001^FS
^PQ1
^XZ
Aynı ayarlarla toTspl bir TSC işi yazar:
SIZE 50 mm,30 mm
GAP 2 mm,0 mm
DIRECTION 1
REFERENCE 0,0
CLS
BARCODE 88,49,"128",120,2,0,2,2,"KRS-0001"
PRINT 1
Kontrol hanesini göster. Boşluk, tire ve nokta atılır; hesap adım adım döner, ekranda gösterilebilsin diye:
import { checkGs1 } from "karsh-barkod";
const gs1 = checkGs1("gtin13", "869 1234 56789");
// status: "computed", full: "8691234567890", expected: 0, sum: 130
// steps: 12 adım, her biri { digit, weight, product }
API
Kodlama
| Dışa aktarım | İmza | Ne yapar |
|---|---|---|
buildBarcode |
(symbology: Symbology, input: string) => BarcodeResult |
Değeri doğrular, kodlar, çubukları ve okunur rakamları verir |
encodeCode128 |
(text: string) => number[] |
En kısa Code 128 simge dizisi; encodeCode128("KRS-0001") → [104, 43, 50, 51, 13, 99, 0, 1, 27, 106] |
printable |
(text: string) => string |
Kontrol karakterlerini Unicode denetim resimlerine çevirir, okunur satır ve SVG geçerli kalsın diye |
foldToAscii |
(text: string) => string |
Türkçe harfleri ve bölünmez boşluğu düz ASCII'ye indirir; "ÜRÜN-Çİğ" → "URUN-CIg" |
GS1
| Dışa aktarım | İmza | Ne yapar |
|---|---|---|
checkGs1 |
(key: Gs1Key, input: string) => Gs1Result |
Eksikse hesaplar, tamsa doğrular; her hane, ağırlığı ve toplam dönen değerde |
Geometri ve çizim
| Dışa aktarım | İmza | Ne yapar |
|---|---|---|
layoutBarcode |
(barcode: Barcode, options: LayoutOptions) => Geometry |
Tam nokta yerleşim: çubuk dikdörtgenleri, metin satırları, sessiz bölge |
barsPath |
(bars: Rect[]) => string |
Bütün çubukları tek bir SVG yolu olarak yazar |
barcodeSvg |
(geometry: Geometry, dotsPerMm: number) => string |
Tek başına duran SVG: viewBox nokta, en/boy milimetre |
pngWithResolution |
(png: Uint8Array, dotsPerMm: number) => Uint8Array |
Var olan bir PNG'ye pHYs chunk'ı yazar; PNG üretmez |
Etiket ve yazıcı
| Dışa aktarım | İmza | Ne yapar |
|---|---|---|
DOTS_PER_MM |
Record<Dpi, number> |
{ 203: 8, 300: 12, 600: 24 } — iki kılavuzun da yazdığı yuvarlama |
recommendedModule |
(symbology: Symbology, dpi: Dpi) => number |
Varsayılan modül, tam nokta: her çözünürlükte aynı fiziksel genişlik |
minimumModule |
(symbology: Symbology, dpi: Dpi) => number |
Tabanı hâlâ tutan en ince modül; altı uyarıyı hak eder |
placeBarcode |
(barcode: Barcode, settings: LabelSettings) => Placement |
Simgeyi etikete yerleştirir, ortalar, taşmayı bildirir |
toTspl |
(barcodes: Barcode[], settings: LabelSettings) => DialectOutput |
TSC yazıcı işi; yazılamayan değerler skipped içinde |
toZpl |
(barcodes: Barcode[], settings: LabelSettings) => DialectOutput |
Zebra yazıcı işi; yazılamayan değerler skipped içinde |
Tipler
Symbology ("code128", "ean13", "ean8", "upca"), Barcode, Bar,
HumanText, BarcodeResult, BarcodeProblem, BarcodeNote, Gs1Key
("gtin8", "gtin12", "gtin13", "gtin14", "sscc"), Gs1Result,
Gs1Step, Geometry, Rect, TextRun, LayoutOptions, Dpi (203,
300, 600), LabelSettings, Placement, DialectOutput,
DialectProblem.
Sınırlar ve kararlar
- Hiçbir şey fırlatmaz. Her giriş hatası
{ ok: false, problem }ya da birstatusalanı olarak döner; mesajı çağıran yazar, paket metin üretmez. - Kontrol hanesi düzeltilmez. Yanlışsa hata döner, doğrusu söylenir, ama numara sessizce değiştirilmez.
- Code 128 en fazla 80 karakter. Daha uzağını hiçbir el terminali güvenle okumuyor; GS1 zaten 48'de duruyor.
DOTS_PER_MMnominal dpi değil. 300 dpi aslında 11,81 nokta/mm'dir; yazıcılar 12'ye yuvarlar. Komutlar, SVG ve PNG aynı yuvarlamayı kullanır ki üçü aynı boyda çıksın.- Taşan etiket basılmaz, atlanır. Çubukları etiketten taşan ya da
koordinatı eksiye düşen bir değer komuta hiç girmez ve
skippediçinde gerekçesiyle bildirilir — yarısı kesik bir barkod, hiç barkod olmamasından kötüdür. - Lehçe kaçışları yazıcının kendi kuralı. ZPL'de basılamayan karakterler
^FHile onaltılık kaçışa çevrilir; TSPL'de veri içindeki çift tırnak kılavuzun verdiği kaçışla yazılır, kontrol karakteri taşıyan bir değer ise hiç yazılmaz — tırnaklı bir dizede kaçışı yoktur ve yazıcı onu komut sanabilir. - PNG üretilmez.
pngWithResolutionvar olan bir PNG'nin baytlarına çözünürlük yazar; kodlayıcı değildir.
Neden kütüphane değil, elle yazıldı
Simgelemeler küçük ve sabit tablolar. Bir kütüphanenin gizleyeceği şey ise tam olarak bu aracın bilmesi gereken şey: simge kaç modül genişliğinde, hangi çubuk nerede duruyor, hangi karakterde yazıcı boğuluyor. Etiketi o genişlikten ortalayabilmek ve önizlemede yazıcının basacağı çubukları gösterebilmek için kodlayıcının içini görmek gerekiyordu.
Code 128 kodlayıcısı bunun en somut örneği. Yaygın kestirme kural — "dört ve üzeri hane varsa C kümesine geç" — çoğu zaman doğru, geri kalanında bir iki modül uzun. Burada onun yerine bir en kısa yol araması var: her konum ve her küme için, oraya kadarki en ucuz kodlama. Sonuç yapısı gereği en kısa, ve etiketin ortalandığı genişlik gerçekten basılan genişlik oluyor. Eşit uzunluktaki yollar arasındaki tercihler de Zebra'nın otomatik kipinin çıktısına bakılarak ayarlandı, önizleme yazıcıyla aynı çubukları göstersin diye.
Dosyada hiç import yok ve DOM'a dokunmuyor; bu yüzden her tablo, düz bir
Node betiğinde bir referans kodlayıcıya karşı doğrulanabiliyor.
Lisans
MIT — bkz. LICENSE. © 2026 Hasan Karşı / KARSH.
English
What it does
Takes a value and gives back the symbol to be printed — and everything that symbol needs on its way to a label:
- Four symbologies. Code 128 (with an encoder that finds the shortest path through code sets A, B and C), EAN-13, EAN-8, UPC-A. Every bar in module units, guard bars marked, the human-readable line laid out.
- The GS1 check digit. Mod-10 for GTIN-8/12/13/14 and SSCC: computed when one digit is missing, verified when the number is complete, with the working kept step by step.
- Whole-dot geometry. Every bar in printer dots, quiet zones included. There is no half-dot bar: a printer prints it as one dot or as none.
- SVG. All bars as a single path; the
viewBoxin dots and the width and height in millimetres, so a layout program places the symbol at the size the printer would print it. - PNG resolution. Writes a
pHYschunk into an existing PNG, so Word and the print dialog stop assuming 96 dpi. - Printer dialects. TSPL (TSC) and ZPL (Zebra) commands, with placement, centring, overflow checks and each dialect's own escapes.
What a library would hide is exactly what this has to know: how many modules wide the symbol is, where each bar sits, which characters a printer will choke on. The label is centred from that width, and the preview draws the bars the printer will draw.
Install
The package ships as TypeScript source; there is no build step.
npm i github:HasanKarsi/karsh-barkod
Because it ships as TypeScript, whatever consumes it has to resolve TS: a
bundler such as Vite, esbuild, webpack or Next. On Next you also need
transpilePackages: ["karsh-barkod"] in your next.config: Next does not compile raw
TypeScript inside node_modules on its own. Plain node cannot run the
package as it ships.
Or copy src/barkod.ts into your project — one file, not a single import
line in it, MIT.
Usage
Encode an EAN-13. Give twelve digits and let it compute the check digit:
import { buildBarcode } from "karsh-barkod";
const result = buildBarcode("ean13", "869123456789");
if (result.ok) {
result.note; // { code: "computed", digit: 0 }
result.barcode.data; // "8691234567890" — check digit included
result.barcode.modules; // 95
result.barcode.quiet; // [11, 7] — the quiet zones, left and right
result.barcode.bars; // 30 bars, in module units
}
Give thirteen digits and the check digit is verified instead. A wrong one is an error and is not corrected: a wrong check digit usually means a mistyped digit somewhere else, and "fixing" the last one would turn a caught typo into a valid-looking number that belongs to someone else.
buildBarcode("ean13", "8691234567891");
// { ok: false, problem: { code: "checkDigit", given: 1, expected: 0, body: 12 } }
Geometry and SVG. Laid out in dots, measured in millimetres:
import { layoutBarcode, barcodeSvg, DOTS_PER_MM } from "karsh-barkod";
const geometry = layoutBarcode(result.barcode, {
module: 3, // module width, whole dots
height: 120, // bar height, dots
showText: true,
});
geometry.width; // 339
geometry.height; // 147 — the digit line included
geometry.left; // 33 — the quiet zone before the first bar
barcodeSvg(geometry, DOTS_PER_MM[300]).split("\n")[0];
// <svg ... width="28.25mm" height="12.25mm" viewBox="0 0 339 147" shape-rendering="crispEdges">
Write a printer job. A Zebra job from Code 128 values:
import { buildBarcode, recommendedModule, toZpl } from "karsh-barkod";
const built = ["KRS-0001", "KRS-0002"].map((v) => buildBarcode("code128", v));
const barcodes = built.flatMap((r) => (r.ok ? [r.barcode] : []));
const zpl = toZpl(barcodes, {
widthMm: 50,
heightMm: 30,
gapMm: 2,
dpi: 203,
quantity: 1,
flip: false,
module: recommendedModule("code128", 203), // 2 dots
barHeightMm: 15,
showText: true,
position: null, // centre it
});
zpl.skipped; // [] — values that did not fit, or that the dialect cannot carry
zpl.code gives this for the first label:
^XA
^PW400
^LL240
^LH0,0
^PON
^FO88,49^BY2^BCN,120,Y,N,N,A^FDKRS-0001^FS
^PQ1
^XZ
With the same settings toTspl writes a TSC job:
SIZE 50 mm,30 mm
GAP 2 mm,0 mm
DIRECTION 1
REFERENCE 0,0
CLS
BARCODE 88,49,"128",120,2,0,2,2,"KRS-0001"
PRINT 1
Show the check digit. Spaces, dashes and dots are stripped, and the working comes back so it can be put on screen:
import { checkGs1 } from "karsh-barkod";
const gs1 = checkGs1("gtin13", "869 1234 56789");
// status: "computed", full: "8691234567890", expected: 0, sum: 130
// steps: 12 of them, each { digit, weight, product }
API
Encoding
| Export | Signature | What it does |
|---|---|---|
buildBarcode |
(symbology: Symbology, input: string) => BarcodeResult |
Validates, encodes, and returns the bars and the human-readable digits |
encodeCode128 |
(text: string) => number[] |
The shortest Code 128 symbol sequence; encodeCode128("KRS-0001") → [104, 43, 50, 51, 13, 99, 0, 1, 27, 106] |
printable |
(text: string) => string |
Turns control characters into Unicode control pictures, so the readable line and the SVG stay valid |
foldToAscii |
(text: string) => string |
Folds Turkish letters and the no-break space to plain ASCII; "ÜRÜN-Çİğ" → "URUN-CIg" |
GS1
| Export | Signature | What it does |
|---|---|---|
checkGs1 |
(key: Gs1Key, input: string) => Gs1Result |
Computes the check digit when one short, verifies it when complete; every digit, its weight and the sum come back with it |
Geometry and drawing
| Export | Signature | What it does |
|---|---|---|
layoutBarcode |
(barcode: Barcode, options: LayoutOptions) => Geometry |
Whole-dot layout: bar rectangles, text runs, quiet zone |
barsPath |
(bars: Rect[]) => string |
Every bar as one SVG path |
barcodeSvg |
(geometry: Geometry, dotsPerMm: number) => string |
A standalone SVG: viewBox in dots, width and height in millimetres |
pngWithResolution |
(png: Uint8Array, dotsPerMm: number) => Uint8Array |
Writes a pHYs chunk into an existing PNG; it does not encode one |
Labels and printers
| Export | Signature | What it does |
|---|---|---|
DOTS_PER_MM |
Record<Dpi, number> |
{ 203: 8, 300: 12, 600: 24 } — the rounding both manuals document |
recommendedModule |
(symbology: Symbology, dpi: Dpi) => number |
The default module in whole dots: the same physical width at every resolution |
minimumModule |
(symbology: Symbology, dpi: Dpi) => number |
The fewest whole dots that still reach the floor; anything thinner deserves a warning |
placeBarcode |
(barcode: Barcode, settings: LabelSettings) => Placement |
Places the symbol on the label, centres it, and reports overflow |
toTspl |
(barcodes: Barcode[], settings: LabelSettings) => DialectOutput |
A TSC job; whatever could not be written is in skipped |
toZpl |
(barcodes: Barcode[], settings: LabelSettings) => DialectOutput |
A Zebra job; whatever could not be written is in skipped |
Types
Symbology ("code128", "ean13", "ean8", "upca"), Barcode, Bar,
HumanText, BarcodeResult, BarcodeProblem, BarcodeNote, Gs1Key
("gtin8", "gtin12", "gtin13", "gtin14", "sscc"), Gs1Result,
Gs1Step, Geometry, Rect, TextRun, LayoutOptions, Dpi (203,
300, 600), LabelSettings, Placement, DialectOutput,
DialectProblem.
Limits and decisions
- Nothing throws. Every input error comes back as
{ ok: false, problem }or as astatus; the caller writes the message, the package writes no prose. - A check digit is never corrected. A wrong one is an error, the right one is named, and the number is not quietly changed.
- Code 128 stops at 80 characters. No handheld scanner reads longer reliably, and GS1 stops at 48 anyway.
DOTS_PER_MMis not the nominal dpi. 300 dpi is really 11.81 dots/mm and printers round it to 12. The commands, the SVG and the PNG all use the same rounding so that all three come out the same size.- A label that overflows is skipped, not printed. A value whose bars run
off the stock, or whose coordinate would be negative, never enters the
command and is reported in
skippedwith its reason — half a barcode is worse than none. - The escapes belong to the printer. In ZPL an unprintable character goes
out through
^FHas a hex escape; in TSPL a double quote inside the data is written with the escape the manual gives, while a value carrying a control character is left out entirely — a quoted string has no escape for it, and the printer might read it as a command. - No PNG is encoded.
pngWithResolutionwrites resolution into the bytes of a PNG you already have.
Why this was written rather than installed
The symbologies are small, fixed tables. What a library would hide is exactly what this tool has to know: how many modules wide the symbol is, where each bar sits, which characters a printer will choke on. Centring the label on that width, and showing in the preview the bars the printer will actually draw, both need the inside of the encoder.
The Code 128 encoder is the clearest case. The usual rule of thumb — "four digits or more, go to set C" — is right most of the time and a module or two long the rest. Here it is a shortest-path search instead: for each position and each code set, the cheapest way to have encoded everything so far. The result is minimal by construction, so the width the label is centred on is the width that gets printed. Ties between equally short paths were tuned against the output of a Zebra printer's automatic mode, so that the preview shows the same bars the printer does.
The file has no import line and never touches the DOM, which is why every
table in it can be checked against a reference encoder from a plain Node
script.
License
MIT — see LICENSE. © 2026 Hasan Karşı / KARSH.