Restructured the repository into a monorepo to better organize application code and maintenance scripts. ## Workspace Structure - web-app: Next.js application (all app code moved from root) - housekeeping: Database backup and maintenance scripts ## Key Changes - Moved all application code to web-app/ using git mv - Moved database scripts to housekeeping/ workspace - Updated Dockerfile for monorepo build process - Updated docker-compose files (volume paths: ./web-app/etc/hosts/) - Updated .gitignore for workspace-level node_modules - Updated documentation (README.md, CLAUDE.md, CHANGELOG.md) ## Migration Impact - Root package.json now manages workspaces - Build commands delegate to web-app workspace - All file history preserved via git mv - Docker build process updated for workspace structure 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1054 lines
30 KiB
TypeScript
1054 lines
30 KiB
TypeScript
import { bcadd, bcdiv, bcmul } from "./bcmath";
|
|
import {
|
|
textsubmodes,
|
|
textlatch,
|
|
clusters,
|
|
rsfactors,
|
|
TextLatchKeys
|
|
} from "./pdf417LookupTables";
|
|
|
|
/**
|
|
* PDF417 - 2D Barcode generator (LGPLv3)
|
|
*
|
|
* Ported from PHP - PDF417 class, version 1.0.005, from TCPDF library (http://www.tcpdf.org/)
|
|
*/
|
|
|
|
export interface BarcodeArray {
|
|
num_rows: number;
|
|
num_cols: number;
|
|
bcode: number[][];
|
|
}
|
|
|
|
const ROWHEIGHT = 4;
|
|
const QUIETH = 2;
|
|
const QUIETV = 2;
|
|
const start_pattern = "11111111010101000";
|
|
const stop_pattern = "111111101000101001";
|
|
|
|
function getInputSequences(code: string): [number, string][] {
|
|
var sequence_array: [number, string][] = []; // array to be returned
|
|
var numseq: [string, number][] = [];
|
|
// get numeric sequences
|
|
var numseqMatch = code.match(/([0-9]{13,44})/g);
|
|
if (numseqMatch == null) {
|
|
numseq = [];
|
|
} else {
|
|
// add offset to each matched line
|
|
for (var n = 0, offset = 0; n < numseqMatch.length; n++) {
|
|
offset = code.indexOf(numseqMatch[n], offset);
|
|
numseq.push([numseqMatch[n], offset]);
|
|
offset += numseqMatch[n].length;
|
|
}
|
|
}
|
|
numseq.push(["", code.length]);
|
|
var offset = 0;
|
|
for (var i = 0; i < numseq.length; i++) {
|
|
var seq = numseq[i];
|
|
var seqlen = seq[0].length;
|
|
if (seq[1] > 0) {
|
|
// extract text sequence before the number sequence
|
|
var prevseq = code.substr(offset, seq[1] - offset);
|
|
var textseq: [string, number][] = [];
|
|
// get text sequences
|
|
var textseqMatch = prevseq.match(/([\x09\x0a\x0d\x20-\x7e]{5,})/g);
|
|
if (textseqMatch == null) {
|
|
textseq = [];
|
|
} else {
|
|
// add offset to each matched line
|
|
for (var n = 0; n < textseqMatch.length; n++) {
|
|
var txtOffset = prevseq.indexOf(textseqMatch[n]);
|
|
textseq.push([textseqMatch[n], txtOffset]);
|
|
}
|
|
}
|
|
textseq.push(["", prevseq.length]);
|
|
var txtoffset = 0;
|
|
for (var j = 0; j < textseq.length; j++) {
|
|
var txtseq = textseq[j];
|
|
var txtseqlen = txtseq[0].length;
|
|
if (txtseq[1] > 0) {
|
|
// extract byte sequence before the text sequence
|
|
var prevtxtseq = prevseq.substr(txtoffset, txtseq[1] - txtoffset);
|
|
if (prevtxtseq.length > 0) {
|
|
// add BYTE sequence
|
|
if (
|
|
prevtxtseq.length == 1 &&
|
|
sequence_array.length > 0 &&
|
|
sequence_array[sequence_array.length - 1][0] == 900
|
|
) {
|
|
sequence_array.push([913, prevtxtseq]);
|
|
} else if (prevtxtseq.length % 6 == 0) {
|
|
sequence_array.push([924, prevtxtseq]);
|
|
} else {
|
|
sequence_array.push([901, prevtxtseq]);
|
|
}
|
|
}
|
|
}
|
|
if (txtseqlen > 0) {
|
|
// add numeric sequence
|
|
sequence_array.push([900, txtseq[0]]);
|
|
}
|
|
txtoffset = txtseq[1] + txtseqlen;
|
|
}
|
|
}
|
|
if (seqlen > 0) {
|
|
// add numeric sequence
|
|
sequence_array.push([902, seq[0]]);
|
|
}
|
|
offset = seq[1] + seqlen;
|
|
}
|
|
return sequence_array;
|
|
};
|
|
|
|
function getCompaction(mode: number, code: string, addmode?: boolean): number[] {
|
|
addmode = addmode ?? true;
|
|
var cw: number[] = []; // array of codewords to return
|
|
switch (mode) {
|
|
case 900: {
|
|
// Text Compaction mode latch
|
|
var submode = 0; // default Alpha sub-mode
|
|
var txtarr: number[] = []; // array of characters and sub-mode switching characters
|
|
var codelen = code.length;
|
|
for (var i = 0; i < codelen; ++i) {
|
|
var chval = _ord(code.charAt(i));
|
|
var k: number | string | false;
|
|
if (
|
|
(k = _array_search(chval, textsubmodes[submode])) !==
|
|
false
|
|
) {
|
|
// we are on the same sub-mode
|
|
txtarr.push(k as number);
|
|
} else {
|
|
// the sub-mode is changed
|
|
for (var s = 0; s < 4; ++s) {
|
|
// search new sub-mode
|
|
if (
|
|
s != submode &&
|
|
(k = _array_search(chval, textsubmodes[s])) !==
|
|
false
|
|
) {
|
|
// s is the new submode
|
|
if (
|
|
(i + 1 == codelen ||
|
|
(i + 1 < codelen &&
|
|
_array_search(
|
|
_ord(code.charAt(i + 1)),
|
|
textsubmodes[submode]
|
|
) !== false)) &&
|
|
(s == 3 || (s == 0 && submode == 1))
|
|
) {
|
|
// shift (temporary change only for this char)
|
|
if (s == 3) {
|
|
// shift to puntuaction
|
|
txtarr.push(29);
|
|
} else {
|
|
// shift from lower to alpha
|
|
txtarr.push(27);
|
|
}
|
|
} else {
|
|
// latch
|
|
txtarr = txtarr.concat(textlatch[("" + submode + s) as TextLatchKeys]);
|
|
// set new submode
|
|
submode = s;
|
|
}
|
|
// add characted code to array
|
|
txtarr.push(k as number);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var txtarrlen = txtarr.length;
|
|
if (txtarrlen % 2 != 0) {
|
|
// add padding
|
|
txtarr.push(29);
|
|
++txtarrlen;
|
|
}
|
|
// calculate codewords
|
|
for (var i = 0; i < txtarrlen; i += 2) {
|
|
cw.push(30 * parseInt(String(txtarr[i])) + parseInt(String(txtarr[i + 1])));
|
|
}
|
|
break;
|
|
}
|
|
case 901:
|
|
case 924: {
|
|
// Byte Compaction mode latch
|
|
var rest: string;
|
|
var sublen: number;
|
|
var codelen: number;
|
|
while ((codelen = code.length) > 0) {
|
|
if (codelen > 6) {
|
|
rest = code.substring(6);
|
|
code = code.substring(0, 6);
|
|
sublen = 6;
|
|
} else {
|
|
rest = "";
|
|
sublen = code.length;
|
|
}
|
|
if (sublen == 6) {
|
|
var t = bcmul("" + _ord(code.charAt(0)), "1099511627776");
|
|
t = bcadd(t, bcmul("" + _ord(code.charAt(1)), "4294967296"));
|
|
t = bcadd(t, bcmul("" + _ord(code.charAt(2)), "16777216"));
|
|
t = bcadd(t, bcmul("" + _ord(code.charAt(3)), "65536"));
|
|
t = bcadd(t, bcmul("" + _ord(code.charAt(4)), "256"));
|
|
t = bcadd(t, "" + _ord(code.charAt(5)));
|
|
// tmp array for the 6 bytes block
|
|
var cw6: number[] = [];
|
|
do {
|
|
var d = _my_bcmod(t, "900");
|
|
t = bcdiv(t, "900");
|
|
// prepend the value to the beginning of the array
|
|
cw6.unshift(d);
|
|
} while (t != "0");
|
|
// append the result array at the end
|
|
cw = cw.concat(cw6);
|
|
} else {
|
|
for (var i = 0; i < sublen; ++i) {
|
|
cw.push(_ord(code.charAt(i)));
|
|
}
|
|
}
|
|
code = rest;
|
|
}
|
|
break;
|
|
}
|
|
case 902: {
|
|
// Numeric Compaction mode latch
|
|
var rest: string;
|
|
var codelen: number;
|
|
while ((codelen = code.length) > 0) {
|
|
if (codelen > 44) {
|
|
rest = code.substring(44);
|
|
code = code.substring(0, 44);
|
|
} else {
|
|
rest = "";
|
|
}
|
|
var t = "1" + code;
|
|
do {
|
|
var d = _my_bcmod(t, "900");
|
|
t = bcdiv(t, "900");
|
|
cw.unshift(d);
|
|
} while (t != "0");
|
|
code = rest;
|
|
}
|
|
break;
|
|
}
|
|
case 913: {
|
|
// Byte Compaction mode shift
|
|
cw.push(_ord(code));
|
|
break;
|
|
}
|
|
}
|
|
if (addmode) {
|
|
// add the compaction mode codeword at the beginning
|
|
cw.unshift(mode);
|
|
}
|
|
return cw;
|
|
};
|
|
|
|
function getErrorCorrectionLevel(ecl: number, numcw: number): number {
|
|
// get maximum correction level
|
|
var maxecl = 8; // starting error level
|
|
var maxerrsize = 928 - numcw; // available codewords for error
|
|
while (maxecl > 0) {
|
|
var errsize = 2 << ecl;
|
|
if (maxerrsize >= errsize) {
|
|
break;
|
|
}
|
|
--maxecl;
|
|
}
|
|
// check for automatic levels
|
|
if (ecl < 0 || ecl > 8) {
|
|
if (numcw < 41) {
|
|
ecl = 2;
|
|
} else if (numcw < 161) {
|
|
ecl = 3;
|
|
} else if (numcw < 321) {
|
|
ecl = 4;
|
|
} else if (numcw < 864) {
|
|
ecl = 5;
|
|
} else {
|
|
ecl = maxecl;
|
|
}
|
|
}
|
|
if (ecl > maxecl) {
|
|
ecl = maxecl;
|
|
}
|
|
return ecl;
|
|
};
|
|
|
|
function getErrorCorrection(cw: number[], ecl: number): number[] {
|
|
// get error correction coefficients
|
|
var ecc = rsfactors[ecl];
|
|
// number of error correction factors
|
|
var eclsize = 2 << ecl;
|
|
// maximum index for rsfactors[ecl]
|
|
var eclmaxid = eclsize - 1;
|
|
// initialize array of error correction codewords
|
|
var ecw = _array_fill(0, eclsize, 0) as number[];
|
|
// for each data codeword
|
|
for (var k = 0; k < cw.length; k++) {
|
|
var t1 = (cw[k] + ecw[eclmaxid]) % 929;
|
|
for (var j = eclmaxid; j > 0; --j) {
|
|
var t2 = t1 * ecc[j] % 929;
|
|
var t3 = 929 - t2;
|
|
ecw[j] = (ecw[j - 1] + t3) % 929;
|
|
}
|
|
t2 = t1 * ecc[0] % 929;
|
|
t3 = 929 - t2;
|
|
ecw[0] = t3 % 929;
|
|
}
|
|
for (var j = 0; j < ecw.length; j++) {
|
|
if (ecw[j] != 0) {
|
|
ecw[j] = 929 - ecw[j];
|
|
}
|
|
}
|
|
ecw = ecw.reverse();
|
|
return ecw;
|
|
};
|
|
|
|
/**
|
|
*
|
|
* Functions from phpjs.org
|
|
*
|
|
*/
|
|
function _array_fill(start_index: number, num: number, mixed_val: number): number[] | Record<number, number> {
|
|
var key: number,
|
|
tmp_arr: Record<number, number> = {};
|
|
|
|
if (start_index == 0) {
|
|
var tmpArray: number[] = [];
|
|
for (var i = 0; i < num; i++) {
|
|
tmpArray.push(mixed_val);
|
|
}
|
|
return tmpArray;
|
|
}
|
|
|
|
if (!isNaN(start_index) && !isNaN(num)) {
|
|
for (key = 0; key < num; key++) {
|
|
tmp_arr[key + start_index] = mixed_val;
|
|
}
|
|
}
|
|
|
|
return tmp_arr;
|
|
};
|
|
|
|
function _str_repeat(input: string, multiplier: number): string {
|
|
// http://kevin.vanzonneveld.net
|
|
// + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
// + improved by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
|
|
// + improved by: Ian Carter (http://euona.com/)
|
|
// * example 1: str_repeat('-=', 10);
|
|
// * returns 1: '-=-=-=-=-=-=-=-=-=-='
|
|
var y = "";
|
|
while (true) {
|
|
if (multiplier & 1) {
|
|
y += input;
|
|
}
|
|
multiplier >>= 1;
|
|
if (multiplier) {
|
|
input += input;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return y;
|
|
};
|
|
|
|
function _intval(mixed_var: any, base?: number): number {
|
|
// http://kevin.vanzonneveld.net
|
|
// + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
// + improved by: stensi
|
|
// + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
// + input by: Matteo
|
|
// + bugfixed by: Brett Zamir (http://brett-zamir.me)
|
|
// + bugfixed by: Rafał Kukawski (http://kukawski.pl)
|
|
// * example 1: intval('Kevin van Zonneveld');
|
|
// * returns 1: 0
|
|
// * example 2: intval(4.2);
|
|
// * returns 2: 4
|
|
// * example 3: intval(42, 8);
|
|
// * returns 3: 42
|
|
// * example 4: intval('09');
|
|
// * returns 4: 9
|
|
// * example 5: intval('1e', 16);
|
|
// * returns 5: 30
|
|
var tmp: number;
|
|
|
|
var type = typeof mixed_var;
|
|
|
|
if (type === "boolean") {
|
|
return +mixed_var;
|
|
} else if (type === "string") {
|
|
tmp = parseInt(mixed_var, base || 10);
|
|
return isNaN(tmp) || !isFinite(tmp) ? 0 : tmp;
|
|
} else if (type === "number" && isFinite(mixed_var)) {
|
|
return mixed_var | 0;
|
|
} else {
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
function _sprintf(format: string, ...args: any[]): string {
|
|
// http://kevin.vanzonneveld.net
|
|
// + original by: Ash Searle (http://hexmen.com/blog/)
|
|
// + namespaced by: Michael White (http://getsprink.com)
|
|
// + tweaked by: Jack
|
|
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
// + input by: Paulo Freitas
|
|
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
// + input by: Brett Zamir (http://brett-zamir.me)
|
|
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
// + improved by: Dj
|
|
// + improved by: Allidylls
|
|
// * example 1: sprintf("%01.2f", 123.1);
|
|
// * returns 1: 123.10
|
|
// * example 2: sprintf("[%10s]", 'monkey');
|
|
// * returns 2: '[ monkey]'
|
|
// * example 3: sprintf("[%'#10s]", 'monkey');
|
|
// * returns 3: '[####monkey]'
|
|
// * example 4: sprintf("%d", 123456789012345);
|
|
// * returns 4: '123456789012345'
|
|
var regex = /%%|%(\d+\$)?([-+\'#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuideEfFgG])/g;
|
|
var a = [format, ...args],
|
|
i = 0;
|
|
format = a[i++];
|
|
|
|
// pad()
|
|
var pad = function (str: string, len: number, chr?: string, leftJustify?: boolean): string {
|
|
if (!chr) {
|
|
chr = " ";
|
|
}
|
|
var padding = str.length >= len
|
|
? ""
|
|
: Array((1 + len - str.length) >>> 0).join(chr);
|
|
return leftJustify ? str + padding : padding + str;
|
|
};
|
|
|
|
// justify()
|
|
var justify = function (
|
|
value: string,
|
|
prefix: string,
|
|
leftJustify: boolean,
|
|
minWidth: number,
|
|
zeroPad: boolean,
|
|
customPadChar?: string
|
|
): string {
|
|
var diff = minWidth - value.length;
|
|
if (diff > 0) {
|
|
if (leftJustify || !zeroPad) {
|
|
value = pad(value, minWidth, customPadChar, leftJustify);
|
|
} else {
|
|
value =
|
|
value.slice(0, prefix.length) +
|
|
pad("", diff, "0", true) +
|
|
value.slice(prefix.length);
|
|
}
|
|
}
|
|
return value;
|
|
};
|
|
|
|
// formatBaseX()
|
|
var formatBaseX = function (
|
|
value: number,
|
|
base: number,
|
|
prefix: string | boolean,
|
|
leftJustify: boolean,
|
|
minWidth: number,
|
|
precision: number,
|
|
zeroPad: boolean
|
|
): string {
|
|
// Note: casts negative numbers to positive ones
|
|
var number = value >>> 0;
|
|
var prefixStr =
|
|
(prefix &&
|
|
number &&
|
|
({
|
|
"2": "0b",
|
|
"8": "0",
|
|
"16": "0x"
|
|
} as Record<string, string>)[String(base)]) ||
|
|
"";
|
|
value = number;
|
|
var valueStr = prefixStr + pad(number.toString(base), precision || 0, "0", false);
|
|
return justify(valueStr, prefixStr, leftJustify, minWidth, zeroPad);
|
|
};
|
|
|
|
// formatString()
|
|
var formatString = function (
|
|
value: string,
|
|
leftJustify: boolean,
|
|
minWidth: number,
|
|
precision: number | null,
|
|
zeroPad: boolean,
|
|
customPadChar?: string
|
|
): string {
|
|
if (precision != null) {
|
|
value = value.slice(0, precision);
|
|
}
|
|
return justify(
|
|
value,
|
|
"",
|
|
leftJustify,
|
|
minWidth,
|
|
zeroPad,
|
|
customPadChar
|
|
);
|
|
};
|
|
|
|
// doFormat()
|
|
var doFormat = function (
|
|
substring: string,
|
|
valueIndex: string | undefined,
|
|
flags: string,
|
|
minWidth: string,
|
|
_: any,
|
|
precision: string,
|
|
type: string
|
|
): string {
|
|
var number: number;
|
|
var prefix: string;
|
|
var method: string;
|
|
var textTransform: string;
|
|
var value: any;
|
|
|
|
if (substring == "%%") {
|
|
return "%";
|
|
}
|
|
|
|
// parse flags
|
|
var leftJustify = false,
|
|
positivePrefix = "",
|
|
zeroPad = false,
|
|
prefixBaseX: string | boolean = false,
|
|
customPadChar = " ";
|
|
var flagsl = flags.length;
|
|
for (var j = 0; flags && j < flagsl; j++) {
|
|
switch (flags.charAt(j)) {
|
|
case " ":
|
|
positivePrefix = " ";
|
|
break;
|
|
case "+":
|
|
positivePrefix = "+";
|
|
break;
|
|
case "-":
|
|
leftJustify = true;
|
|
break;
|
|
case "'":
|
|
customPadChar = flags.charAt(j + 1);
|
|
break;
|
|
case "0":
|
|
zeroPad = true;
|
|
break;
|
|
case "#":
|
|
prefixBaseX = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// parameters may be null, undefined, empty-string or real valued
|
|
// we want to ignore null, undefined and empty-string values
|
|
var minWidthNum = 0;
|
|
if (!minWidth) {
|
|
minWidthNum = 0;
|
|
} else if (minWidth == "*") {
|
|
minWidthNum = +a[i++];
|
|
} else if (minWidth.charAt(0) == "*") {
|
|
minWidthNum = +a[parseInt(minWidth.slice(1, -1))];
|
|
} else {
|
|
minWidthNum = +minWidth;
|
|
}
|
|
|
|
// Note: undocumented perl feature:
|
|
if (minWidthNum < 0) {
|
|
minWidthNum = -minWidthNum;
|
|
leftJustify = true;
|
|
}
|
|
|
|
if (!isFinite(minWidthNum)) {
|
|
throw new Error("sprintf: (minimum-)width must be finite");
|
|
}
|
|
|
|
var precisionNum: number | undefined;
|
|
if (!precision) {
|
|
precisionNum = "fFeE".indexOf(type) > -1
|
|
? 6
|
|
: type == "d" ? 0 : undefined;
|
|
} else if (precision == "*") {
|
|
precisionNum = +a[i++];
|
|
} else if (precision.charAt(0) == "*") {
|
|
precisionNum = +a[parseInt(precision.slice(1, -1))];
|
|
} else {
|
|
precisionNum = +precision;
|
|
}
|
|
|
|
// grab value using valueIndex if required?
|
|
value = valueIndex ? a[parseInt(valueIndex.slice(0, -1))] : a[i++];
|
|
|
|
switch (type) {
|
|
case "s":
|
|
return formatString(
|
|
String(value),
|
|
leftJustify,
|
|
minWidthNum,
|
|
precisionNum === undefined ? null : precisionNum,
|
|
zeroPad,
|
|
customPadChar
|
|
);
|
|
case "c":
|
|
return formatString(
|
|
String.fromCharCode(+value),
|
|
leftJustify,
|
|
minWidthNum,
|
|
precisionNum === undefined ? null : precisionNum,
|
|
zeroPad
|
|
);
|
|
case "b":
|
|
return formatBaseX(
|
|
value,
|
|
2,
|
|
prefixBaseX,
|
|
leftJustify,
|
|
minWidthNum,
|
|
precisionNum || 0,
|
|
zeroPad
|
|
);
|
|
case "o":
|
|
return formatBaseX(
|
|
value,
|
|
8,
|
|
prefixBaseX,
|
|
leftJustify,
|
|
minWidthNum,
|
|
precisionNum || 0,
|
|
zeroPad
|
|
);
|
|
case "x":
|
|
return formatBaseX(
|
|
value,
|
|
16,
|
|
prefixBaseX,
|
|
leftJustify,
|
|
minWidthNum,
|
|
precisionNum || 0,
|
|
zeroPad
|
|
);
|
|
case "X":
|
|
return formatBaseX(
|
|
value,
|
|
16,
|
|
prefixBaseX,
|
|
leftJustify,
|
|
minWidthNum,
|
|
precisionNum || 0,
|
|
zeroPad
|
|
).toUpperCase();
|
|
case "u":
|
|
return formatBaseX(
|
|
value,
|
|
10,
|
|
prefixBaseX,
|
|
leftJustify,
|
|
minWidthNum,
|
|
precisionNum || 0,
|
|
zeroPad
|
|
);
|
|
case "i":
|
|
case "d":
|
|
number = +value || 0;
|
|
number = Math.round(number - number % 1); // Plain Math.round doesn't just truncate
|
|
prefix = number < 0 ? "-" : positivePrefix;
|
|
value =
|
|
prefix + pad(String(Math.abs(number)), precisionNum || 0, "0", false);
|
|
return justify(value, prefix, leftJustify, minWidthNum, zeroPad);
|
|
case "e":
|
|
case "E":
|
|
case "f": // Should handle locales (as per setlocale)
|
|
case "F":
|
|
case "g":
|
|
case "G":
|
|
number = +value;
|
|
prefix = number < 0 ? "-" : positivePrefix;
|
|
method = ["toExponential", "toFixed", "toPrecision"][
|
|
"efg".indexOf(type.toLowerCase())
|
|
];
|
|
textTransform = ["toString", "toUpperCase"][
|
|
"eEfFgG".indexOf(type) % 2
|
|
];
|
|
value = prefix + Math.abs(number)[method as 'toExponential' | 'toFixed' | 'toPrecision'](precisionNum);
|
|
return justify(value, prefix, leftJustify, minWidthNum, zeroPad)[
|
|
textTransform as 'toString' | 'toUpperCase'
|
|
]();
|
|
default:
|
|
return substring;
|
|
}
|
|
};
|
|
|
|
return format.replace(regex, doFormat as any);
|
|
};
|
|
|
|
// function _preg_split(pattern: RegExp | string, subject: string, limit?: number, flags?: string | number): (string | [string, number])[] {
|
|
// // http://kevin.vanzonneveld.net
|
|
// // + original by: Marco Marchiò
|
|
// // * example 1: preg_split(/[\s,]+/, 'hypertext language, programming');
|
|
// // * returns 1: ['hypertext', 'language', 'programming']
|
|
// // * example 2: preg_split('//', 'string', -1, 'PREG_SPLIT_NO_EMPTY');
|
|
// // * returns 2: ['s', 't', 'r', 'i', 'n', 'g']
|
|
// // * example 3: var str = 'hypertext language programming';
|
|
// // * example 3: preg_split('/ /', str, -1, 'PREG_SPLIT_OFFSET_CAPTURE');
|
|
// // * returns 3: [['hypertext', 0], ['language', 10], ['programming', 19]]
|
|
// // * example 4: preg_split('/( )/', '1 2 3 4 5 6 7 8', 4, 'PREG_SPLIT_DELIM_CAPTURE');
|
|
// // * returns 4: ['1', ' ', '2', ' ', '3', ' ', '4 5 6 7 8']
|
|
// // * example 5: preg_split('/( )/', '1 2 3 4 5 6 7 8', 4, (2 | 4));
|
|
// // * returns 5: [['1', 0], [' ', 1], ['2', 2], [' ', 3], ['3', 4], [' ', 5], ['4 5 6 7 8', 6]]
|
|
|
|
// limit = limit || 0;
|
|
// flags = flags || ""; // Limit and flags are optional
|
|
|
|
// var result: RegExpExecArray | null,
|
|
// ret: (string | [string, number])[] = [],
|
|
// index = 0,
|
|
// i = 0,
|
|
// noEmpty = false,
|
|
// delim = false,
|
|
// offset = false,
|
|
// OPTS: Record<string, number> = {},
|
|
// optTemp = 0;
|
|
|
|
// var patternRegex: RegExp;
|
|
// if (typeof pattern === 'string') {
|
|
// var regexpBody = /^\/(.*)\/\w*$/.exec(pattern)?.[1] || pattern;
|
|
// var regexpFlags = /^\/.*\/(\w*)$/.exec(pattern)?.[1] || '';
|
|
// patternRegex = new RegExp(regexpBody, regexpFlags + (regexpFlags.indexOf("g") !== -1 ? "" : "g"));
|
|
// } else {
|
|
// var regexpBody = pattern.source;
|
|
// var regexpFlags = pattern.flags || '';
|
|
// // Non-global regexp causes an infinite loop when executing the while,
|
|
// // so if it's not global, copy the regexp and add the "g" modifier.
|
|
// patternRegex = pattern.global
|
|
// ? pattern
|
|
// : new RegExp(
|
|
// regexpBody,
|
|
// regexpFlags + (regexpFlags.indexOf("g") !== -1 ? "" : "g")
|
|
// );
|
|
// }
|
|
|
|
// OPTS = {
|
|
// PREG_SPLIT_NO_EMPTY: 1,
|
|
// PREG_SPLIT_DELIM_CAPTURE: 2,
|
|
// PREG_SPLIT_OFFSET_CAPTURE: 4
|
|
// };
|
|
// if (typeof flags !== "number") {
|
|
// // Allow for a single string or an array of string flags
|
|
// var flagsArr = [].concat(flags as any);
|
|
// for (i = 0; i < flagsArr.length; i++) {
|
|
// // Resolve string input to bitwise e.g. 'PREG_SPLIT_OFFSET_CAPTURE' becomes 4
|
|
// if (OPTS[flagsArr[i]]) {
|
|
// optTemp = optTemp | OPTS[flagsArr[i]];
|
|
// }
|
|
// }
|
|
// flags = optTemp;
|
|
// }
|
|
// noEmpty = !!((flags as number) & OPTS.PREG_SPLIT_NO_EMPTY);
|
|
// delim = !!((flags as number) & OPTS.PREG_SPLIT_DELIM_CAPTURE);
|
|
// offset = !!((flags as number) & OPTS.PREG_SPLIT_OFFSET_CAPTURE);
|
|
|
|
// var _filter = function (str: string, strindex: number): void {
|
|
// // If the match is empty and the PREG_SPLIT_NO_EMPTY flag is set don't add it
|
|
// if (noEmpty && !str.length) {
|
|
// return;
|
|
// }
|
|
// // If the PREG_SPLIT_OFFSET_CAPTURE flag is set
|
|
// // transform the match into an array and add the index at position 1
|
|
// if (offset) {
|
|
// ret.push([str, strindex]);
|
|
// } else {
|
|
// ret.push(str);
|
|
// }
|
|
// };
|
|
// // Special case for empty regexp
|
|
// if (!regexpBody) {
|
|
// var resultSplit = subject.split("");
|
|
// for (i = 0; i < resultSplit.length; i++) {
|
|
// _filter(resultSplit[i], i);
|
|
// }
|
|
// return ret;
|
|
// }
|
|
// // Exec the pattern and get the result
|
|
// while ((result = patternRegex.exec(subject))) {
|
|
// // Stop if the limit is 1
|
|
// if (limit === 1) {
|
|
// break;
|
|
// }
|
|
// // Take the correct portion of the string and filter the match
|
|
// _filter(subject.slice(index, result.index), index);
|
|
// index = result.index + result[0].length;
|
|
// // If the PREG_SPLIT_DELIM_CAPTURE flag is set, every capture match must be included in the results array
|
|
// if (delim) {
|
|
// // Convert the regexp result into a normal array
|
|
// var resarr = Array.prototype.slice.call(result);
|
|
// for (i = 1; i < resarr.length; i++) {
|
|
// if (result[i] !== undefined) {
|
|
// _filter(result[i], result.index + result[0].indexOf(result[i]));
|
|
// }
|
|
// }
|
|
// }
|
|
// limit--;
|
|
// }
|
|
// // Filter last match
|
|
// _filter(subject.slice(index, subject.length), index);
|
|
// return ret;
|
|
// };
|
|
|
|
function _ord(string: string): number {
|
|
return string.charCodeAt(0);
|
|
};
|
|
|
|
function _array_search(needle: any, haystack: any, argStrict?: boolean): string | number | false {
|
|
// http://kevin.vanzonneveld.net
|
|
// + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
// + input by: Brett Zamir (http://brett-zamir.me)
|
|
// + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
// * example 1: array_search('zonneveld', {firstname: 'kevin', middle: 'van', surname: 'zonneveld'});
|
|
// * returns 1: 'surname'
|
|
// * example 2: ini_set('phpjs.return_phpjs_arrays', 'on');
|
|
// * example 2: var ordered_arr = array({3:'value'}, {2:'value'}, {'a':'value'}, {'b':'value'});
|
|
// * example 2: var key = array_search(/val/g, ordered_arr); // or var key = ordered_arr.search(/val/g);
|
|
// * returns 2: '3'
|
|
|
|
var strict = !!argStrict,
|
|
key: string | number = "";
|
|
|
|
if (
|
|
haystack &&
|
|
typeof haystack === "object" &&
|
|
(haystack as any).change_key_case
|
|
) {
|
|
// Duck-type check for our own array()-created PHPJS_Array
|
|
return (haystack as any).search(needle, argStrict);
|
|
}
|
|
if (typeof needle === "object" && (needle as RegExp).exec) {
|
|
// Duck-type for RegExp
|
|
if (!strict) {
|
|
// Let's consider case sensitive searches as strict
|
|
var needleRegex = needle as RegExp;
|
|
var flags =
|
|
"i" +
|
|
(needleRegex.global ? "g" : "") +
|
|
(needleRegex.multiline ? "m" : "") +
|
|
((needleRegex as any).sticky ? "y" : ""); // sticky is FF only
|
|
needle = new RegExp(needleRegex.source, flags);
|
|
}
|
|
for (key in haystack) {
|
|
if ((needle as RegExp).test(haystack[key])) {
|
|
return key;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
for (key in haystack) {
|
|
if (
|
|
(strict && haystack[key] === needle) ||
|
|
(!strict && haystack[key] == needle)
|
|
) {
|
|
return key;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
function _my_bcmod(x: string, y: string): number {
|
|
// how many numbers to take at once? carefull not to exceed (int)
|
|
var take = 5;
|
|
var mod = "";
|
|
do {
|
|
var a = parseInt(mod + "" + x.substring(0, take));
|
|
x = x.substring(take);
|
|
mod = String(a % parseInt(y));
|
|
} while (x.length);
|
|
|
|
return parseInt(mod);
|
|
}
|
|
|
|
/**
|
|
* Creates a PDF417 object
|
|
* @param code (string) code to represent using PDF417
|
|
* @param ecl (int) error correction level (0-8); default -1 = automatic correction level
|
|
* @param aspectratio (float) the width to height of the symbol (excluding quiet zones)
|
|
*/
|
|
export function generateBarcode(code: string, ecl?: number, aspectratio?: number) {
|
|
|
|
code = unescape(encodeURIComponent(code)); // covert UTF-8 to ISO-8859-1
|
|
ecl = ecl || -1;
|
|
aspectratio = aspectratio || 2;
|
|
if (code === "") {
|
|
throw new Error("PDF417 init: empty input code");
|
|
}
|
|
// get the input sequence array
|
|
let sequence = getInputSequences(code);
|
|
let codewords: number[] = []; // array of code-words
|
|
for (var i = 0; i < sequence.length; i++) {
|
|
var cw = getCompaction(sequence[i][0], sequence[i][1], true);
|
|
codewords = codewords.concat(cw);
|
|
}
|
|
if (codewords[0] == 900) {
|
|
// Text Alpha is the default mode, so remove the first code
|
|
codewords.shift();
|
|
}
|
|
// count number of codewords
|
|
var numcw = codewords.length;
|
|
if (numcw > 925) {
|
|
// reached maximum data codeword capacity
|
|
throw new Error("PDF417 init: maximum data codeword capacity exceeded");
|
|
}
|
|
|
|
// set error correction level
|
|
ecl = getErrorCorrectionLevel(ecl, numcw);
|
|
// number of codewords for error correction
|
|
var errsize = 2 << ecl;
|
|
// calculate number of columns (number of codewords per row) and rows
|
|
var nce = numcw + errsize + 1;
|
|
var cols = Math.round(
|
|
(Math.sqrt(4761 + 68 * aspectratio * ROWHEIGHT * nce) - 69) / 34
|
|
);
|
|
// adjust cols
|
|
if (cols < 1) {
|
|
cols = 1;
|
|
} else if (cols > 30) {
|
|
cols = 30;
|
|
}
|
|
var rows = Math.ceil(nce / cols);
|
|
var size = cols * rows;
|
|
// adjust rows
|
|
if (rows < 3 || rows > 90) {
|
|
if (rows < 3) {
|
|
rows = 3;
|
|
} else if (rows > 90) {
|
|
rows = 90;
|
|
}
|
|
cols = Math.ceil(size / rows);
|
|
size = cols * rows;
|
|
}
|
|
if (size > 928) {
|
|
// set dimensions to get maximum capacity
|
|
if (
|
|
Math.abs(aspectratio - 17 * 29 / 32) <
|
|
Math.abs(aspectratio - 17 * 16 / 58)
|
|
) {
|
|
cols = 29;
|
|
rows = 32;
|
|
} else {
|
|
cols = 16;
|
|
rows = 58;
|
|
}
|
|
size = 928;
|
|
}
|
|
// calculate padding
|
|
var pad = size - nce;
|
|
if (pad > 0) {
|
|
if (size - rows == nce) {
|
|
--rows;
|
|
size -= rows;
|
|
} else {
|
|
// add pading
|
|
codewords = codewords.concat(_array_fill(0, pad, 900) as number[]);
|
|
}
|
|
}
|
|
|
|
// Symbol Length Descriptor (number of data codewords including Symbol Length Descriptor and pad codewords)
|
|
var sld = size - errsize;
|
|
// add symbol length description
|
|
codewords.unshift(sld);
|
|
// calculate error correction
|
|
var ecw = getErrorCorrection(codewords, ecl);
|
|
// add error correction codewords
|
|
codewords = codewords.concat(ecw);
|
|
// add horizontal quiet zones to start and stop patterns
|
|
var pstart = _str_repeat("0", QUIETH) + start_pattern;
|
|
var pstop = stop_pattern + "" + _str_repeat("0", QUIETH);
|
|
|
|
const barcode_array: BarcodeArray = {
|
|
num_rows: rows * ROWHEIGHT + 2 * QUIETV,
|
|
num_cols: (cols + 2) * 17 + 35 + 2 * QUIETH,
|
|
bcode: []
|
|
};
|
|
|
|
var empty_row: number[] = [];
|
|
// build rows for vertical quiet zone
|
|
if (QUIETV > 0) {
|
|
empty_row = _array_fill(0, barcode_array.num_cols, 0) as number[];
|
|
for (var i = 0; i < QUIETV; ++i) {
|
|
// add vertical quiet rows
|
|
barcode_array.bcode.push(empty_row);
|
|
}
|
|
}
|
|
|
|
var L: number = 0;
|
|
var k = 0; // codeword index
|
|
var cid = 0; // initial cluster
|
|
// for each row
|
|
for (var r = 0; r < rows; ++r) {
|
|
// row start code
|
|
var row = pstart;
|
|
switch (cid) {
|
|
case 0: {
|
|
L = 30 * _intval(r / 3) + _intval((rows - 1) / 3);
|
|
break;
|
|
}
|
|
case 1: {
|
|
L = 30 * _intval(r / 3) + ecl * 3 + (rows - 1) % 3;
|
|
break;
|
|
}
|
|
case 2: {
|
|
L = 30 * _intval(r / 3) + (cols - 1);
|
|
break;
|
|
}
|
|
}
|
|
// left row indicator
|
|
row += _sprintf("%17b", clusters[cid][L]);
|
|
// for each column
|
|
for (var c = 0; c < cols; ++c) {
|
|
row += _sprintf("%17b", clusters[cid][codewords[k]]);
|
|
++k;
|
|
}
|
|
switch (cid) {
|
|
case 0: {
|
|
L = 30 * _intval(r / 3) + (cols - 1);
|
|
break;
|
|
}
|
|
case 1: {
|
|
L = 30 * _intval(r / 3) + _intval((rows - 1) / 3);
|
|
break;
|
|
}
|
|
case 2: {
|
|
L = 30 * _intval(r / 3) + ecl * 3 + (rows - 1) % 3;
|
|
break;
|
|
}
|
|
}
|
|
// right row indicator
|
|
row += _sprintf("%17b", clusters[cid][L]);
|
|
// row stop code
|
|
row += pstop;
|
|
// convert the string to array
|
|
const arow = row.split('');
|
|
// duplicate row to get the desired height
|
|
for (var h = 0; h < ROWHEIGHT; ++h) {
|
|
barcode_array.bcode.push(arow.map(x => parseInt(x)));
|
|
}
|
|
++cid;
|
|
if (cid > 2) {
|
|
cid = 0;
|
|
}
|
|
}
|
|
if (QUIETV > 0) {
|
|
for (var i = 0; i < QUIETV; ++i) {
|
|
// add vertical quiet rows
|
|
barcode_array.bcode.push(empty_row);
|
|
}
|
|
}
|
|
|
|
return (barcode_array);
|
|
};
|
|
|
|
|