1
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import { SIDE_BIN, MIDDLE_BIN } from './constants';
|
||||
import encode from './encoder';
|
||||
import Barcode from '../Barcode';
|
||||
|
||||
// Base class for EAN8 & EAN13
|
||||
class EAN extends Barcode {
|
||||
|
||||
constructor(data, options) {
|
||||
super(data, options);
|
||||
|
||||
// Make sure the font is not bigger than the space between the guard bars
|
||||
this.fontSize = !options.flat && options.fontSize > options.width * 10
|
||||
? options.width * 10
|
||||
: options.fontSize;
|
||||
|
||||
// Make the guard bars go down half the way of the text
|
||||
this.guardHeight = options.height + this.fontSize / 2 + options.textMargin;
|
||||
}
|
||||
|
||||
encode() {
|
||||
return this.options.flat
|
||||
? this.encodeFlat()
|
||||
: this.encodeGuarded();
|
||||
}
|
||||
|
||||
leftText(from, to) {
|
||||
return this.text.substr(from, to);
|
||||
}
|
||||
|
||||
leftEncode(data, structure) {
|
||||
return encode(data, structure);
|
||||
}
|
||||
|
||||
rightText(from, to) {
|
||||
return this.text.substr(from, to);
|
||||
}
|
||||
|
||||
rightEncode(data, structure) {
|
||||
return encode(data, structure);
|
||||
}
|
||||
|
||||
encodeGuarded() {
|
||||
const textOptions = { fontSize: this.fontSize };
|
||||
const guardOptions = { height: this.guardHeight };
|
||||
|
||||
return [
|
||||
{ data: SIDE_BIN, options: guardOptions },
|
||||
{ data: this.leftEncode(), text: this.leftText(), options: textOptions },
|
||||
{ data: MIDDLE_BIN, options: guardOptions },
|
||||
{ data: this.rightEncode(), text: this.rightText(), options: textOptions },
|
||||
{ data: SIDE_BIN, options: guardOptions },
|
||||
];
|
||||
}
|
||||
|
||||
encodeFlat() {
|
||||
const data = [
|
||||
SIDE_BIN,
|
||||
this.leftEncode(),
|
||||
MIDDLE_BIN,
|
||||
this.rightEncode(),
|
||||
SIDE_BIN
|
||||
];
|
||||
|
||||
return {
|
||||
data: data.join(''),
|
||||
text: this.text
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default EAN;
|
||||
@@ -0,0 +1,90 @@
|
||||
// Encoding documentation:
|
||||
// https://en.wikipedia.org/wiki/International_Article_Number_(EAN)#Binary_encoding_of_data_digits_into_EAN-13_barcode
|
||||
|
||||
import { EAN13_STRUCTURE } from './constants';
|
||||
import EAN from './EAN';
|
||||
|
||||
// Calculate the checksum digit
|
||||
// https://en.wikipedia.org/wiki/International_Article_Number_(EAN)#Calculation_of_checksum_digit
|
||||
const checksum = (number) => {
|
||||
const res = number
|
||||
.substr(0, 12)
|
||||
.split('')
|
||||
.map((n) => +n)
|
||||
.reduce((sum, a, idx) => (
|
||||
idx % 2 ? sum + a * 3 : sum + a
|
||||
), 0);
|
||||
|
||||
return (10 - (res % 10)) % 10;
|
||||
};
|
||||
|
||||
class EAN13 extends EAN {
|
||||
|
||||
constructor(data, options) {
|
||||
// Add checksum if it does not exist
|
||||
if (data.search(/^[0-9]{12}$/) !== -1) {
|
||||
data += checksum(data);
|
||||
}
|
||||
|
||||
super(data, options);
|
||||
|
||||
// Adds a last character to the end of the barcode
|
||||
this.lastChar = options.lastChar;
|
||||
}
|
||||
|
||||
valid() {
|
||||
return (
|
||||
this.data.search(/^[0-9]{13}$/) !== -1 &&
|
||||
+this.data[12] === checksum(this.data)
|
||||
);
|
||||
}
|
||||
|
||||
leftText() {
|
||||
return super.leftText(1, 6);
|
||||
}
|
||||
|
||||
leftEncode() {
|
||||
const data = this.data.substr(1, 6);
|
||||
const structure = EAN13_STRUCTURE[this.data[0]];
|
||||
return super.leftEncode(data, structure);
|
||||
}
|
||||
|
||||
rightText() {
|
||||
return super.rightText(7, 6);
|
||||
}
|
||||
|
||||
rightEncode() {
|
||||
const data = this.data.substr(7, 6);
|
||||
return super.rightEncode(data, 'RRRRRR');
|
||||
}
|
||||
|
||||
// The "standard" way of printing EAN13 barcodes with guard bars
|
||||
encodeGuarded() {
|
||||
const data = super.encodeGuarded();
|
||||
|
||||
// Extend data with left digit & last character
|
||||
if (this.options.displayValue) {
|
||||
data.unshift({
|
||||
data: '000000000000',
|
||||
text: this.text.substr(0, 1),
|
||||
options: { textAlign: 'left', fontSize: this.fontSize }
|
||||
});
|
||||
|
||||
if (this.options.lastChar) {
|
||||
data.push({
|
||||
data: '00'
|
||||
});
|
||||
data.push({
|
||||
data: '00000',
|
||||
text: this.options.lastChar,
|
||||
options: { fontSize: this.fontSize }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default EAN13;
|
||||
@@ -0,0 +1,30 @@
|
||||
// Encoding documentation:
|
||||
// https://en.wikipedia.org/wiki/EAN_2#Encoding
|
||||
|
||||
import { EAN2_STRUCTURE } from './constants';
|
||||
import encode from './encoder';
|
||||
import Barcode from '../Barcode';
|
||||
|
||||
class EAN2 extends Barcode {
|
||||
|
||||
constructor(data, options) {
|
||||
super(data, options);
|
||||
}
|
||||
|
||||
valid() {
|
||||
return this.data.search(/^[0-9]{2}$/) !== -1;
|
||||
}
|
||||
|
||||
encode(){
|
||||
// Choose the structure based on the number mod 4
|
||||
const structure = EAN2_STRUCTURE[parseInt(this.data) % 4];
|
||||
return {
|
||||
// Start bits + Encode the two digits with 01 in between
|
||||
data: '1011' + encode(this.data, structure, '01'),
|
||||
text: this.text
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default EAN2;
|
||||
@@ -0,0 +1,40 @@
|
||||
// Encoding documentation:
|
||||
// https://en.wikipedia.org/wiki/EAN_5#Encoding
|
||||
|
||||
import { EAN5_STRUCTURE } from './constants';
|
||||
import encode from './encoder';
|
||||
import Barcode from '../Barcode';
|
||||
|
||||
const checksum = (data) => {
|
||||
const result = data
|
||||
.split('')
|
||||
.map(n => +n)
|
||||
.reduce((sum, a, idx) => {
|
||||
return idx % 2
|
||||
? sum + a * 9
|
||||
: sum + a * 3;
|
||||
}, 0);
|
||||
return result % 10;
|
||||
};
|
||||
|
||||
class EAN5 extends Barcode {
|
||||
|
||||
constructor(data, options) {
|
||||
super(data, options);
|
||||
}
|
||||
|
||||
valid() {
|
||||
return this.data.search(/^[0-9]{5}$/) !== -1;
|
||||
}
|
||||
|
||||
encode() {
|
||||
const structure = EAN5_STRUCTURE[checksum(this.data)];
|
||||
return {
|
||||
data: '1011' + encode(this.data, structure, '01'),
|
||||
text: this.text
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default EAN5;
|
||||
@@ -0,0 +1,57 @@
|
||||
// Encoding documentation:
|
||||
// http://www.barcodeisland.com/ean8.phtml
|
||||
|
||||
import EAN from './EAN';
|
||||
|
||||
// Calculate the checksum digit
|
||||
const checksum = (number) => {
|
||||
const res = number
|
||||
.substr(0, 7)
|
||||
.split('')
|
||||
.map((n) => +n)
|
||||
.reduce((sum, a, idx) => (
|
||||
idx % 2 ? sum + a : sum + a * 3
|
||||
), 0);
|
||||
|
||||
return (10 - (res % 10)) % 10;
|
||||
};
|
||||
|
||||
class EAN8 extends EAN {
|
||||
|
||||
constructor(data, options) {
|
||||
// Add checksum if it does not exist
|
||||
if (data.search(/^[0-9]{7}$/) !== -1) {
|
||||
data += checksum(data);
|
||||
}
|
||||
|
||||
super(data, options);
|
||||
}
|
||||
|
||||
valid() {
|
||||
return (
|
||||
this.data.search(/^[0-9]{8}$/) !== -1 &&
|
||||
+this.data[7] === checksum(this.data)
|
||||
);
|
||||
}
|
||||
|
||||
leftText() {
|
||||
return super.leftText(0, 4);
|
||||
}
|
||||
|
||||
leftEncode() {
|
||||
const data = this.data.substr(0, 4);
|
||||
return super.leftEncode(data, 'LLLL');
|
||||
}
|
||||
|
||||
rightText() {
|
||||
return super.rightText(4, 4);
|
||||
}
|
||||
|
||||
rightEncode() {
|
||||
const data = this.data.substr(4, 4);
|
||||
return super.rightEncode(data, 'RRRR');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default EAN8;
|
||||
@@ -0,0 +1,132 @@
|
||||
// Encoding documentation:
|
||||
// https://en.wikipedia.org/wiki/Universal_Product_Code#Encoding
|
||||
|
||||
import encode from './encoder';
|
||||
import Barcode from "../Barcode.js";
|
||||
|
||||
class UPC extends Barcode{
|
||||
constructor(data, options){
|
||||
// Add checksum if it does not exist
|
||||
if(data.search(/^[0-9]{11}$/) !== -1){
|
||||
data += checksum(data);
|
||||
}
|
||||
|
||||
super(data, options);
|
||||
|
||||
this.displayValue = options.displayValue;
|
||||
|
||||
// Make sure the font is not bigger than the space between the guard bars
|
||||
if(options.fontSize > options.width * 10){
|
||||
this.fontSize = options.width * 10;
|
||||
}
|
||||
else{
|
||||
this.fontSize = options.fontSize;
|
||||
}
|
||||
|
||||
// Make the guard bars go down half the way of the text
|
||||
this.guardHeight = options.height + this.fontSize / 2 + options.textMargin;
|
||||
}
|
||||
|
||||
valid(){
|
||||
return this.data.search(/^[0-9]{12}$/) !== -1 &&
|
||||
this.data[11] == checksum(this.data);
|
||||
}
|
||||
|
||||
encode(){
|
||||
if(this.options.flat){
|
||||
return this.flatEncoding();
|
||||
}
|
||||
else{
|
||||
return this.guardedEncoding();
|
||||
}
|
||||
}
|
||||
|
||||
flatEncoding(){
|
||||
var result = "";
|
||||
|
||||
result += "101";
|
||||
result += encode(this.data.substr(0, 6), "LLLLLL");
|
||||
result += "01010";
|
||||
result += encode(this.data.substr(6, 6), "RRRRRR");
|
||||
result += "101";
|
||||
|
||||
return {
|
||||
data: result,
|
||||
text: this.text
|
||||
};
|
||||
}
|
||||
|
||||
guardedEncoding(){
|
||||
var result = [];
|
||||
|
||||
// Add the first digit
|
||||
if(this.displayValue){
|
||||
result.push({
|
||||
data: "00000000",
|
||||
text: this.text.substr(0, 1),
|
||||
options: {textAlign: "left", fontSize: this.fontSize}
|
||||
});
|
||||
}
|
||||
|
||||
// Add the guard bars
|
||||
result.push({
|
||||
data: "101" + encode(this.data[0], "L"),
|
||||
options: {height: this.guardHeight}
|
||||
});
|
||||
|
||||
// Add the left side
|
||||
result.push({
|
||||
data: encode(this.data.substr(1, 5), "LLLLL"),
|
||||
text: this.text.substr(1, 5),
|
||||
options: {fontSize: this.fontSize}
|
||||
});
|
||||
|
||||
// Add the middle bits
|
||||
result.push({
|
||||
data: "01010",
|
||||
options: {height: this.guardHeight}
|
||||
});
|
||||
|
||||
// Add the right side
|
||||
result.push({
|
||||
data: encode(this.data.substr(6, 5), "RRRRR"),
|
||||
text: this.text.substr(6, 5),
|
||||
options: {fontSize: this.fontSize}
|
||||
});
|
||||
|
||||
// Add the end bits
|
||||
result.push({
|
||||
data: encode(this.data[11], "R") + "101",
|
||||
options: {height: this.guardHeight}
|
||||
});
|
||||
|
||||
// Add the last digit
|
||||
if(this.displayValue){
|
||||
result.push({
|
||||
data: "00000000",
|
||||
text: this.text.substr(11, 1),
|
||||
options: {textAlign: "right", fontSize: this.fontSize}
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Calulate the checksum digit
|
||||
// https://en.wikipedia.org/wiki/International_Article_Number_(EAN)#Calculation_of_checksum_digit
|
||||
export function checksum(number){
|
||||
var result = 0;
|
||||
|
||||
var i;
|
||||
for(i = 1; i < 11; i += 2){
|
||||
result += parseInt(number[i]);
|
||||
}
|
||||
for(i = 0; i < 11; i += 2){
|
||||
result += parseInt(number[i]) * 3;
|
||||
}
|
||||
|
||||
return (10 - (result % 10)) % 10;
|
||||
}
|
||||
|
||||
export default UPC;
|
||||
@@ -0,0 +1,177 @@
|
||||
// Encoding documentation:
|
||||
// https://en.wikipedia.org/wiki/Universal_Product_Code#Encoding
|
||||
//
|
||||
// UPC-E documentation:
|
||||
// https://en.wikipedia.org/wiki/Universal_Product_Code#UPC-E
|
||||
|
||||
import encode from './encoder';
|
||||
import Barcode from "../Barcode.js";
|
||||
import { checksum } from './UPC.js';
|
||||
|
||||
const EXPANSIONS = [
|
||||
"XX00000XXX",
|
||||
"XX10000XXX",
|
||||
"XX20000XXX",
|
||||
"XXX00000XX",
|
||||
"XXXX00000X",
|
||||
"XXXXX00005",
|
||||
"XXXXX00006",
|
||||
"XXXXX00007",
|
||||
"XXXXX00008",
|
||||
"XXXXX00009"
|
||||
];
|
||||
|
||||
const PARITIES = [
|
||||
["EEEOOO", "OOOEEE"],
|
||||
["EEOEOO", "OOEOEE"],
|
||||
["EEOOEO", "OOEEOE"],
|
||||
["EEOOOE", "OOEEEO"],
|
||||
["EOEEOO", "OEOOEE"],
|
||||
["EOOEEO", "OEEOOE"],
|
||||
["EOOOEE", "OEEEOO"],
|
||||
["EOEOEO", "OEOEOE"],
|
||||
["EOEOOE", "OEOEEO"],
|
||||
["EOOEOE", "OEEOEO"]
|
||||
];
|
||||
|
||||
class UPCE extends Barcode{
|
||||
constructor(data, options){
|
||||
// Code may be 6 or 8 digits;
|
||||
// A 7 digit code is ambiguous as to whether the extra digit
|
||||
// is a UPC-A check or number system digit.
|
||||
super(data, options);
|
||||
this.isValid = false;
|
||||
if(data.search(/^[0-9]{6}$/) !== -1){
|
||||
this.middleDigits = data;
|
||||
this.upcA = expandToUPCA(data, "0");
|
||||
this.text = options.text ||
|
||||
`${this.upcA[0]}${data}${this.upcA[this.upcA.length - 1]}`;
|
||||
this.isValid = true;
|
||||
}
|
||||
else if(data.search(/^[01][0-9]{7}$/) !== -1){
|
||||
this.middleDigits = data.substring(1, data.length - 1);
|
||||
this.upcA = expandToUPCA(this.middleDigits, data[0]);
|
||||
|
||||
if(this.upcA[this.upcA.length - 1] === data[data.length - 1]){
|
||||
this.isValid = true;
|
||||
}
|
||||
else{
|
||||
// checksum mismatch
|
||||
return;
|
||||
}
|
||||
}
|
||||
else{
|
||||
return;
|
||||
}
|
||||
|
||||
this.displayValue = options.displayValue;
|
||||
|
||||
// Make sure the font is not bigger than the space between the guard bars
|
||||
if(options.fontSize > options.width * 10){
|
||||
this.fontSize = options.width * 10;
|
||||
}
|
||||
else{
|
||||
this.fontSize = options.fontSize;
|
||||
}
|
||||
|
||||
// Make the guard bars go down half the way of the text
|
||||
this.guardHeight = options.height + this.fontSize / 2 + options.textMargin;
|
||||
}
|
||||
|
||||
valid(){
|
||||
return this.isValid;
|
||||
}
|
||||
|
||||
encode(){
|
||||
if(this.options.flat){
|
||||
return this.flatEncoding();
|
||||
}
|
||||
else{
|
||||
return this.guardedEncoding();
|
||||
}
|
||||
}
|
||||
|
||||
flatEncoding(){
|
||||
var result = "";
|
||||
|
||||
result += "101";
|
||||
result += this.encodeMiddleDigits();
|
||||
result += "010101";
|
||||
|
||||
return {
|
||||
data: result,
|
||||
text: this.text
|
||||
};
|
||||
}
|
||||
|
||||
guardedEncoding(){
|
||||
var result = [];
|
||||
|
||||
// Add the UPC-A number system digit beneath the quiet zone
|
||||
if(this.displayValue){
|
||||
result.push({
|
||||
data: "00000000",
|
||||
text: this.text[0],
|
||||
options: {textAlign: "left", fontSize: this.fontSize}
|
||||
});
|
||||
}
|
||||
|
||||
// Add the guard bars
|
||||
result.push({
|
||||
data: "101",
|
||||
options: {height: this.guardHeight}
|
||||
});
|
||||
|
||||
// Add the 6 UPC-E digits
|
||||
result.push({
|
||||
data: this.encodeMiddleDigits(),
|
||||
text: this.text.substring(1, 7),
|
||||
options: {fontSize: this.fontSize}
|
||||
});
|
||||
|
||||
// Add the end bits
|
||||
result.push({
|
||||
data: "010101",
|
||||
options: {height: this.guardHeight}
|
||||
});
|
||||
|
||||
// Add the UPC-A check digit beneath the quiet zone
|
||||
if(this.displayValue){
|
||||
result.push({
|
||||
data: "00000000",
|
||||
text: this.text[7],
|
||||
options: {textAlign: "right", fontSize: this.fontSize}
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
encodeMiddleDigits() {
|
||||
const numberSystem = this.upcA[0];
|
||||
const checkDigit = this.upcA[this.upcA.length - 1];
|
||||
const parity = PARITIES[parseInt(checkDigit)][parseInt(numberSystem)];
|
||||
return encode(this.middleDigits, parity);
|
||||
}
|
||||
}
|
||||
|
||||
function expandToUPCA(middleDigits, numberSystem) {
|
||||
const lastUpcE = parseInt(middleDigits[middleDigits.length - 1]);
|
||||
const expansion = EXPANSIONS[lastUpcE];
|
||||
|
||||
let result = "";
|
||||
let digitIndex = 0;
|
||||
for(let i = 0; i < expansion.length; i++) {
|
||||
let c = expansion[i];
|
||||
if (c === 'X') {
|
||||
result += middleDigits[digitIndex++];
|
||||
} else {
|
||||
result += c;
|
||||
}
|
||||
}
|
||||
|
||||
result = `${numberSystem}${result}`;
|
||||
return `${result}${checksum(result)}`;
|
||||
}
|
||||
|
||||
export default UPCE;
|
||||
@@ -0,0 +1,41 @@
|
||||
// Standard start end and middle bits
|
||||
export const SIDE_BIN = '101';
|
||||
export const MIDDLE_BIN = '01010';
|
||||
|
||||
export const BINARIES = {
|
||||
'L': [ // The L (left) type of encoding
|
||||
'0001101', '0011001', '0010011', '0111101', '0100011',
|
||||
'0110001', '0101111', '0111011', '0110111', '0001011'
|
||||
],
|
||||
'G': [ // The G type of encoding
|
||||
'0100111', '0110011', '0011011', '0100001', '0011101',
|
||||
'0111001', '0000101', '0010001', '0001001', '0010111'
|
||||
],
|
||||
'R': [ // The R (right) type of encoding
|
||||
'1110010', '1100110', '1101100', '1000010', '1011100',
|
||||
'1001110', '1010000', '1000100', '1001000', '1110100'
|
||||
],
|
||||
'O': [ // The O (odd) encoding for UPC-E
|
||||
'0001101', '0011001', '0010011', '0111101', '0100011',
|
||||
'0110001', '0101111', '0111011', '0110111', '0001011'
|
||||
],
|
||||
'E': [ // The E (even) encoding for UPC-E
|
||||
'0100111', '0110011', '0011011', '0100001', '0011101',
|
||||
'0111001', '0000101', '0010001', '0001001', '0010111'
|
||||
]
|
||||
};
|
||||
|
||||
// Define the EAN-2 structure
|
||||
export const EAN2_STRUCTURE = ['LL', 'LG', 'GL', 'GG'];
|
||||
|
||||
// Define the EAN-5 structure
|
||||
export const EAN5_STRUCTURE = [
|
||||
'GGLLL', 'GLGLL', 'GLLGL', 'GLLLG', 'LGGLL',
|
||||
'LLGGL', 'LLLGG', 'LGLGL', 'LGLLG', 'LLGLG'
|
||||
];
|
||||
|
||||
// Define the EAN-13 structure
|
||||
export const EAN13_STRUCTURE = [
|
||||
'LLLLLL', 'LLGLGG', 'LLGGLG', 'LLGGGL', 'LGLLGG',
|
||||
'LGGLLG', 'LGGGLL', 'LGLGLG', 'LGLGGL', 'LGGLGL'
|
||||
];
|
||||
@@ -0,0 +1,20 @@
|
||||
import { BINARIES } from './constants';
|
||||
|
||||
// Encode data string
|
||||
const encode = (data, structure, separator) => {
|
||||
let encoded = data
|
||||
.split('')
|
||||
.map((val, idx) => BINARIES[structure[idx]])
|
||||
.map((val, idx) => val ? val[data[idx]] : '');
|
||||
|
||||
if (separator) {
|
||||
const last = data.length - 1;
|
||||
encoded = encoded.map((val, idx) => (
|
||||
idx < last ? val + separator : val
|
||||
));
|
||||
}
|
||||
|
||||
return encoded.join('');
|
||||
};
|
||||
|
||||
export default encode;
|
||||
@@ -0,0 +1,8 @@
|
||||
import EAN13 from './EAN13.js';
|
||||
import EAN8 from './EAN8.js';
|
||||
import EAN5 from './EAN5.js';
|
||||
import EAN2 from './EAN2.js';
|
||||
import UPC from './UPC.js';
|
||||
import UPCE from './UPCE.js';
|
||||
|
||||
export {EAN13, EAN8, EAN5, EAN2, UPC, UPCE};
|
||||
Reference in New Issue
Block a user