Files
evidencija-rezija/web-app/app/lib/pdf/pdf417.ts
Knee Cola 57dcebd640 refactor: convert repository to monorepo with npm workspaces
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>
2025-12-25 12:13:04 +01:00

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);
};