uk.org.okapibarcode.backend.Composite Maven / Gradle / Ivy
Show all versions of okapibarcode Show documentation
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package uk.org.okapibarcode.backend;
import static uk.org.okapibarcode.util.Arrays.positionOf;
import static uk.org.okapibarcode.util.Strings.binaryAppend;
import java.awt.geom.Rectangle2D;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import uk.org.okapibarcode.backend.DataBar14.Mode;
/**
* Implements GS1 Composite symbology according to ISO/IEC 24723:2010.
*
*
Composite symbols comprise a 2D element which encodes GS1 data and a
* "linear" element which can be UPC, EAN, Code 128 or GS1 DataBar symbol.
*
* @author Robin Stuart
*/
public class Composite extends Symbol {
/** The linear component choices available. */
public static enum LinearEncoding {
UPCA, UPCE, EAN, CODE_128, DATABAR_14, DATABAR_14_STACK,
DATABAR_14_STACK_OMNI, DATABAR_LIMITED, DATABAR_EXPANDED,
DATABAR_EXPANDED_STACK
}
/** The 2D component choices available. */
public static enum CompositeMode {
/**
* Indicates that the composite symbol uses a MicroPDF417 variant as the 2D component.
* Of the 2D component choices, this one holds the least amount of data.
*/
CC_A,
/**
* Indicates that the composite symbol uses a MicroPDF417 symbol as the 2D component, starting with a codeword of 920.
*/
CC_B,
/**
* Indicates that the composite symbol uses a PDF417 symbol as the 2D component, starting with a codeword of 920.
* Of the 2D component choices, this one holds the most amount of data. May only be used if the linear component
* is {@link LinearEncoding#CODE_128 Code 128}.
*/
CC_C
}
private static enum GeneralFieldMode {
NUMERIC, ALPHA, ISOIEC, INVALID_CHAR, ANY_ENC, ALPHA_OR_ISO
}
/* CC-A component coefficients from ISO/IEC 24728:2006 Annex F */
private static final int[] CCA_COEFFS = {
/* k = 4 */
522, 568, 723, 809,
/* k = 5 */
427, 919, 460, 155, 566,
/* k = 6 */
861, 285, 19, 803, 17, 766,
/* k = 7 */
76, 925, 537, 597, 784, 691, 437,
/* k = 8 */
237, 308, 436, 284, 646, 653, 428, 379
};
private static final int[] COEFRS = {
/* k = 2 */
27, 917,
/* k = 4 */
522, 568, 723, 809,
/* k = 8 */
237, 308, 436, 284, 646, 653, 428, 379,
/* k = 16 */
274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65,
/* k = 32 */
361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517,
273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410,
/* k = 64 */
539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733, 877, 381, 612,
723, 476, 462, 172, 430, 609, 858, 822, 543, 376, 511, 400, 672, 762, 283, 184,
440, 35, 519, 31, 460, 594, 225, 535, 517, 352, 605, 158, 651, 201, 488, 502,
648, 733, 717, 83, 404, 97, 280, 771, 840, 629, 4, 381, 843, 623, 264, 543,
/* k = 128 */
521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400, 925, 749, 415,
822, 93, 217, 208, 928, 244, 583, 620, 246, 148, 447, 631, 292, 908, 490, 704,
516, 258, 457, 907, 594, 723, 674, 292, 272, 96, 684, 432, 686, 606, 860, 569,
193, 219, 129, 186, 236, 287, 192, 775, 278, 173, 40, 379, 712, 463, 646, 776,
171, 491, 297, 763, 156, 732, 95, 270, 447, 90, 507, 48, 228, 821, 808, 898,
784, 663, 627, 378, 382, 262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616,
157, 374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814, 587, 804, 34,
211, 330, 539, 297, 827, 865, 37, 517, 834, 315, 550, 86, 801, 4, 108, 539,
/* k = 256 */
524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905, 786, 138, 720,
858, 194, 311, 913, 275, 190, 375, 850, 438, 733, 194, 280, 201, 280, 828, 757,
710, 814, 919, 89, 68, 569, 11, 204, 796, 605, 540, 913, 801, 700, 799, 137,
439, 418, 592, 668, 353, 859, 370, 694, 325, 240, 216, 257, 284, 549, 209, 884,
315, 70, 329, 793, 490, 274, 877, 162, 749, 812, 684, 461, 334, 376, 849, 521,
307, 291, 803, 712, 19, 358, 399, 908, 103, 511, 51, 8, 517, 225, 289, 470,
637, 731, 66, 255, 917, 269, 463, 830, 730, 433, 848, 585, 136, 538, 906, 90,
2, 290, 743, 199, 655, 903, 329, 49, 802, 580, 355, 588, 188, 462, 10, 134,
628, 320, 479, 130, 739, 71, 263, 318, 374, 601, 192, 605, 142, 673, 687, 234,
722, 384, 177, 752, 607, 640, 455, 193, 689, 707, 805, 641, 48, 60, 732, 621,
895, 544, 261, 852, 655, 309, 697, 755, 756, 60, 231, 773, 434, 421, 726, 528,
503, 118, 49, 795, 32, 144, 500, 238, 836, 394, 280, 566, 319, 9, 647, 550,
73, 914, 342, 126, 32, 681, 331, 792, 620, 60, 609, 441, 180, 791, 893, 754,
605, 383, 228, 749, 760, 213, 54, 297, 134, 54, 834, 299, 922, 191, 910, 532,
609, 829, 189, 20, 167, 29, 872, 449, 83, 402, 41, 656, 505, 579, 481, 173,
404, 251, 688, 95, 497, 555, 642, 543, 307, 159, 924, 558, 648, 55, 497, 10,
/* k = 512 */
352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285, 380, 350, 492,
197, 265, 920, 155, 914, 299, 229, 643, 294, 871, 306, 88, 87, 193, 352, 781,
846, 75, 327, 520, 435, 543, 203, 666, 249, 346, 781, 621, 640, 268, 794, 534,
539, 781, 408, 390, 644, 102, 476, 499, 290, 632, 545, 37, 858, 916, 552, 41,
542, 289, 122, 272, 383, 800, 485, 98, 752, 472, 761, 107, 784, 860, 658, 741,
290, 204, 681, 407, 855, 85, 99, 62, 482, 180, 20, 297, 451, 593, 913, 142,
808, 684, 287, 536, 561, 76, 653, 899, 729, 567, 744, 390, 513, 192, 516, 258,
240, 518, 794, 395, 768, 848, 51, 610, 384, 168, 190, 826, 328, 596, 786, 303,
570, 381, 415, 641, 156, 237, 151, 429, 531, 207, 676, 710, 89, 168, 304, 402,
40, 708, 575, 162, 864, 229, 65, 861, 841, 512, 164, 477, 221, 92, 358, 785,
288, 357, 850, 836, 827, 736, 707, 94, 8, 494, 114, 521, 2, 499, 851, 543,
152, 729, 771, 95, 248, 361, 578, 323, 856, 797, 289, 51, 684, 466, 533, 820,
669, 45, 902, 452, 167, 342, 244, 173, 35, 463, 651, 51, 699, 591, 452, 578,
37, 124, 298, 332, 552, 43, 427, 119, 662, 777, 475, 850, 764, 364, 578, 911,
283, 711, 472, 420, 245, 288, 594, 394, 511, 327, 589, 777, 699, 688, 43, 408,
842, 383, 721, 521, 560, 644, 714, 559, 62, 145, 873, 663, 713, 159, 672, 729,
624, 59, 193, 417, 158, 209, 563, 564, 343, 693, 109, 608, 563, 365, 181, 772,
677, 310, 248, 353, 708, 410, 579, 870, 617, 841, 632, 860, 289, 536, 35, 777,
618, 586, 424, 833, 77, 597, 346, 269, 757, 632, 695, 751, 331, 247, 184, 45,
787, 680, 18, 66, 407, 369, 54, 492, 228, 613, 830, 922, 437, 519, 644, 905,
789, 420, 305, 441, 207, 300, 892, 827, 141, 537, 381, 662, 513, 56, 252, 341,
242, 797, 838, 837, 720, 224, 307, 631, 61, 87, 560, 310, 756, 665, 397, 808,
851, 309, 473, 795, 378, 31, 647, 915, 459, 806, 590, 731, 425, 216, 548, 249,
321, 881, 699, 535, 673, 782, 210, 815, 905, 303, 843, 922, 281, 73, 469, 791,
660, 162, 498, 308, 155, 422, 907, 817, 187, 62, 16, 425, 535, 336, 286, 437,
375, 273, 610, 296, 183, 923, 116, 667, 751, 353, 62, 366, 691, 379, 687, 842,
37, 357, 720, 742, 330, 5, 39, 923, 311, 424, 242, 749, 321, 54, 669, 316,
342, 299, 534, 105, 667, 488, 640, 672, 576, 540, 316, 486, 721, 610, 46, 656,
447, 171, 616, 464, 190, 531, 297, 321, 762, 752, 533, 175, 134, 14, 381, 433,
717, 45, 111, 20, 596, 284, 736, 138, 646, 411, 877, 669, 141, 919, 45, 780,
407, 164, 332, 899, 165, 726, 600, 325, 498, 655, 357, 752, 768, 223, 849, 647,
63, 310, 863, 251, 366, 304, 282, 738, 675, 410, 389, 244, 31, 121, 303, 263
};
/* rows, error codewords, k-offset of valid CC-A sizes from ISO/IEC 24723:2006 Table 9 */
private static final int[] CCA_VARIANTS = {
5, 6, 7, 8, 9, 10, 12, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 4, 4, 5, 5, 6, 6, 7, 4, 5, 6, 7, 7, 4, 5, 6, 7, 8, 0, 0, 4, 4, 9, 9, 15, 0, 4, 9, 15, 15, 0, 4, 9, 15, 22
};
/* following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24723:2006 tables 10 and 11 */
private static final int[] A_RAP_TABLE = {
39, 1, 32, 8, 14, 43, 20, 11, 1, 5, 15, 21, 40, 43, 46, 34, 29, 0, 0, 0, 0, 0, 0, 0, 43, 33, 37, 47, 1, 20, 23, 26, 14, 9, 19, 33, 12, 40, 46, 23, 52, 23, 13, 17, 27, 33, 52, 3, 6, 46, 41, 6, 0, 3, 3, 3, 0, 3, 3, 0, 3, 6, 6, 0, 0, 0, 0, 3
};
private static final String[] CODAGEMC = {
"urA", "xfs", "ypy", "unk", "xdw", "yoz", "pDA", "uls", "pBk", "eBA",
"pAs", "eAk", "prA", "uvs", "xhy", "pnk", "utw", "xgz", "fDA", "pls", "fBk", "frA", "pvs",
"uxy", "fnk", "ptw", "uwz", "fls", "psy", "fvs", "pxy", "ftw", "pwz", "fxy", "yrx", "ufk",
"xFw", "ymz", "onA", "uds", "xEy", "olk", "ucw", "dBA", "oks", "uci", "dAk", "okg", "dAc",
"ovk", "uhw", "xaz", "dnA", "ots", "ugy", "dlk", "osw", "ugj", "dks", "osi", "dvk", "oxw",
"uiz", "dts", "owy", "dsw", "owj", "dxw", "oyz", "dwy", "dwj", "ofA", "uFs", "xCy", "odk",
"uEw", "xCj", "clA", "ocs", "uEi", "ckk", "ocg", "ckc", "ckE", "cvA", "ohs", "uay", "ctk",
"ogw", "uaj", "css", "ogi", "csg", "csa", "cxs", "oiy", "cww", "oij", "cwi", "cyy", "oFk",
"uCw", "xBj", "cdA", "oEs", "uCi", "cck", "oEg", "uCb", "ccc", "oEa", "ccE", "oED", "chk",
"oaw", "uDj", "cgs", "oai", "cgg", "oab", "cga", "cgD", "obj", "cib", "cFA", "oCs", "uBi",
"cEk", "oCg", "uBb", "cEc", "oCa", "cEE", "oCD", "cEC", "cas", "cag", "caa", "cCk", "uAr",
"oBa", "oBD", "cCB", "tfk", "wpw", "yez", "mnA", "tds", "woy", "mlk", "tcw", "woj", "FBA",
"mks", "FAk", "mvk", "thw", "wqz", "FnA", "mts", "tgy", "Flk", "msw", "Fks", "Fkg", "Fvk",
"mxw", "tiz", "Fts", "mwy", "Fsw", "Fsi", "Fxw", "myz", "Fwy", "Fyz", "vfA", "xps", "yuy",
"vdk", "xow", "yuj", "qlA", "vcs", "xoi", "qkk", "vcg", "xob", "qkc", "vca", "mfA", "tFs",
"wmy", "qvA", "mdk", "tEw", "wmj", "qtk", "vgw", "xqj", "hlA", "Ekk", "mcg", "tEb", "hkk",
"qsg", "hkc", "EvA", "mhs", "tay", "hvA", "Etk", "mgw", "taj", "htk", "qww", "vij", "hss",
"Esg", "hsg", "Exs", "miy", "hxs", "Eww", "mij", "hww", "qyj", "hwi", "Eyy", "hyy", "Eyj",
"hyj", "vFk", "xmw", "ytj", "qdA", "vEs", "xmi", "qck", "vEg", "xmb", "qcc", "vEa", "qcE",
"qcC", "mFk", "tCw", "wlj", "qhk", "mEs", "tCi", "gtA", "Eck", "vai", "tCb", "gsk", "Ecc",
"mEa", "gsc", "qga", "mED", "EcC", "Ehk", "maw", "tDj", "gxk", "Egs", "mai", "gws", "qii",
"mab", "gwg", "Ega", "EgD", "Eiw", "mbj", "gyw", "Eii", "gyi", "Eib", "gyb", "gzj", "qFA",
"vCs", "xli", "qEk", "vCg", "xlb", "qEc", "vCa", "qEE", "vCD", "qEC", "qEB", "EFA", "mCs",
"tBi", "ghA", "EEk", "mCg", "tBb", "ggk", "qag", "vDb", "ggc", "EEE", "mCD", "ggE", "qaD",
"ggC", "Eas", "mDi", "gis", "Eag", "mDb", "gig", "qbb", "gia", "EaD", "giD", "gji", "gjb",
"qCk", "vBg", "xkr", "qCc", "vBa", "qCE", "vBD", "qCC", "qCB", "ECk", "mBg", "tAr", "gak",
"ECc", "mBa", "gac", "qDa", "mBD", "gaE", "ECC", "gaC", "ECB", "EDg", "gbg", "gba", "gbD",
"vAq", "vAn", "qBB", "mAq", "EBE", "gDE", "gDC", "gDB", "lfA", "sps", "wey", "ldk", "sow",
"ClA", "lcs", "soi", "Ckk", "lcg", "Ckc", "CkE", "CvA", "lhs", "sqy", "Ctk", "lgw", "sqj",
"Css", "lgi", "Csg", "Csa", "Cxs", "liy", "Cww", "lij", "Cwi", "Cyy", "Cyj", "tpk", "wuw",
"yhj", "ndA", "tos", "wui", "nck", "tog", "wub", "ncc", "toa", "ncE", "toD", "lFk", "smw",
"wdj", "nhk", "lEs", "smi", "atA", "Cck", "tqi", "smb", "ask", "ngg", "lEa", "asc", "CcE",
"asE", "Chk", "law", "snj", "axk", "Cgs", "trj", "aws", "nii", "lab", "awg", "Cga", "awa",
"Ciw", "lbj", "ayw", "Cii", "ayi", "Cib", "Cjj", "azj", "vpA", "xus", "yxi", "vok", "xug",
"yxb", "voc", "xua", "voE", "xuD", "voC", "nFA", "tms", "wti", "rhA", "nEk", "xvi", "wtb",
"rgk", "vqg", "xvb", "rgc", "nEE", "tmD", "rgE", "vqD", "nEB", "CFA", "lCs", "sli", "ahA",
"CEk", "lCg", "slb", "ixA", "agk", "nag", "tnb", "iwk", "rig", "vrb", "lCD", "iwc", "agE",
"naD", "iwE", "CEB", "Cas", "lDi", "ais", "Cag", "lDb", "iys", "aig", "nbb", "iyg", "rjb",
"CaD", "aiD", "Cbi", "aji", "Cbb", "izi", "ajb", "vmk", "xtg", "ywr", "vmc", "xta", "vmE",
"xtD", "vmC", "vmB", "nCk", "tlg", "wsr", "rak", "nCc", "xtr", "rac", "vna", "tlD", "raE",
"nCC", "raC", "nCB", "raB", "CCk", "lBg", "skr", "aak", "CCc", "lBa", "iik", "aac", "nDa",
"lBD", "iic", "rba", "CCC", "iiE", "aaC", "CCB", "aaB", "CDg", "lBr", "abg", "CDa", "ijg",
"aba", "CDD", "ija", "abD", "CDr", "ijr", "vlc", "xsq", "vlE", "xsn", "vlC", "vlB", "nBc",
"tkq", "rDc", "nBE", "tkn", "rDE", "vln", "rDC", "nBB", "rDB", "CBc", "lAq", "aDc", "CBE",
"lAn", "ibc", "aDE", "nBn", "ibE", "rDn", "CBB", "ibC", "aDB", "ibB", "aDq", "ibq", "ibn",
"xsf", "vkl", "tkf", "nAm", "nAl", "CAo", "aBo", "iDo", "CAl", "aBl", "kpk", "BdA", "kos",
"Bck", "kog", "seb", "Bcc", "koa", "BcE", "koD", "Bhk", "kqw", "sfj", "Bgs", "kqi", "Bgg",
"kqb", "Bga", "BgD", "Biw", "krj", "Bii", "Bib", "Bjj", "lpA", "sus", "whi", "lok", "sug",
"loc", "sua", "loE", "suD", "loC", "BFA", "kms", "sdi", "DhA", "BEk", "svi", "sdb", "Dgk",
"lqg", "svb", "Dgc", "BEE", "kmD", "DgE", "lqD", "BEB", "Bas", "kni", "Dis", "Bag", "knb",
"Dig", "lrb", "Dia", "BaD", "Bbi", "Dji", "Bbb", "Djb", "tuk", "wxg", "yir", "tuc", "wxa",
"tuE", "wxD", "tuC", "tuB", "lmk", "stg", "nqk", "lmc", "sta", "nqc", "tva", "stD", "nqE",
"lmC", "nqC", "lmB", "nqB", "BCk", "klg", "Dak", "BCc", "str", "bik", "Dac", "lna", "klD",
"bic", "nra", "BCC", "biE", "DaC", "BCB", "DaB", "BDg", "klr", "Dbg", "BDa", "bjg", "Dba",
"BDD", "bja", "DbD", "BDr", "Dbr", "bjr", "xxc", "yyq", "xxE", "yyn", "xxC", "xxB", "ttc",
"wwq", "vvc", "xxq", "wwn", "vvE", "xxn", "vvC", "ttB", "vvB", "llc", "ssq", "nnc", "llE",
"ssn", "rrc", "nnE", "ttn", "rrE", "vvn", "llB", "rrC", "nnB", "rrB", "BBc", "kkq", "DDc",
"BBE", "kkn", "bbc", "DDE", "lln", "jjc", "bbE", "nnn", "BBB", "jjE", "rrn", "DDB", "jjC",
"BBq", "DDq", "BBn", "bbq", "DDn", "jjq", "bbn", "jjn", "xwo", "yyf", "xwm", "xwl", "tso",
"wwf", "vto", "xwv", "vtm", "tsl", "vtl", "lko", "ssf", "nlo", "lkm", "rno", "nlm", "lkl",
"rnm", "nll", "rnl", "BAo", "kkf", "DBo", "lkv", "bDo", "DBm", "BAl", "jbo", "bDm", "DBl",
"jbm", "bDl", "jbl", "DBv", "jbv", "xwd", "vsu", "vst", "nku", "rlu", "rlt", "DAu", "bBu",
"jDu", "jDt", "ApA", "Aok", "keg", "Aoc", "AoE", "AoC", "Aqs", "Aqg", "Aqa", "AqD", "Ari",
"Arb", "kuk", "kuc", "sha", "kuE", "shD", "kuC", "kuB", "Amk", "kdg", "Bqk", "kvg", "kda",
"Bqc", "kva", "BqE", "kvD", "BqC", "AmB", "BqB", "Ang", "kdr", "Brg", "kvr", "Bra", "AnD",
"BrD", "Anr", "Brr", "sxc", "sxE", "sxC", "sxB", "ktc", "lvc", "sxq", "sgn", "lvE", "sxn",
"lvC", "ktB", "lvB", "Alc", "Bnc", "AlE", "kcn", "Drc", "BnE", "AlC", "DrE", "BnC", "AlB",
"DrC", "BnB", "Alq", "Bnq", "Aln", "Drq", "Bnn", "Drn", "wyo", "wym", "wyl", "swo", "txo",
"wyv", "txm", "swl", "txl", "kso", "sgf", "lto", "swv", "nvo", "ltm", "ksl", "nvm", "ltl",
"nvl", "Ako", "kcf", "Blo", "ksv", "Dno", "Blm", "Akl", "bro", "Dnm", "Bll", "brm", "Dnl",
"Akv", "Blv", "Dnv", "brv", "yze", "yzd", "wye", "xyu", "wyd", "xyt", "swe", "twu", "swd",
"vxu", "twt", "vxt", "kse", "lsu", "ksd", "ntu", "lst", "rvu", "ypk", "zew", "xdA", "yos",
"zei", "xck", "yog", "zeb", "xcc", "yoa", "xcE", "yoD", "xcC", "xhk", "yqw", "zfj", "utA",
"xgs", "yqi", "usk", "xgg", "yqb", "usc", "xga", "usE", "xgD", "usC", "uxk", "xiw", "yrj",
"ptA", "uws", "xii", "psk", "uwg", "xib", "psc", "uwa", "psE", "uwD", "psC", "pxk", "uyw",
"xjj", "ftA", "pws", "uyi", "fsk", "pwg", "uyb", "fsc", "pwa", "fsE", "pwD", "fxk", "pyw",
"uzj", "fws", "pyi", "fwg", "pyb", "fwa", "fyw", "pzj", "fyi", "fyb", "xFA", "yms", "zdi",
"xEk", "ymg", "zdb", "xEc", "yma", "xEE", "ymD", "xEC", "xEB", "uhA", "xas", "yni", "ugk",
"xag", "ynb", "ugc", "xaa", "ugE", "xaD", "ugC", "ugB", "oxA", "uis", "xbi", "owk", "uig",
"xbb", "owc", "uia", "owE", "uiD", "owC", "owB", "dxA", "oys", "uji", "dwk", "oyg", "ujb",
"dwc", "oya", "dwE", "oyD", "dwC", "dys", "ozi", "dyg", "ozb", "dya", "dyD", "dzi", "dzb",
"xCk", "ylg", "zcr", "xCc", "yla", "xCE", "ylD", "xCC", "xCB", "uak", "xDg", "ylr", "uac",
"xDa", "uaE", "xDD", "uaC", "uaB", "oik", "ubg", "xDr", "oic", "uba", "oiE", "ubD", "oiC",
"oiB", "cyk", "ojg", "ubr", "cyc", "oja", "cyE", "ojD", "cyC", "cyB", "czg", "ojr", "cza",
"czD", "czr", "xBc", "ykq", "xBE", "ykn", "xBC", "xBB", "uDc", "xBq", "uDE", "xBn", "uDC",
"uDB", "obc", "uDq", "obE", "uDn", "obC", "obB", "cjc", "obq", "cjE", "obn", "cjC", "cjB",
"cjq", "cjn", "xAo", "ykf", "xAm", "xAl", "uBo", "xAv", "uBm", "uBl", "oDo", "uBv", "oDm",
"oDl", "cbo", "oDv", "cbm", "cbl", "xAe", "xAd", "uAu", "uAt", "oBu", "oBt", "wpA", "yes",
"zFi", "wok", "yeg", "zFb", "woc", "yea", "woE", "yeD", "woC", "woB", "thA", "wqs", "yfi",
"tgk", "wqg", "yfb", "tgc", "wqa", "tgE", "wqD", "tgC", "tgB", "mxA", "tis", "wri", "mwk",
"tig", "wrb", "mwc", "tia", "mwE", "tiD", "mwC", "mwB", "FxA", "mys", "tji", "Fwk", "myg",
"tjb", "Fwc", "mya", "FwE", "myD", "FwC", "Fys", "mzi", "Fyg", "mzb", "Fya", "FyD", "Fzi",
"Fzb", "yuk", "zhg", "hjs", "yuc", "zha", "hbw", "yuE", "zhD", "hDy", "yuC", "yuB", "wmk",
"ydg", "zEr", "xqk", "wmc", "zhr", "xqc", "yva", "ydD", "xqE", "wmC", "xqC", "wmB", "xqB",
"tak", "wng", "ydr", "vik", "tac", "wna", "vic", "xra", "wnD", "viE", "taC", "viC", "taB",
"viB", "mik", "tbg", "wnr", "qyk", "mic", "tba", "qyc", "vja", "tbD", "qyE", "miC", "qyC",
"miB", "qyB", "Eyk", "mjg", "tbr", "hyk", "Eyc", "mja", "hyc", "qza", "mjD", "hyE", "EyC",
"hyC", "EyB", "Ezg", "mjr", "hzg", "Eza", "hza", "EzD", "hzD", "Ezr", "ytc", "zgq", "grw",
"ytE", "zgn", "gny", "ytC", "glz", "ytB", "wlc", "ycq", "xnc", "wlE", "ycn", "xnE", "ytn",
"xnC", "wlB", "xnB", "tDc", "wlq", "vbc", "tDE", "wln", "vbE", "xnn", "vbC", "tDB", "vbB",
"mbc", "tDq", "qjc", "mbE", "tDn", "qjE", "vbn", "qjC", "mbB", "qjB", "Ejc", "mbq", "gzc",
"EjE", "mbn", "gzE", "qjn", "gzC", "EjB", "gzB", "Ejq", "gzq", "Ejn", "gzn", "yso", "zgf",
"gfy", "ysm", "gdz", "ysl", "wko", "ycf", "xlo", "ysv", "xlm", "wkl", "xll", "tBo", "wkv",
"vDo", "tBm", "vDm", "tBl", "vDl", "mDo", "tBv", "qbo", "vDv", "qbm", "mDl", "qbl", "Ebo",
"mDv", "gjo", "Ebm", "gjm", "Ebl", "gjl", "Ebv", "gjv", "yse", "gFz", "ysd", "wke", "xku",
"wkd", "xkt", "tAu", "vBu", "tAt", "vBt", "mBu", "qDu", "mBt", "qDt", "EDu", "gbu", "EDt",
"gbt", "ysF", "wkF", "xkh", "tAh", "vAx", "mAx", "qBx", "wek", "yFg", "zCr", "wec", "yFa",
"weE", "yFD", "weC", "weB", "sqk", "wfg", "yFr", "sqc", "wfa", "sqE", "wfD", "sqC", "sqB",
"lik", "srg", "wfr", "lic", "sra", "liE", "srD", "liC", "liB", "Cyk", "ljg", "srr", "Cyc",
"lja", "CyE", "ljD", "CyC", "CyB", "Czg", "ljr", "Cza", "CzD", "Czr", "yhc", "zaq", "arw",
"yhE", "zan", "any", "yhC", "alz", "yhB", "wdc", "yEq", "wvc", "wdE", "yEn", "wvE", "yhn",
"wvC", "wdB", "wvB", "snc", "wdq", "trc", "snE", "wdn", "trE", "wvn", "trC", "snB", "trB",
"lbc", "snq", "njc", "lbE", "snn", "njE", "trn", "njC", "lbB", "njB", "Cjc", "lbq", "azc",
"CjE", "lbn", "azE", "njn", "azC", "CjB", "azB", "Cjq", "azq", "Cjn", "azn", "zio", "irs",
"rfy", "zim", "inw", "rdz", "zil", "ily", "ikz", "ygo", "zaf", "afy", "yxo", "ziv", "ivy",
"adz", "yxm", "ygl", "itz", "yxl", "wco", "yEf", "wto", "wcm", "xvo", "yxv", "wcl", "xvm",
"wtl", "xvl", "slo", "wcv", "tno", "slm", "vro", "tnm", "sll", "vrm", "tnl", "vrl", "lDo",
"slv", "nbo", "lDm", "rjo", "nbm", "lDl", "rjm", "nbl", "rjl", "Cbo", "lDv", "ajo", "Cbm",
"izo", "ajm", "Cbl", "izm", "ajl", "izl", "Cbv", "ajv", "zie", "ifw", "rFz", "zid", "idy",
"icz", "yge", "aFz", "ywu", "ygd", "ihz", "ywt", "wce", "wsu", "wcd", "xtu", "wst", "xtt",
"sku", "tlu", "skt", "vnu", "tlt", "vnt", "lBu", "nDu", "lBt", "rbu", "nDt", "rbt", "CDu",
"abu", "CDt", "iju", "abt", "ijt", "ziF", "iFy", "iEz", "ygF", "ywh", "wcF", "wsh", "xsx",
"skh", "tkx", "vlx", "lAx", "nBx", "rDx", "CBx", "aDx", "ibx", "iCz", "wFc", "yCq", "wFE",
"yCn", "wFC", "wFB", "sfc", "wFq", "sfE", "wFn", "sfC", "sfB", "krc", "sfq", "krE", "sfn",
"krC", "krB", "Bjc", "krq", "BjE", "krn", "BjC", "BjB", "Bjq", "Bjn", "yao", "zDf", "Dfy",
"yam", "Ddz", "yal", "wEo", "yCf", "who", "wEm", "whm", "wEl", "whl", "sdo", "wEv", "svo",
"sdm", "svm", "sdl", "svl", "kno", "sdv", "lro", "knm", "lrm", "knl", "lrl", "Bbo", "knv",
"Djo", "Bbm", "Djm", "Bbl", "Djl", "Bbv", "Djv", "zbe", "bfw", "npz", "zbd", "bdy", "bcz",
"yae", "DFz", "yiu", "yad", "bhz", "yit", "wEe", "wgu", "wEd", "wxu", "wgt", "wxt", "scu",
"stu", "sct", "tvu", "stt", "tvt", "klu", "lnu", "klt", "nru", "lnt", "nrt", "BDu", "Dbu",
"BDt", "bju", "Dbt", "bjt", "jfs", "rpy", "jdw", "roz", "jcy", "jcj", "zbF", "bFy", "zjh",
"jhy", "bEz", "jgz", "yaF", "yih", "yyx", "wEF", "wgh", "wwx", "xxx", "sch", "ssx", "ttx",
"vvx", "kkx", "llx", "nnx", "rrx", "BBx", "DDx", "bbx", "jFw", "rmz", "jEy", "jEj", "bCz",
"jaz", "jCy", "jCj", "jBj", "wCo", "wCm", "wCl", "sFo", "wCv", "sFm", "sFl", "kfo", "sFv",
"kfm", "kfl", "Aro", "kfv", "Arm", "Arl", "Arv", "yDe", "Bpz", "yDd", "wCe", "wau", "wCd",
"wat", "sEu", "shu", "sEt", "sht", "kdu", "kvu", "kdt", "kvt", "Anu", "Bru", "Ant", "Brt",
"zDp", "Dpy", "Doz", "yDF", "ybh", "wCF", "wah", "wix", "sEh", "sgx", "sxx", "kcx", "ktx",
"lvx", "Alx", "Bnx", "Drx", "bpw", "nuz", "boy", "boj", "Dmz", "bqz", "jps", "ruy", "jow",
"ruj", "joi", "job", "bmy", "jqy", "bmj", "jqj", "jmw", "rtj", "jmi", "jmb", "blj", "jnj",
"jli", "jlb", "jkr", "sCu", "sCt", "kFu", "kFt", "Afu", "Aft", "wDh", "sCh", "sax", "kEx",
"khx", "Adx", "Avx", "Buz", "Duy", "Duj", "buw", "nxj", "bui", "bub", "Dtj", "bvj", "jus",
"rxi", "jug", "rxb", "jua", "juD", "bti", "jvi", "btb", "jvb", "jtg", "rwr", "jta", "jtD",
"bsr", "jtr", "jsq", "jsn", "Bxj", "Dxi", "Dxb", "bxg", "nyr", "bxa", "bxD", "Dwr", "bxr",
"bwq", "bwn", "pjk", "urw", "ejA", "pbs", "uny", "ebk", "pDw", "ulz", "eDs", "pBy", "eBw",
"zfc", "fjk", "prw", "zfE", "fbs", "pny", "zfC", "fDw", "plz", "zfB", "fBy", "yrc", "zfq",
"frw", "yrE", "zfn", "fny", "yrC", "flz", "yrB", "xjc", "yrq", "xjE", "yrn", "xjC", "xjB",
"uzc", "xjq", "uzE", "xjn", "uzC", "uzB", "pzc", "uzq", "pzE", "uzn", "pzC", "djA", "ors",
"ufy", "dbk", "onw", "udz", "dDs", "oly", "dBw", "okz", "dAy", "zdo", "drs", "ovy", "zdm",
"dnw", "otz", "zdl", "dly", "dkz", "yno", "zdv", "dvy", "ynm", "dtz", "ynl", "xbo", "ynv",
"xbm", "xbl", "ujo", "xbv", "ujm", "ujl", "ozo", "ujv", "ozm", "ozl", "crk", "ofw", "uFz",
"cns", "ody", "clw", "ocz", "cky", "ckj", "zcu", "cvw", "ohz", "zct", "cty", "csz", "ylu",
"cxz", "ylt", "xDu", "xDt", "ubu", "ubt", "oju", "ojt", "cfs", "oFy", "cdw", "oEz", "ccy",
"ccj", "zch", "chy", "cgz", "ykx", "xBx", "uDx", "cFw", "oCz", "cEy", "cEj", "caz", "cCy",
"cCj", "FjA", "mrs", "tfy", "Fbk", "mnw", "tdz", "FDs", "mly", "FBw", "mkz", "FAy", "zFo",
"Frs", "mvy", "zFm", "Fnw", "mtz", "zFl", "Fly", "Fkz", "yfo", "zFv", "Fvy", "yfm", "Ftz",
"yfl", "wro", "yfv", "wrm", "wrl", "tjo", "wrv", "tjm", "tjl", "mzo", "tjv", "mzm", "mzl",
"qrk", "vfw", "xpz", "hbA", "qns", "vdy", "hDk", "qlw", "vcz", "hBs", "qky", "hAw", "qkj",
"hAi", "Erk", "mfw", "tFz", "hrk", "Ens", "mdy", "hns", "qty", "mcz", "hlw", "Eky", "hky",
"Ekj", "hkj", "zEu", "Evw", "mhz", "zhu", "zEt", "hvw", "Ety", "zht", "hty", "Esz", "hsz",
"ydu", "Exz", "yvu", "ydt", "hxz", "yvt", "wnu", "xru", "wnt", "xrt", "tbu", "vju", "tbt",
"vjt", "mju", "mjt", "grA", "qfs", "vFy", "gnk", "qdw", "vEz", "gls", "qcy", "gkw", "qcj",
"gki", "gkb", "Efs", "mFy", "gvs", "Edw", "mEz", "gtw", "qgz", "gsy", "Ecj", "gsj", "zEh",
"Ehy", "zgx", "gxy", "Egz", "gwz", "ycx", "ytx", "wlx", "xnx", "tDx", "vbx", "mbx", "gfk",
"qFw", "vCz", "gds", "qEy", "gcw", "qEj", "gci", "gcb", "EFw", "mCz", "ghw", "EEy", "ggy",
"EEj", "ggj", "Eaz", "giz", "gFs", "qCy", "gEw", "qCj", "gEi", "gEb", "ECy", "gay", "ECj",
"gaj", "gCw", "qBj", "gCi", "gCb", "EBj", "gDj", "gBi", "gBb", "Crk", "lfw", "spz", "Cns",
"ldy", "Clw", "lcz", "Cky", "Ckj", "zCu", "Cvw", "lhz", "zCt", "Cty", "Csz", "yFu", "Cxz",
"yFt", "wfu", "wft", "sru", "srt", "lju", "ljt", "arA", "nfs", "tpy", "ank", "ndw", "toz",
"als", "ncy", "akw", "ncj", "aki", "akb", "Cfs", "lFy", "avs", "Cdw", "lEz", "atw", "ngz",
"asy", "Ccj", "asj", "zCh", "Chy", "zax", "axy", "Cgz", "awz", "yEx", "yhx", "wdx", "wvx",
"snx", "trx", "lbx", "rfk", "vpw", "xuz", "inA", "rds", "voy", "ilk", "rcw", "voj", "iks",
"rci", "ikg", "rcb", "ika", "afk", "nFw", "tmz", "ivk", "ads", "nEy", "its", "rgy", "nEj",
"isw", "aci", "isi", "acb", "isb", "CFw", "lCz", "ahw", "CEy", "ixw", "agy", "CEj", "iwy",
"agj", "iwj", "Caz", "aiz", "iyz", "ifA", "rFs", "vmy", "idk", "rEw", "vmj", "ics", "rEi",
"icg", "rEb", "ica", "icD", "aFs", "nCy", "ihs", "aEw", "nCj", "igw", "raj", "igi", "aEb",
"igb", "CCy", "aay", "CCj", "iiy", "aaj", "iij", "iFk", "rCw", "vlj", "iEs", "rCi", "iEg",
"rCb", "iEa", "iED", "aCw", "nBj", "iaw", "aCi", "iai", "aCb", "iab", "CBj", "aDj", "ibj",
"iCs", "rBi", "iCg", "rBb", "iCa", "iCD", "aBi", "iDi", "aBb", "iDb", "iBg", "rAr", "iBa",
"iBD", "aAr", "iBr", "iAq", "iAn", "Bfs", "kpy", "Bdw", "koz", "Bcy", "Bcj", "Bhy", "Bgz",
"yCx", "wFx", "sfx", "krx", "Dfk", "lpw", "suz", "Dds", "loy", "Dcw", "loj", "Dci", "Dcb",
"BFw", "kmz", "Dhw", "BEy", "Dgy", "BEj", "Dgj", "Baz", "Diz", "bfA", "nps", "tuy", "bdk",
"now", "tuj", "bcs", "noi", "bcg", "nob", "bca", "bcD", "DFs", "lmy", "bhs", "DEw", "lmj",
"bgw", "DEi", "bgi", "DEb", "bgb", "BCy", "Day", "BCj", "biy", "Daj", "bij", "rpk", "vuw",
"xxj", "jdA", "ros", "vui", "jck", "rog", "vub", "jcc", "roa", "jcE", "roD", "jcC", "bFk",
"nmw", "ttj", "jhk", "bEs", "nmi", "jgs", "rqi", "nmb", "jgg", "bEa", "jga", "bED", "jgD",
"DCw", "llj", "baw", "DCi", "jiw", "bai", "DCb", "jii", "bab", "jib", "BBj", "DDj", "bbj",
"jjj", "jFA", "rms", "vti", "jEk", "rmg", "vtb", "jEc", "rma", "jEE", "rmD", "jEC", "jEB",
"bCs", "nli", "jas", "bCg", "nlb", "jag", "rnb", "jaa", "bCD", "jaD", "DBi", "bDi", "DBb",
"jbi", "bDb", "jbb", "jCk", "rlg", "vsr", "jCc", "rla", "jCE", "rlD", "jCC", "jCB", "bBg",
"nkr", "jDg", "bBa", "jDa", "bBD", "jDD", "DAr", "bBr", "jDr", "jBc", "rkq", "jBE", "rkn",
"jBC", "jBB", "bAq", "jBq", "bAn", "jBn", "jAo", "rkf", "jAm", "jAl", "bAf", "jAv", "Apw",
"kez", "Aoy", "Aoj", "Aqz", "Bps", "kuy", "Bow", "kuj", "Boi", "Bob", "Amy", "Bqy", "Amj",
"Bqj", "Dpk", "luw", "sxj", "Dos", "lui", "Dog", "lub", "Doa", "DoD", "Bmw", "ktj", "Dqw",
"Bmi", "Dqi", "Bmb", "Dqb", "Alj", "Bnj", "Drj", "bpA", "nus", "txi", "bok", "nug", "txb",
"boc", "nua", "boE", "nuD", "boC", "boB", "Dms", "lti", "bqs", "Dmg", "ltb", "bqg", "nvb",
"bqa", "DmD", "bqD", "Bli", "Dni", "Blb", "bri", "Dnb", "brb", "ruk", "vxg", "xyr", "ruc",
"vxa", "ruE", "vxD", "ruC", "ruB", "bmk", "ntg", "twr", "jqk", "bmc", "nta", "jqc", "rva",
"ntD", "jqE", "bmC", "jqC", "bmB", "jqB", "Dlg", "lsr", "bng", "Dla", "jrg", "bna", "DlD",
"jra", "bnD", "jrD", "Bkr", "Dlr", "bnr", "jrr", "rtc", "vwq", "rtE", "vwn", "rtC", "rtB",
"blc", "nsq", "jnc", "blE", "nsn", "jnE", "rtn", "jnC", "blB", "jnB", "Dkq", "blq", "Dkn",
"jnq", "bln", "jnn", "rso", "vwf", "rsm", "rsl", "bko", "nsf", "jlo", "bkm", "jlm", "bkl",
"jll", "Dkf", "bkv", "jlv", "rse", "rsd", "bke", "jku", "bkd", "jkt", "Aey", "Aej", "Auw",
"khj", "Aui", "Aub", "Adj", "Avj", "Bus", "kxi", "Bug", "kxb", "Bua", "BuD", "Ati", "Bvi",
"Atb", "Bvb", "Duk", "lxg", "syr", "Duc", "lxa", "DuE", "lxD", "DuC", "DuB", "Btg", "kwr",
"Dvg", "lxr", "Dva", "BtD", "DvD", "Asr", "Btr", "Dvr", "nxc", "tyq", "nxE", "tyn", "nxC",
"nxB", "Dtc", "lwq", "bvc", "nxq", "lwn", "bvE", "DtC", "bvC", "DtB", "bvB", "Bsq", "Dtq",
"Bsn", "bvq", "Dtn", "bvn", "vyo", "xzf", "vym", "vyl", "nwo", "tyf", "rxo", "nwm", "rxm",
"nwl", "rxl", "Dso", "lwf", "bto", "Dsm", "jvo", "btm", "Dsl", "jvm", "btl", "jvl", "Bsf",
"Dsv", "btv", "jvv", "vye", "vyd", "nwe", "rwu", "nwd", "rwt", "Dse", "bsu", "Dsd", "jtu",
"bst", "jtt", "vyF", "nwF", "rwh", "DsF", "bsh", "jsx", "Ahi", "Ahb", "Axg", "kir", "Axa",
"AxD", "Agr", "Axr", "Bxc", "kyq", "BxE", "kyn", "BxC", "BxB", "Awq", "Bxq", "Awn", "Bxn",
"lyo", "szf", "lym", "lyl", "Bwo", "kyf", "Dxo", "lyv", "Dxm", "Bwl", "Dxl", "Awf", "Bwv",
"Dxv", "tze", "tzd", "lye", "nyu", "lyd", "nyt", "Bwe", "Dwu", "Bwd", "bxu", "Dwt", "bxt",
"tzF", "lyF", "nyh", "BwF", "Dwh", "bwx", "Aiq", "Ain", "Ayo", "kjf", "Aym", "Ayl", "Aif",
"Ayv", "kze", "kzd", "Aye", "Byu", "Ayd", "Byt", "szp"
};
private static final char[] BR_SET = {
'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '*', '+', '-'
};
private static final String[] PDF_TTF = {
"00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111",
"01000", "01001", "01010", "01011", "01100", "01101", "01110", "01111", "10000", "10001",
"10010", "10011", "10100", "10101", "10110", "10111", "11000", "11001", "11010",
"11011", "11100", "11101", "11110", "11111", "01", "1111111101010100", "11111101000101001"
};
/* Left and Right Row Address Pattern from Table 2 */
private static final String[] RAPLR = {
"", "221311", "311311", "312211", "222211", "213211", "214111", "223111",
"313111", "322111", "412111", "421111", "331111", "241111", "232111", "231211", "321211",
"411211", "411121", "411112", "321112", "312112", "311212", "311221", "311131", "311122",
"311113", "221113", "221122", "221131", "221221", "222121", "312121", "321121", "231121",
"231112", "222112", "213112", "212212", "212221", "212131", "212122", "212113", "211213",
"211123", "211132", "211141", "211231", "211222", "211312", "211321", "211411", "212311"
};
/* Centre Row Address Pattern from Table 2 */
private static final String[] RAPC = {
"", "112231", "121231", "122131", "131131", "131221", "132121", "141121",
"141211", "142111", "133111", "132211", "131311", "122311", "123211", "124111", "115111",
"114211", "114121", "123121", "123112", "122212", "122221", "121321", "121411", "112411",
"113311", "113221", "113212", "113122", "122122", "131122", "131113", "122113", "113113",
"112213", "112222", "112312", "112321", "111421", "111331", "111322", "111232", "111223",
"111133", "111124", "111214", "112114", "121114", "121123", "121132", "112132", "112141"
};
private static final int[] MICRO_VARIANTS = {
1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
11, 14, 17, 20, 24, 28, 8, 11, 14, 17, 20, 23, 26, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, 4, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44,
7, 7, 7, 8, 8, 8, 8, 9, 9, 10, 11, 13, 15, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, 8, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50,
0, 0, 0, 7, 7, 7, 7, 15, 15, 24, 34, 57, 84, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294, 7, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294
};
/* rows, columns, error codewords, k-offset */
/* MicroPDF417 coefficients from ISO/IEC 24728:2006 Annex F */
private static final int[] MICROCOEFFS = {
/* k = 7 */
76, 925, 537, 597, 784, 691, 437,
/* k = 8 */
237, 308, 436, 284, 646, 653, 428, 379,
/* k = 9 */
567, 527, 622, 257, 289, 362, 501, 441, 205,
/* k = 10 */
377, 457, 64, 244, 826, 841, 818, 691, 266, 612,
/* k = 11 */
462, 45, 565, 708, 825, 213, 15, 68, 327, 602, 904,
/* k = 12 */
597, 864, 757, 201, 646, 684, 347, 127, 388, 7, 69, 851,
/* k = 13 */
764, 713, 342, 384, 606, 583, 322, 592, 678, 204, 184, 394, 692,
/* k = 14 */
669, 677, 154, 187, 241, 286, 274, 354, 478, 915, 691, 833, 105, 215,
/* k = 15 */
460, 829, 476, 109, 904, 664, 230, 5, 80, 74, 550, 575, 147, 868, 642,
/* k = 16 */
274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65,
/* k = 18 */
279, 577, 315, 624, 37, 855, 275, 739, 120, 297, 312, 202, 560, 321, 233, 756,
760, 573,
/* k = 21 */
108, 519, 781, 534, 129, 425, 681, 553, 422, 716, 763, 693, 624, 610, 310, 691,
347, 165, 193, 259, 568,
/* k = 26 */
443, 284, 887, 544, 788, 93, 477, 760, 331, 608, 269, 121, 159, 830, 446, 893,
699, 245, 441, 454, 325, 858, 131, 847, 764, 169,
/* k = 32 */
361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517,
273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410,
/* k = 38 */
234, 228, 438, 848, 133, 703, 529, 721, 788, 322, 280, 159, 738, 586, 388, 684,
445, 680, 245, 595, 614, 233, 812, 32, 284, 658, 745, 229, 95, 689, 920, 771,
554, 289, 231, 125, 117, 518,
/* k = 44 */
476, 36, 659, 848, 678, 64, 764, 840, 157, 915, 470, 876, 109, 25, 632, 405,
417, 436, 714, 60, 376, 97, 413, 706, 446, 21, 3, 773, 569, 267, 272, 213,
31, 560, 231, 758, 103, 271, 572, 436, 339, 730, 82, 285,
/* k = 50 */
923, 797, 576, 875, 156, 706, 63, 81, 257, 874, 411, 416, 778, 50, 205, 303,
188, 535, 909, 155, 637, 230, 534, 96, 575, 102, 264, 233, 919, 593, 865, 26,
579, 623, 766, 146, 10, 739, 246, 127, 71, 244, 211, 477, 920, 876, 427, 820,
718, 435
};
/* following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24728:2006 tables 10, 11 and 12 */
private static final int[] RAP_TABLE = {
1, 8, 36, 19, 9, 25, 1, 1, 8, 36, 19, 9, 27, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, 47, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, 19, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25,
9, 8, 36, 19, 17, 33, 1, 9, 8, 36, 19, 17, 35, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, 43, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49,
0, 3, 6, 0, 6, 0, 0, 0, 3, 6, 0, 6, 6, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0, 3, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0
};
private StringBuilder binary_string;
private int ecc;
private LinearEncoding symbology = LinearEncoding.CODE_128;
private int[] general_field;
private GeneralFieldMode[] general_field_type;
private int cc_width;
private int[][] pwr928 = new int[69][7];
private int[] codeWords = new int[180];
private int codeWordCount;
private int[] bitStr = new int[13];
private int[] inputData;
private CompositeMode cc_mode;
private DataType linearDataType;
private String linearContent;
private CompositeMode userPreferredMode = CompositeMode.CC_A;
private int target_bitsize;
private int remainder;
private int linearWidth; // width of Code 128 linear
private Integer guardPatternExtraHeight; // for UPC and EAN only
private int separatorHeight = 1; // for all except UPC and EAN
private Integer preferredColumns; // for DataBar Expanded Stacked only
public Composite() {
inputDataType = Symbol.DataType.GS1;
}
@Override
protected boolean gs1Supported() {
return true;
}
/**
* Set the type of linear component included in the composite symbol,
* this will determine how the lower part of the symbol is encoded.
*
* @param linearSymbology The symbology of the linear component
*/
public void setSymbology(LinearEncoding linearSymbology) {
symbology = linearSymbology;
}
/**
* Returns the type of linear component included in the composite symbol.
*
* @return the type of linear component included in the composite symbol
*/
public LinearEncoding getSymbology() {
return symbology;
}
/**
* Sets the data type of the linear component. Note that not all data types
* are valid for all linear component types.
*
* @param linearDataType the data type of the linear component
*/
public void setLinearDataType(DataType linearDataType) {
this.linearDataType = linearDataType;
}
/**
* Returns the data type of the linear component.
*
* @return the data type of the linear component
*/
public DataType getLinearDataType() {
return linearDataType;
}
/**
* Set the data to be encoded in the linear component of the composite symbol.
*
* @param linearContent the linear data in GS1 format
*/
public void setLinearContent(String linearContent) {
this.linearContent = linearContent;
}
/**
* Returns the data encoded in the linear component of the composite symbol.
*
* @return the data encoded in the linear component of the composite symbol
*/
public String getLinearContent() {
return linearContent;
}
/**
* Set the preferred encoding method for the 2D component of the composite symbol.
* This value may be ignored if the amount of data supplied is too big for the selected
* encoding. Mode CC-C can only be used with a Code 128 linear component.
*
* @param userMode Preferred mode
*/
public void setPreferredMode(CompositeMode userMode) {
userPreferredMode = userMode;
}
/**
* Returns the preferred encoding method for the 2D component of the composite symbol.
*
* @return the preferred encoding method for the 2D component of the composite symbol
*/
public CompositeMode getPreferredMode() {
return userPreferredMode;
}
/**
* Returns the actual 2D component encoding method that was used.
* Only available after {@link #setContent(String)} has been called.
*
* @return the actual 2D component encoding method that was used
*/
public CompositeMode getModeUsed() {
return cc_mode;
}
/**
* Sets the extra height used for the guard patterns. Used only when the linear component is
* {@link LinearEncoding#EAN}, {@link LinearEncoding#UPCA} or {@link LinearEncoding#UPCE}.
*
* @param guardPatternExtraHeight the extra height used for the guard patterns
*/
public void setGuardPatternExtraHeight(int guardPatternExtraHeight) {
this.guardPatternExtraHeight = guardPatternExtraHeight;
}
/**
* Sets the separator height to use between the linear component and the 2D component. Used
* for all types of linear components except {@link LinearEncoding#EAN},
* {@link LinearEncoding#UPCA} or {@link LinearEncoding#UPCE}. The default value is {@code 1}.
*
* @param separatorHeight the separator height to use between the linear component and the 2D component
*/
public void setSeparatorHeight(int separatorHeight) {
if (separatorHeight < 1) {
throw new IllegalArgumentException("Invalid Composite separator height: " + separatorHeight);
}
this.separatorHeight = separatorHeight;
}
/**
* Sets the preferred width of a stacked symbol by selecting the number of "columns" or symbol
* segments in each row of data. Valid only when using a {@link LinearEncoding#DATABAR_EXPANDED_STACK}
* linear component.
*
* @param columns the number of segments in each row
*/
public void setPreferredColumns(int columns) {
if (columns < 1 || columns > 10) {
throw new IllegalArgumentException("Invalid column count: " + columns);
}
this.preferredColumns = columns;
}
@Override
protected void encode() {
List < Rectangle2D.Double > combine_rect = new ArrayList<>();
List < TextBox > combine_txt = new ArrayList<>();
int top_shift = 0; // 2D component x-coordinate shift
int bottom_shift = 0; // linear component x-coordinate shift
linearWidth = 0;
if (linearContent.isEmpty()) {
throw new OkapiException("No linear data set");
}
// Manage composite component encoding first
encodeComposite();
// Then encode linear component
Symbol linear;
switch (symbology) {
case UPCA:
Upc upca = new Upc();
upca.setMode(Upc.Mode.UPCA);
upca.setLinkageFlag(true);
if (guardPatternExtraHeight != null) {
upca.setGuardPatternExtraHeight(guardPatternExtraHeight);
}
linear = upca;
bottom_shift = 6;
top_shift = 3;
break;
case UPCE:
Upc upce = new Upc();
upce.setMode(Upc.Mode.UPCE);
upce.setLinkageFlag(true);
if (guardPatternExtraHeight != null) {
upce.setGuardPatternExtraHeight(guardPatternExtraHeight);
}
linear = upce;
bottom_shift = 6;
top_shift = 3;
break;
case EAN:
Ean ean = new Ean();
if (eanCalculateVersion() == 8) {
ean.setMode(Ean.Mode.EAN8);
bottom_shift = (cc_mode == CompositeMode.CC_A ? 4 : 14); // left row address pattern omitted in 3-column CC-A
} else {
ean.setMode(Ean.Mode.EAN13);
bottom_shift = 6;
top_shift = 3;
}
ean.setLinkageFlag(true);
if (guardPatternExtraHeight != null) {
ean.setGuardPatternExtraHeight(guardPatternExtraHeight);
}
linear = ean;
break;
case CODE_128:
Code128 code128 = new Code128();
switch (cc_mode) {
case CC_A:
code128.setCca();
break;
case CC_B:
code128.setCcb();
break;
case CC_C:
code128.setCcc();
bottom_shift = 7;
break;
}
code128.setDataType(Symbol.DataType.GS1);
linear = code128;
break;
case DATABAR_14:
DataBar14 dataBar14 = new DataBar14();
dataBar14.setLinkageFlag(true);
dataBar14.setMode(Mode.LINEAR);
linear = dataBar14;
bottom_shift = 4;
break;
case DATABAR_14_STACK_OMNI:
DataBar14 dataBar14SO = new DataBar14();
dataBar14SO.setLinkageFlag(true);
dataBar14SO.setMode(Mode.OMNI);
dataBar14SO.setSeparatorHeight(separatorHeight);
linear = dataBar14SO;
top_shift = 1;
break;
case DATABAR_14_STACK:
DataBar14 dataBar14S = new DataBar14();
dataBar14S.setLinkageFlag(true);
dataBar14S.setMode(Mode.STACKED);
dataBar14S.setSeparatorHeight(separatorHeight);
linear = dataBar14S;
top_shift = 1;
break;
case DATABAR_LIMITED:
DataBarLimited dataBarLimited = new DataBarLimited();
dataBarLimited.setLinkageFlag();
linear = dataBarLimited;
top_shift = 1;
bottom_shift = (cc_mode == CompositeMode.CC_A ? 0 : 10); // left row address pattern omitted in 3-column CC-A
break;
case DATABAR_EXPANDED:
DataBarExpanded dataBarExpanded = new DataBarExpanded();
dataBarExpanded.setLinkageFlag(true);
dataBarExpanded.setStacked(false);
linear = dataBarExpanded;
top_shift = 2;
break;
case DATABAR_EXPANDED_STACK:
DataBarExpanded dataBarExpandedS = new DataBarExpanded();
dataBarExpandedS.setLinkageFlag(true);
dataBarExpandedS.setStacked(true);
if (preferredColumns != null) {
dataBarExpandedS.setPreferredColumns(preferredColumns);
}
linear = dataBarExpandedS;
top_shift = 2;
break;
default:
throw new OkapiException("Linear symbol not recognised");
}
if (linearDataType != null) {
linear.setDataType(linearDataType);
}
copyPropertiesTo(linear);
linear.setContent(linearContent);
if (symbology == LinearEncoding.CODE_128) {
linearWidth = linear.symbol_width;
if (cc_mode == CompositeMode.CC_C) {
/* Width of composite component depends on width of linear component, so recalculate. */
row_count = 0;
rectangles.clear();
symbol_height = 0;
symbol_width = 0;
encodeInfo.setLength(0);
encodeComposite();
} else {
if (linearWidth > symbol_width) {
top_shift = (linearWidth - symbol_width) / 2;
}
}
}
for (Rectangle2D.Double orig : rectangles) {
combine_rect.add(new Rectangle2D.Double(orig.x + top_shift, orig.y, orig.width, orig.height));
}
int extraSepHeight = separatorHeight - 1;
if (extraSepHeight > 0 && (symbology == LinearEncoding.EAN || symbology == LinearEncoding.UPCA || symbology == LinearEncoding.UPCE)) {
throw new OkapiException("Composite EAN and UPC separator height cannot be changed");
}
for (Rectangle2D.Double orig : linear.rectangles) {
double h = orig.height + (extraSepHeight > 0 && orig.height == 1 ? extraSepHeight : 0);
double y = orig.y + (extraSepHeight > 0 && orig.height != 1 ? extraSepHeight : 0);
combine_rect.add(new Rectangle2D.Double(orig.x + bottom_shift, y + symbol_height, orig.width, h));
}
for (TextBox orig : linear.texts) {
combine_txt.add(new TextBox(orig.x + bottom_shift, orig.y + symbol_height + extraSepHeight, orig.width, orig.text, humanReadableAlignment));
}
int max_x = 0;
for (Rectangle2D.Double rect : combine_rect) {
if (rect.x + rect.width > max_x) {
max_x = (int) Math.ceil(rect.x + rect.width);
}
}
rectangles = combine_rect;
texts = combine_txt;
symbol_height += linear.symbol_height + extraSepHeight;
symbol_width = max_x;
info(linear.getEncodeInfo());
}
private void copyPropertiesTo(Symbol linear) {
linear.setBarHeight(this.getBarHeight());
linear.setHumanReadableLocation(this.getHumanReadableLocation());
linear.setHumanReadableAlignment(this.getHumanReadableAlignment());
linear.setModuleWidth(this.getModuleWidth());
linear.setQuietZoneHorizontal(this.getQuietZoneHorizontal());
linear.setQuietZoneVertical(this.getQuietZoneVertical());
if (this.getFont() != null) {
linear.setFont(this.getFont());
} else {
linear.setFontName(this.getFontName());
linear.setFontSize(this.getFontSize());
}
}
private void encodeComposite() {
if (content.length() > 2990) {
throw new OkapiException("2D component input data too long");
}
inputData = toBytes(content, StandardCharsets.US_ASCII);
if (inputData == null) {
throw new OkapiException("Invalid characters in input data");
}
cc_mode = userPreferredMode;
if (cc_mode == CompositeMode.CC_C && symbology != LinearEncoding.CODE_128) {
/* CC-C can only be used with a GS1-128 linear part */
throw new OkapiException("Invalid mode (CC-C only valid with GS1-128 linear component)");
}
switch (symbology) {
/* Determine width of 2D component according to ISO/IEC 24723 Table 1 */
case EAN:
if (eanCalculateVersion() == 8) {
cc_width = 3;
} else {
cc_width = 4;
}
break;
case UPCE:
case DATABAR_14_STACK_OMNI:
case DATABAR_14_STACK:
cc_width = 2;
break;
case DATABAR_LIMITED:
cc_width = 3;
break;
case CODE_128:
case DATABAR_14:
case DATABAR_EXPANDED:
case UPCA:
case DATABAR_EXPANDED_STACK:
cc_width = 4;
break;
}
infoLine("Composite Width: " + cc_width);
if (cc_mode == CompositeMode.CC_A && !cc_binary_string()) {
cc_mode = CompositeMode.CC_B;
}
if (cc_mode == CompositeMode.CC_B) { /* If the data didn't fit into CC-A it is recalculated for CC-B */
if (!cc_binary_string()) {
if (symbology != LinearEncoding.CODE_128) {
throw new OkapiException("Input too long");
} else {
cc_mode = CompositeMode.CC_C;
}
}
}
if (cc_mode == CompositeMode.CC_C) {
/* If the data didn't fit in CC-B (and linear
* part is GS1-128) it is recalculated for CC-C */
if (!cc_binary_string()) {
throw new OkapiException("Input too long");
}
}
switch (cc_mode) { /* Note that ecc_level is only relevant to CC-C */
case CC_A:
cc_a();
infoLine("Composite Type: CC-A");
break;
case CC_B:
cc_b();
infoLine("Composite Type: CC-B");
break;
case CC_C:
cc_c();
infoLine("Composite Type: CC-C");
break;
}
super.plotSymbol();
}
@Override
protected void plotSymbol() {
// empty
}
private int eanCalculateVersion() {
/* Determine if EAN-8 or EAN-13 is being used */
int length = 0;
int i;
boolean latch;
latch = true;
for (i = 0; i < linearContent.length(); i++) {
if ((linearContent.charAt(i) >= '0') && (linearContent.charAt(i) <= '9')) {
if (latch) {
length++;
}
} else {
latch = false;
}
}
if (length <= 7) {
// EAN-8
return 8;
} else {
// EAN-13
return 13;
}
}
private boolean calculateSymbolSize() {
int i;
int binary_length = binary_string.length();
if (cc_mode == CompositeMode.CC_A) {
/* CC-A 2D component - calculate remaining space */
switch (cc_width) {
case 2:
if (binary_length > 167) {
return false;
}
if (binary_length <= 167) {
target_bitsize = 167;
}
if (binary_length <= 138) {
target_bitsize = 138;
}
if (binary_length <= 118) {
target_bitsize = 118;
}
if (binary_length <= 108) {
target_bitsize = 108;
}
if (binary_length <= 88) {
target_bitsize = 88;
}
if (binary_length <= 78) {
target_bitsize = 78;
}
if (binary_length <= 59) {
target_bitsize = 59;
}
break;
case 3:
if (binary_length > 167) {
return false;
}
if (binary_length <= 167) {
target_bitsize = 167;
}
if (binary_length <= 138) {
target_bitsize = 138;
}
if (binary_length <= 118) {
target_bitsize = 118;
}
if (binary_length <= 98) {
target_bitsize = 98;
}
if (binary_length <= 78) {
target_bitsize = 78;
}
break;
case 4:
if (binary_length > 197) {
return false;
}
if (binary_length <= 197) {
target_bitsize = 197;
}
if (binary_length <= 167) {
target_bitsize = 167;
}
if (binary_length <= 138) {
target_bitsize = 138;
}
if (binary_length <= 108) {
target_bitsize = 108;
}
if (binary_length <= 78) {
target_bitsize = 78;
}
break;
}
}
if (cc_mode == CompositeMode.CC_B) {
/* CC-B 2D component - calculated from ISO/IEC 24728 Table 1 */
switch (cc_width) {
case 2:
if (binary_length > 336) {
return false;
}
if (binary_length <= 336) {
target_bitsize = 336;
}
if (binary_length <= 296) {
target_bitsize = 296;
}
if (binary_length <= 256) {
target_bitsize = 256;
}
if (binary_length <= 208) {
target_bitsize = 208;
}
if (binary_length <= 160) {
target_bitsize = 160;
}
if (binary_length <= 104) {
target_bitsize = 104;
}
if (binary_length <= 56) {
target_bitsize = 56;
}
break;
case 3:
if (binary_length > 768) {
return false;
}
if (binary_length <= 768) {
target_bitsize = 768;
}
if (binary_length <= 648) {
target_bitsize = 648;
}
if (binary_length <= 536) {
target_bitsize = 536;
}
if (binary_length <= 416) {
target_bitsize = 416;
}
if (binary_length <= 304) {
target_bitsize = 304;
}
if (binary_length <= 208) {
target_bitsize = 208;
}
if (binary_length <= 152) {
target_bitsize = 152;
}
if (binary_length <= 112) {
target_bitsize = 112;
}
if (binary_length <= 72) {
target_bitsize = 72;
}
if (binary_length <= 32) {
target_bitsize = 32;
}
break;
case 4:
if (binary_length > 1184) {
return false;
}
if (binary_length <= 1184) {
target_bitsize = 1184;
}
if (binary_length <= 1016) {
target_bitsize = 1016;
}
if (binary_length <= 840) {
target_bitsize = 840;
}
if (binary_length <= 672) {
target_bitsize = 672;
}
if (binary_length <= 496) {
target_bitsize = 496;
}
if (binary_length <= 352) {
target_bitsize = 352;
}
if (binary_length <= 264) {
target_bitsize = 264;
}
if (binary_length <= 208) {
target_bitsize = 208;
}
if (binary_length <= 152) {
target_bitsize = 152;
}
if (binary_length <= 96) {
target_bitsize = 96;
}
if (binary_length <= 56) {
target_bitsize = 56;
}
break;
}
}
if (cc_mode == CompositeMode.CC_C) {
/* CC-C 2D Component is a bit more complex! */
int byte_length, codewords_used, ecc_level, ecc_codewords, rows;
int codewords_total, target_codewords, target_bytesize;
byte_length = binary_length / 8;
if (binary_length % 8 != 0) {
byte_length++;
}
codewords_used = (byte_length / 6) * 5;
codewords_used += byte_length % 6;
ecc_level = 7;
if (codewords_used <= 1280) {
ecc_level = 6;
}
if (codewords_used <= 640) {
ecc_level = 5;
}
if (codewords_used <= 320) {
ecc_level = 4;
}
if (codewords_used <= 160) {
ecc_level = 3;
}
if (codewords_used <= 40) {
ecc_level = 2;
}
ecc = ecc_level;
ecc_codewords = 1;
for (i = 1; i <= (ecc_level + 1); i++) {
ecc_codewords *= 2;
}
codewords_used += ecc_codewords;
codewords_used += 3;
if (linearWidth == 0) {
/* Linear component not yet calculated */
cc_width = (int)(0.5 + Math.sqrt((codewords_used) / 3.0));
} else {
cc_width = (linearWidth - 53) / 17;
}
if ((codewords_used / cc_width) > 90) {
/* stop the symbol from becoming too high */
cc_width = cc_width + 1;
}
rows = codewords_used / cc_width;
if (codewords_used % cc_width != 0) {
rows++;
}
while (cc_width > (3 * rows)) {
/* stop the symbol from becoming too wide (section 10) */
cc_width--;
rows = codewords_used / cc_width;
if (codewords_used % cc_width != 0) {
rows++;
}
}
codewords_total = cc_width * rows;
target_codewords = codewords_total - ecc_codewords;
target_codewords -= 3;
target_bytesize = 6 * (target_codewords / 5);
target_bytesize += target_codewords % 5;
target_bitsize = 8 * target_bytesize;
}
remainder = target_bitsize - binary_length;
return true;
}
private boolean cc_binary_string() {
/* Handles all data encodation from section 5 of ISO/IEC 24723 */
int encoding_method, read_posn, d1, d2, value, alpha_pad;
int i, j, ai_crop, fnc1_latch;
int group_val;
int ai90_mode;
boolean latch;
int numeric_value, table3_letter;
int latchOffset;
encoding_method = 1;
read_posn = 0;
ai_crop = 0;
fnc1_latch = 0;
alpha_pad = 0;
ai90_mode = 0;
ecc = 0;
value = 0;
target_bitsize = 0;
if (inputData[0] == '1' && (inputData[1] == '0' || inputData[1] == '1' || inputData[1] == '7') && inputData.length >= 8) {
/* Source starts (10), (11) or (17) */
encoding_method = 2;
}
if (inputData[0] == '9' && inputData[1] == '0') {
/* Source starts (90) */
encoding_method = 3;
}
info("Composite Encodation: ");
switch (encoding_method) {
case 1:
infoLine("0");
break;
case 2:
infoLine("10");
break;
case 3:
infoLine("11");
break;
}
binary_string = new StringBuilder();
if (encoding_method == 1) {
binary_string.append('0');
}
if (encoding_method == 2) {
/* Encoding Method field "10" - date and lot number */
binary_string.append("10");
if (inputData[1] == '0') {
/* No date data */
binary_string.append("11");
} else {
/* Production Date (11) or Expiration Date (17) */
group_val = ((10 * (inputData[2] - '0')) + (inputData[3] - '0')) * 384;
group_val += (((10 * (inputData[4] - '0')) + (inputData[5] - '0')) - 1) * 32;
group_val += (10 * (inputData[6] - '0')) + (inputData[7] - '0');
binaryAppend(binary_string, group_val, 16);
if (inputData[1] == '1') {
/* Production Date AI 11 */
binary_string.append('0');
} else {
/* Expiration Date AI 17 */
binary_string.append('1');
}
read_posn = 8;
}
if((read_posn + 2) < inputData.length) {
if ((inputData[read_posn] == '1') && (inputData[read_posn + 1] == '0')) {
/* Followed by AI 10 - strip this from general field */
read_posn += 2;
} else {
/* An FNC1 character needs to be inserted in the general field */
fnc1_latch = 1;
}
} else {
fnc1_latch = 1;
}
}
if (encoding_method == 3) {
/* Encodation Method field of "11" - AI 90 */
/* "This encodation method may be used if an element string with an AI
90 occurs at the start of the data message, and if the data field
following the two-digit AI 90 starts with an alphanumeric string which
complies with a specific format." (para 5.2.2) */
j = inputData.length;
for (i = inputData.length - 1; i > 2; i--) {
if (inputData[i] == FNC1) {
j = i;
}
}
int[] ninety = Arrays.copyOfRange(inputData, 2, j);
/* Find out if the AI 90 data is alphabetic or numeric or both */
int alpha = 0;
int alphanum = 0;
int numeric = 0;
for (i = 0; i < ninety.length; i++) {
if ((ninety[i] >= 'A') && (ninety[i] <= 'Z')) {
/* Character is alphabetic */
alpha += 1;
}
if ((ninety[i] >= '0') && (ninety[i] <= '9')) {
/* Character is numeric */
numeric += 1;
}
switch (ninety[i]) {
case '*':
case ',':
case '-':
case '.':
case '/':
alphanum += 1;
break;
}
if (!((ninety[i] >= '0' && ninety[i] <= '9') || (ninety[i] >= 'A' && ninety[i] <= 'Z'))) {
if (ninety[i] != '*' && ninety[i] != ',' && ninety[i] != '-' && ninety[i] != '.' && ninety[i] != '/') {
/* An Invalid AI 90 character */
throw new OkapiException("Invalid AI 90 data");
}
}
}
/* must start with 0, 1, 2 or 3 digits followed by an uppercase character */
int test1 = -1;
for (i = 3; i >= 0; i--) {
if (i < ninety.length && ninety[i] >= 'A' && ninety[i] <= 'Z') {
test1 = i; // first uppercase alpha position
}
}
int test2 = 0;
for (i = 0; i < test1; i++) {
if (!(ninety[i] >= '0' && ninety[i] <= '9')) {
test2 = 1; // non-digit before first uppercase alpha
}
}
/* leading zeros are not permitted */
int test3 = 0;
if (test1 >= 1 && ninety[0] == '0') {
test3 = 1; // leading zero found
}
if (test1 != -1 && test2 == 0 && test3 == 0) {
/* Encodation method "11" can be used */
binary_string.append("11");
numeric -= test1;
alpha--;
/* Decide on numeric, alpha or alphanumeric mode */
/* Alpha mode is a special mode for AI 90 */
if (alphanum > 0) {
/* Alphanumeric mode */
binary_string.append('0');
ai90_mode = 1;
} else {
if (alpha > numeric) {
/* Alphabetic mode */
binary_string.append("11");
ai90_mode = 2;
} else {
/* Numeric mode */
binary_string.append("10");
ai90_mode = 3;
}
}
int next_ai_posn = 2 + ninety.length;
if (next_ai_posn < inputData.length && inputData[next_ai_posn] == FNC1) {
/* There are more AIs afterwards */
if (next_ai_posn + 2 < inputData.length && inputData[next_ai_posn + 1] == '2' && inputData[next_ai_posn + 2] == '1') {
/* AI 21 follows */
ai_crop = 1;
}
if (next_ai_posn + 4 < inputData.length && inputData[next_ai_posn + 1] == '8' && inputData[next_ai_posn + 2] == '0' && inputData[next_ai_posn + 3] == '0' && inputData[next_ai_posn + 4] == '4') {
/* AI 8004 follows */
ai_crop = 2;
}
}
switch (ai_crop) {
case 0:
binary_string.append('0');
break;
case 1:
binary_string.append("10");
break;
case 2:
binary_string.append("11");
break;
}
numeric_value = 0;
for (i = 0; i < test1; i++) {
numeric_value *= 10;
numeric_value += ninety[i] - '0';
}
table3_letter = -1;
if (numeric_value < 31) {
table3_letter = "BDHIJKLNPQRSTVWZ".indexOf(ninety[test1]);
}
if (table3_letter != -1) {
/* Encoding can be done according to 5.2.2 c) 2) */
/* five bit binary string representing value before letter */
binaryAppend(binary_string, numeric_value, 5);
/* followed by four bit representation of letter from Table 3 */
binaryAppend(binary_string, table3_letter, 4);
} else {
/* Encoding is done according to 5.2.2 c) 3) */
binary_string.append("11111");
/* ten bit representation of number */
binaryAppend(binary_string, numeric_value, 10);
/* five bit representation of ASCII character */
binaryAppend(binary_string, ninety[test1] - 65, 5);
}
read_posn = test1 + 3;
} else {
/* Use general field encodation instead */
binary_string.append('0');
read_posn = 0;
}
/* Now encode the rest of the AI 90 data field */
if (ai90_mode == 2) {
/* Alpha encodation (section 5.2.3) */
do {
if (inputData[read_posn] >= '0' && inputData[read_posn] <= '9') {
binaryAppend(binary_string, inputData[read_posn] + 4, 6);
}
if (inputData[read_posn] >= 'A' && inputData[read_posn] <= 'Z') {
binaryAppend(binary_string, inputData[read_posn] - 65, 5);
}
if (inputData[read_posn] == FNC1) {
binary_string.append("11111");
}
read_posn++;
} while (inputData[read_posn - 1] != FNC1 && read_posn < inputData.length);
alpha_pad = 1; /* This is overwritten if a general field is encoded */
}
if (ai90_mode == 1) {
/* Alphanumeric mode */
do {
if (inputData[read_posn] >= '0' && inputData[read_posn] <= '9') {
binaryAppend(binary_string, inputData[read_posn] - 43, 5);
}
if (inputData[read_posn] >= 'A' && inputData[read_posn] <= 'Z') {
binaryAppend(binary_string, inputData[read_posn] - 33, 6);
}
switch (inputData[read_posn]) {
case FNC1:
binary_string.append("01111");
break;
case '*':
binary_string.append("111010");
break;
case ',':
binary_string.append("111011");
break;
case '-':
binary_string.append("111100");
break;
case '.':
binary_string.append("111101");
break;
case '/':
binary_string.append("111110");
break;
}
read_posn++;
} while (inputData[read_posn - 1] != FNC1 && read_posn < inputData.length);
}
read_posn += (2 * ai_crop);
} // end encodation method "11" (AI 90) handling
/* The compressed data field has been processed if appropriate - the
rest of the data (if any) goes into a general-purpose data compaction field */
j = 0;
if (fnc1_latch == 1) {
/* Encodation method "10" has been used but it is not followed by
AI 10, so a FNC1 character needs to be added */
general_field = new int[inputData.length - read_posn + 1];
general_field[0] = FNC1;
System.arraycopy(inputData, read_posn, general_field, 1, general_field.length - 1);
} else {
general_field = Arrays.copyOfRange(inputData, read_posn, inputData.length);
}
latch = false;
if (general_field.length != 0) {
alpha_pad = 0;
general_field_type = new GeneralFieldMode[general_field.length];
for (i = 0; i < general_field.length; i++) {
/* Table 13 - ISO/IEC 646 encodation */
if (general_field[i] == FNC1) {
/* FNC1 can be encoded in any system */
general_field_type[i] = GeneralFieldMode.ANY_ENC;
} else if (general_field[i] < ' ' || general_field[i] > 'z') {
general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
} else {
general_field_type[i] = GeneralFieldMode.ISOIEC;
}
if (general_field[i] == '#') {
general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
if (general_field[i] == '$') {
general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
if (general_field[i] == '@') {
general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
if (general_field[i] == 92) {
general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
if (general_field[i] == '^') {
general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
if (general_field[i] == 96) {
general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
/* Table 12 - Alphanumeric encodation */
if (general_field[i] >= 'A' && general_field[i] <= 'Z') {
general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
if (general_field[i] == '*') {
general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
if (general_field[i] == ',') {
general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
if (general_field[i] == '-') {
general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
if (general_field[i] == '.') {
general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
if (general_field[i] == '/') {
general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
/* Numeric encodation */
if (general_field[i] >= '0' && general_field[i] <= '9') {
general_field_type[i] = GeneralFieldMode.ANY_ENC;
}
}
if (latch) {
/* Invalid characters in input data */
throw new OkapiException("Invalid characters in input data");
}
for (i = 0; i < general_field.length - 1; i++) {
if (general_field_type[i] == GeneralFieldMode.ISOIEC && general_field[i + 1] == FNC1) {
general_field_type[i + 1] = GeneralFieldMode.ISOIEC;
}
}
for (i = 0; i < general_field.length - 1; i++) {
if (general_field_type[i] == GeneralFieldMode.ALPHA_OR_ISO && general_field[i + 1] == FNC1) {
general_field_type[i + 1] = GeneralFieldMode.ALPHA_OR_ISO;
}
}
latch = applyGeneralFieldRules();
i = 0;
do {
switch (general_field_type[i]) {
case NUMERIC:
if (i != 0 && general_field_type[i - 1] != GeneralFieldMode.NUMERIC && general_field[i - 1] != FNC1) {
binary_string.append("000"); /* Numeric latch */
}
if (general_field[i] != FNC1) {
d1 = general_field[i] - '0';
} else {
d1 = 10;
}
if (i < general_field.length - 1) {
if (general_field[i + 1] != FNC1) {
d2 = general_field[i + 1] - '0';
} else {
d2 = 10;
}
} else {
d2 = 10;
}
if (d1 != 10 || d2 != 10) {
/* If (d1==10)&&(d2==10) then input is either FNC1,FNC1 or FNC1,EOL */
value = (11 * d1) + d2 + 8;
binaryAppend(binary_string, value, 7);
i += 2;
}
break;
case ALPHA:
if (i == 0 || general_field_type[i - 1] == GeneralFieldMode.NUMERIC || general_field[i - 1] == FNC1) {
binary_string.append("0000"); /* Alphanumeric latch */
} else if (general_field_type[i - 1] == GeneralFieldMode.ISOIEC) {
binary_string.append("00100"); /* ISO/IEC 646 latch */
}
if (general_field[i] >= '0' && general_field[i] <= '9') {
value = general_field[i] - 43;
binaryAppend(binary_string, value, 5);
}
if (general_field[i] >= 'A' && general_field[i] <= 'Z') {
value = general_field[i] - 33;
binaryAppend(binary_string, value, 6);
}
if (general_field[i] == FNC1) {
binary_string.append("01111"); /* FNC1/Numeric latch */
}
if (general_field[i] == '*') {
binary_string.append("111010"); /* asterisk */
}
if (general_field[i] == ',') {
binary_string.append("111011"); /* comma */
}
if (general_field[i] == '-') {
binary_string.append("111100"); /* minus or hyphen */
}
if (general_field[i] == '.') {
binary_string.append("111101"); /* period or full stop */
}
if (general_field[i] == '/') {
binary_string.append("111110"); /* slash or solidus */
}
i++;
break;
case ISOIEC:
if (i == 0 || general_field_type[i - 1] == GeneralFieldMode.NUMERIC || general_field[i - 1] == FNC1) {
binary_string.append("0000"); /* Alphanumeric latch */
binary_string.append("00100"); /* ISO/IEC 646 latch */
} else if (general_field_type[i - 1] == GeneralFieldMode.ALPHA) {
binary_string.append("00100"); /* ISO/IEC 646 latch */
}
if (general_field[i] >= '0' && general_field[i] <= '9') {
value = general_field[i] - 43;
binaryAppend(binary_string, value, 5);
}
if (general_field[i] >= 'A' && general_field[i] <= 'Z') {
value = general_field[i] - 1;
binaryAppend(binary_string, value, 7);
}
if (general_field[i] >= 'a' && general_field[i] <= 'z') {
value = general_field[i] - 7;
binaryAppend(binary_string, value, 7);
}
if (general_field[i] == FNC1) {
binary_string.append("01111"); /* FNC1/Numeric latch */
}
if (general_field[i] == '!') {
binary_string.append("11101000"); /* exclamation mark */
}
if (general_field[i] == 34) {
binary_string.append("11101001"); /* quotation mark */
}
if (general_field[i] == 37) {
binary_string.append("11101010"); /* percent sign */
}
if (general_field[i] == '&') {
binary_string.append("11101011"); /* ampersand */
}
if (general_field[i] == 39) {
binary_string.append("11101100"); /* apostrophe */
}
if (general_field[i] == '(') {
binary_string.append("11101101"); /* left parenthesis */
}
if (general_field[i] == ')') {
binary_string.append("11101110"); /* right parenthesis */
}
if (general_field[i] == '*') {
binary_string.append("11101111"); /* asterisk */
}
if (general_field[i] == '+') {
binary_string.append("11110000"); /* plus sign */
}
if (general_field[i] == ',') {
binary_string.append("11110001"); /* comma */
}
if (general_field[i] == '-') {
binary_string.append("11110010"); /* minus or hyphen */
}
if (general_field[i] == '.') {
binary_string.append("11110011"); /* period or full stop */
}
if (general_field[i] == '/') {
binary_string.append("11110100"); /* slash or solidus */
}
if (general_field[i] == ':') {
binary_string.append("11110101"); /* colon */
}
if (general_field[i] == ';') {
binary_string.append("11110110"); /* semicolon */
}
if (general_field[i] == '<') {
binary_string.append("11110111"); /* less-than sign */
}
if (general_field[i] == '=') {
binary_string.append("11111000"); /* equals sign */
}
if (general_field[i] == '>') {
binary_string.append("11111001"); /* greater-than sign */
}
if (general_field[i] == '?') {
binary_string.append("11111010"); /* question mark */
}
if (general_field[i] == '_') {
binary_string.append("11111011"); /* underline or low line */
}
if (general_field[i] == ' ') {
binary_string.append("11111100"); /* space */
}
i++;
break;
}
latchOffset = 0;
if (latch) {
latchOffset = 1;
}
} while (i + latchOffset < general_field.length);
} // end general-purpose compaction data handling
if (!calculateSymbolSize()) {
return false;
}
if (latch) {
/* There is still one more numeric digit to encode */
i = general_field.length - 1;
if (general_field[i] == FNC1) {
binary_string.append("000001111");
} else {
if (remainder >= 4 && remainder <= 6) {
/* ISO/IEC 24723:2010 5.4.1 c) 2) "If four to six bits remain, add 1 to the digit value and encode the result in the next four bits" */
d1 = general_field[i] - '0';
value = d1 + 1;
binaryAppend(binary_string, value, 4);
} else {
d1 = general_field[i] - '0';
d2 = 10;
value = (11 * d1) + d2 + 8;
binaryAppend(binary_string, value, 7);
/* This may push the symbol up to the next size */
}
}
}
if (binary_string.length() > 11805) { /* (2361 * 5) */
throw new OkapiException("Input too long");
}
/* size of the symbol may have changed when adding data in the above sequence */
if (!calculateSymbolSize()) {
return false;
}
infoLine("Composite Binary Length: " + binary_string.length());
logBinaryString();
if (binary_string.length() < target_bitsize) {
/* Now add padding to binary string */
if (alpha_pad == 1) {
/* Extra FNC1 character required after Alpha encodation (section 5.2.3) */
binary_string.append("11111");
alpha_pad = 0;
}
if (general_field.length == 0 || general_field_type[general_field.length - 1] == GeneralFieldMode.NUMERIC) {
/* Latch from numeric to alphanumeric */
binary_string.append("0000");
}
while (binary_string.length() < target_bitsize) {
/* Latch back and forth between alphanumeric and ISO/IEC encodation (section 5.3.4) */
binary_string.append("00100");
}
binary_string.delete(target_bitsize, binary_string.length());
}
return true;
}
private void logBinaryString() {
int i, nibble;
/* Display binary string as hexadecimal */
info("Composite Binary String: ");
nibble = 0;
for(i = 0; i < binary_string.length(); i++) {
switch (i % 4) {
case 0:
if (binary_string.charAt(i) == '1') {
nibble += 8;
}
break;
case 1:
if (binary_string.charAt(i) == '1') {
nibble += 4;
}
break;
case 2:
if (binary_string.charAt(i) == '1') {
nibble += 2;
}
break;
case 3:
if (binary_string.charAt(i) == '1') {
nibble += 1;
}
info(Integer.toHexString(nibble));
nibble = 0;
break;
}
}
if ((binary_string.length() % 4) != 0) {
info(Integer.toHexString(nibble));
}
infoLine();
}
private boolean applyGeneralFieldRules() {
/* Attempts to apply encoding rules from sections 7.2.5.5.1 to 7.2.5.5.3
of ISO/IEC 24724:2006 */
int block_count, i, j, k;
GeneralFieldMode current, next, last;
int[] blockLength = new int[200];
GeneralFieldMode[] blockType = new GeneralFieldMode[200];
block_count = 0;
blockLength[block_count] = 1;
blockType[block_count] = general_field_type[0];
for (i = 1; i < general_field.length; i++) {
current = general_field_type[i];
last = general_field_type[i - 1];
if (current == last) {
blockLength[block_count] = blockLength[block_count] + 1;
} else {
block_count++;
blockLength[block_count] = 1;
blockType[block_count] = general_field_type[i];
}
}
block_count++;
for (i = 0; i < block_count; i++) {
current = blockType[i];
next = blockType[i + 1];
if ((current == GeneralFieldMode.ISOIEC) && (i != (block_count - 1))) {
if ((next == GeneralFieldMode.ANY_ENC) && (blockLength[i + 1] >= 4)) {
blockType[i + 1] = GeneralFieldMode.NUMERIC;
}
if ((next == GeneralFieldMode.ANY_ENC) && (blockLength[i + 1] < 4)) {
blockType[i + 1] = GeneralFieldMode.ISOIEC;
}
if ((next == GeneralFieldMode.ALPHA_OR_ISO) && (blockLength[i + 1] >= 5)) {
blockType[i + 1] = GeneralFieldMode.ALPHA;
}
if ((next == GeneralFieldMode.ALPHA_OR_ISO) && (blockLength[i + 1] < 5)) {
blockType[i + 1] = GeneralFieldMode.ISOIEC;
}
}
if (current == GeneralFieldMode.ALPHA_OR_ISO) {
blockType[i] = GeneralFieldMode.ALPHA;
}
if ((current == GeneralFieldMode.ALPHA) && (i != (block_count - 1))) {
if ((next == GeneralFieldMode.ANY_ENC) && (blockLength[i + 1] >= 6)) {
blockType[i + 1] = GeneralFieldMode.NUMERIC;
}
if ((next == GeneralFieldMode.ANY_ENC) && (blockLength[i + 1] < 6)) {
if ((i == block_count - 2) && (blockLength[i + 1] >= 4)) {
blockType[i + 1] = GeneralFieldMode.NUMERIC;
} else {
blockType[i + 1] = GeneralFieldMode.ALPHA;
}
}
}
if (current == GeneralFieldMode.ANY_ENC) {
blockType[i] = GeneralFieldMode.NUMERIC;
}
}
if (block_count > 1) {
i = 1;
while (i < block_count) {
if (blockType[i - 1] == blockType[i]) {
/* bring together */
blockLength[i - 1] = blockLength[i - 1] + blockLength[i];
j = i + 1;
/* decrease the list */
while (j < block_count) {
blockLength[j - 1] = blockLength[j];
blockType[j - 1] = blockType[j];
j++;
}
block_count--;
i--;
}
i++;
}
}
for (i = 0; i < block_count - 1; i++) {
if ((blockType[i] == GeneralFieldMode.NUMERIC) && ((blockLength[i] & 1) != 0)) {
/* Odd size numeric block */
blockLength[i] = blockLength[i] - 1;
blockLength[i + 1] = blockLength[i + 1] + 1;
}
}
j = 0;
for (i = 0; i < block_count; i++) {
for (k = 0; k < blockLength[i]; k++) {
general_field_type[j] = blockType[i];
j++;
}
}
if ((blockType[block_count - 1] == GeneralFieldMode.NUMERIC) && ((blockLength[block_count - 1] & 1) != 0)) {
/* If the last block is numeric and an odd size, further
processing needs to be done outside this procedure */
return true;
} else {
return false;
}
}
private void cc_a() {
/* CC-A 2D component */
int i, strpos, segment, cwCnt, variant, rows;
int k, offset, j, total;
int[] rsCodeWords = new int[8];
int LeftRAPStart, RightRAPStart, CentreRAPStart, StartCluster;
int LeftRAP, RightRAP, CentreRAP, Cluster;
int[] dummy = new int[5];
int flip, loop;
String codebarre;
StringBuilder bin = new StringBuilder();
StringBuilder local_source; /* A copy of source but with padding zeroes to make 208 bits */
variant = 0;
for (i = 0; i < 13; i++) {
bitStr[i] = 0;
}
for (i = 0; i < 28; i++) {
codeWords[i] = 0;
}
local_source = new StringBuilder(208);
local_source.append(binary_string);
for (i = binary_string.length(); i < 208; i++) {
local_source.append('0');
}
for (segment = 0; segment < 13; segment++) {
strpos = segment * 16;
bitStr[segment] = 0;
for (i = 0; i < 16; i++) {
if (local_source.charAt(strpos + i) == '1') {
bitStr[segment] += 0x8000 >> i;
}
}
}
init928();
/* encode codeWords from bitStr */
cwCnt = encode928(binary_string.length());
switch (cc_width) {
case 2:
switch (cwCnt) {
case 6:
variant = 0;
break;
case 8:
variant = 1;
break;
case 9:
variant = 2;
break;
case 11:
variant = 3;
break;
case 12:
variant = 4;
break;
case 14:
variant = 5;
break;
case 17:
variant = 6;
break;
}
break;
case 3:
switch (cwCnt) {
case 8:
variant = 7;
break;
case 10:
variant = 8;
break;
case 12:
variant = 9;
break;
case 14:
variant = 10;
break;
case 17:
variant = 11;
break;
}
break;
case 4:
switch (cwCnt) {
case 8:
variant = 12;
break;
case 11:
variant = 13;
break;
case 14:
variant = 14;
break;
case 17:
variant = 15;
break;
case 20:
variant = 16;
break;
}
break;
}
rows = CCA_VARIANTS[variant];
k = CCA_VARIANTS[17 + variant];
offset = CCA_VARIANTS[34 + variant];
/* Reed-Solomon error correction */
for (i = 0; i < 8; i++) {
rsCodeWords[i] = 0;
}
total = 0;
info("Composite Codewords: ");
for (i = 0; i < cwCnt; i++) {
total = (codeWords[i] + rsCodeWords[k - 1]) % 929;
for (j = k - 1; j >= 0; j--) {
if (j == 0) {
rsCodeWords[j] = (929 - (total * CCA_COEFFS[offset + j]) % 929) % 929;
} else {
rsCodeWords[j] = (rsCodeWords[j - 1] + 929 - (total * CCA_COEFFS[offset + j]) % 929) % 929;
}
}
infoSpace(codeWords[i]);
}
infoLine();
for (j = 0; j < k; j++) {
if (rsCodeWords[j] != 0) {
rsCodeWords[j] = 929 - rsCodeWords[j];
}
}
for (i = k - 1; i >= 0; i--) {
codeWords[cwCnt] = rsCodeWords[i];
cwCnt++;
}
/* Place data into table */
LeftRAPStart = A_RAP_TABLE[variant];
CentreRAPStart = A_RAP_TABLE[variant + 17];
RightRAPStart = A_RAP_TABLE[variant + 34];
StartCluster = A_RAP_TABLE[variant + 51] / 3;
LeftRAP = LeftRAPStart;
CentreRAP = CentreRAPStart;
RightRAP = RightRAPStart;
Cluster = StartCluster; /* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and Cluster(6) */
readable = "";
row_count = rows;
pattern = new String[row_count];
row_height = new int[row_count];
for (i = 0; i < rows; i++) {
codebarre = "";
offset = 929 * Cluster;
for (j = 0; j < 5; j++) {
dummy[j] = 0;
}
for (j = 0; j < cc_width; j++) {
dummy[j + 1] = codeWords[i * cc_width + j];
}
/* Copy the data into codebarre */
if (cc_width != 3) {
codebarre += RAPLR[LeftRAP];
}
codebarre += "1";
codebarre += CODAGEMC[offset + dummy[1]];
codebarre += "1";
if (cc_width == 3) {
codebarre += RAPC[CentreRAP];
}
if (cc_width >= 2) {
codebarre += "1";
codebarre += CODAGEMC[offset + dummy[2]];
codebarre += "1";
}
if (cc_width == 4) {
codebarre += RAPC[CentreRAP];
}
if (cc_width >= 3) {
codebarre += "1";
codebarre += CODAGEMC[offset + dummy[3]];
codebarre += "1";
}
if (cc_width == 4) {
codebarre += "1";
codebarre += CODAGEMC[offset + dummy[4]];
codebarre += "1";
}
codebarre += RAPLR[RightRAP];
codebarre += "1"; /* stop */
/* Now codebarre is a mixture of letters and numbers */
flip = 1;
bin.setLength(0);
for (loop = 0; loop < codebarre.length(); loop++) {
if ((codebarre.charAt(loop) >= '0') && (codebarre.charAt(loop) <= '9')) {
for (k = 0; k < codebarre.charAt(loop) - '0'; k++) {
if (flip == 0) {
bin.append('0');
} else {
bin.append('1');
}
}
if (flip == 0) {
flip = 1;
} else {
flip = 0;
}
} else {
bin.append(PDF_TTF[positionOf(codebarre.charAt(loop), BR_SET)]);
}
}
row_height[i] = 2;
pattern[i] = bin2pat(bin);
/* Set up RAPs and Cluster for next row */
LeftRAP++;
CentreRAP++;
RightRAP++;
Cluster++;
if (LeftRAP == 53) {
LeftRAP = 1;
}
if (CentreRAP == 53) {
CentreRAP = 1;
}
if (RightRAP == 53) {
RightRAP = 1;
}
if (Cluster == 3) {
Cluster = 0;
}
}
}
/* initialize pwr928 encoding table */
private void init928() {
int i, j, v;
int[] cw = new int[7];
cw[6] = 1;
for (i = 5; i >= 0; i--) {
cw[i] = 0;
}
for (i = 0; i < 7; i++) {
pwr928[0][i] = cw[i];
}
for (j = 1; j < 69; j++) {
for (v = 0, i = 6; i >= 1; i--) {
v = (2 * cw[i]) + (v / 928);
pwr928[j][i] = cw[i] = v % 928;
}
pwr928[j][0] = cw[0] = (2 * cw[0]) + (v / 928);
}
}
/* converts bit string to base 928 values, codeWords[0] is highest order */
private int encode928(int bitLng) {
int i, j, b, bitCnt, cwNdx, cwCnt, cwLng;
for (cwNdx = cwLng = b = 0; b < bitLng; b += 69, cwNdx += 7) {
bitCnt = min(bitLng - b, 69);
cwLng += cwCnt = bitCnt / 10 + 1;
for (i = 0; i < cwCnt; i++) {
codeWords[cwNdx + i] = 0; /* init 0 */
}
for (i = 0; i < bitCnt; i++) {
if (getBit(b + bitCnt - i - 1)) {
for (j = 0; j < cwCnt; j++) {
codeWords[cwNdx + j] += pwr928[i][j + 7 - cwCnt];
}
}
}
for (i = cwCnt - 1; i > 0; i--) {
/* add "carries" */
codeWords[cwNdx + i - 1] += codeWords[cwNdx + i] / 928;
codeWords[cwNdx + i] %= 928;
}
}
return (cwLng);
}
private int min(int first, int second) {
if (first <= second) {
return first;
} else {
return second;
}
}
/* gets bit in bitString at bitPos */
private boolean getBit(int arg) {
if ((bitStr[arg >> 4] & (0x8000 >> (arg & 15))) != 0) {
return true;
} else {
return false;
}
}
private void cc_b() {
/* CC-B 2D component */
int length, i, binloc;
int k, j, longueur, offset;
int[] mccorrection = new int[50];
int total;
int[] dummy = new int[5];
String codebarre;
StringBuilder bin = new StringBuilder();
int variant, LeftRAPStart, CentreRAPStart, RightRAPStart, StartCluster;
int LeftRAP, CentreRAP, RightRAP, Cluster, flip, loop;
int option_2, rows;
inputData = new int[(binary_string.length() / 8) + 3];
length = binary_string.length() / 8;
for (i = 0; i < length; i++) {
binloc = i * 8;
inputData[i] = 0;
for (j = 0; j < 8; j++) {
if (binary_string.charAt(binloc + j) == '1') {
inputData[i] += 0x80 >> j;
}
}
}
codeWordCount = 0;
/* "the CC-B component shall have codeword 920 in the first symbol character position" (section 9a) */
codeWords[codeWordCount] = 920;
codeWordCount++;
byteprocess(0, length);
/* Now figure out which variant of the symbol to use and load values accordingly */
variant = 0;
if (cc_width == 2) {
variant = 13;
if (codeWordCount <= 33) {
variant = 12;
}
if (codeWordCount <= 29) {
variant = 11;
}
if (codeWordCount <= 24) {
variant = 10;
}
if (codeWordCount <= 19) {
variant = 9;
}
if (codeWordCount <= 13) {
variant = 8;
}
if (codeWordCount <= 8) {
variant = 7;
}
}
if (cc_width == 3) {
variant = 23;
if (codeWordCount <= 70) {
variant = 22;
}
if (codeWordCount <= 58) {
variant = 21;
}
if (codeWordCount <= 46) {
variant = 20;
}
if (codeWordCount <= 34) {
variant = 19;
}
if (codeWordCount <= 24) {
variant = 18;
}
if (codeWordCount <= 18) {
variant = 17;
}
if (codeWordCount <= 14) {
variant = 16;
}
if (codeWordCount <= 10) {
variant = 15;
}
if (codeWordCount <= 6) {
variant = 14;
}
}
if (cc_width == 4) {
variant = 34;
if (codeWordCount <= 108) {
variant = 33;
}
if (codeWordCount <= 90) {
variant = 32;
}
if (codeWordCount <= 72) {
variant = 31;
}
if (codeWordCount <= 54) {
variant = 30;
}
if (codeWordCount <= 39) {
variant = 29;
}
if (codeWordCount <= 30) {
variant = 28;
}
if (codeWordCount <= 24) {
variant = 27;
}
if (codeWordCount <= 18) {
variant = 26;
}
if (codeWordCount <= 12) {
variant = 25;
}
if (codeWordCount <= 8) {
variant = 24;
}
}
/* Now we have the variant we can load the data - from here on the same as MicroPDF417 code */
variant--;
option_2 = MICRO_VARIANTS[variant]; /* columns */
rows = MICRO_VARIANTS[variant + 34]; /* rows */
k = MICRO_VARIANTS[variant + 68]; /* number of EC CWs */
longueur = (option_2 * rows) - k; /* number of non-EC CWs */
i = longueur - codeWordCount; /* amount of padding required */
offset = MICRO_VARIANTS[variant + 102]; /* coefficient offset */
/* We add the padding */
while (i > 0) {
codeWords[codeWordCount] = 900;
codeWordCount++;
i--;
}
/* Reed-Solomon error correction */
longueur = codeWordCount;
for (loop = 0; loop < 50; loop++) {
mccorrection[loop] = 0;
}
total = 0;
info("Composite Codewords: ");
for (i = 0; i < longueur; i++) {
total = (codeWords[i] + mccorrection[k - 1]) % 929;
for (j = k - 1; j >= 0; j--) {
if (j == 0) {
mccorrection[j] = (929 - (total * MICROCOEFFS[offset + j]) % 929) % 929;
} else {
mccorrection[j] = (mccorrection[j - 1] + 929 - (total * MICROCOEFFS[offset + j]) % 929) % 929;
}
}
infoSpace(codeWords[i]);
}
infoLine();
for (j = 0; j < k; j++) {
if (mccorrection[j] != 0) {
mccorrection[j] = 929 - mccorrection[j];
}
}
/* we add these codes to the string */
for (i = k - 1; i >= 0; i--) {
codeWords[codeWordCount] = mccorrection[i];
codeWordCount++;
}
/* Now get the RAP (Row Address Pattern) start values */
LeftRAPStart = RAP_TABLE[variant];
CentreRAPStart = RAP_TABLE[variant + 34];
RightRAPStart = RAP_TABLE[variant + 68];
StartCluster = RAP_TABLE[variant + 102] / 3;
/* That's all values loaded, get on with the encoding */
LeftRAP = LeftRAPStart;
CentreRAP = CentreRAPStart;
RightRAP = RightRAPStart;
Cluster = StartCluster; /* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and Cluster(6) */
readable = "";
row_count = rows;
pattern = new String[row_count];
row_height = new int[row_count];
for (i = 0; i < rows; i++) {
codebarre = "";
offset = 929 * Cluster;
for (j = 0; j < 5; j++) {
dummy[j] = 0;
}
for (j = 0; j < option_2; j++) {
dummy[j + 1] = codeWords[i * option_2 + j];
}
/* Copy the data into codebarre */
codebarre += RAPLR[LeftRAP];
codebarre += "1";
codebarre += CODAGEMC[offset + dummy[1]];
codebarre += "1";
if (cc_width == 3) {
codebarre += RAPC[CentreRAP];
}
if (cc_width >= 2) {
codebarre += "1";
codebarre += CODAGEMC[offset + dummy[2]];
codebarre += "1";
}
if (cc_width == 4) {
codebarre += RAPC[CentreRAP];
}
if (cc_width >= 3) {
codebarre += "1";
codebarre += CODAGEMC[offset + dummy[3]];
codebarre += "1";
}
if (cc_width == 4) {
codebarre += "1";
codebarre += CODAGEMC[offset + dummy[4]];
codebarre += "1";
}
codebarre += RAPLR[RightRAP];
codebarre += "1"; /* stop */
/* Now codebarre is a mixture of letters and numbers */
flip = 1;
bin.setLength(0);
for (loop = 0; loop < codebarre.length(); loop++) {
if ((codebarre.charAt(loop) >= '0') && (codebarre.charAt(loop) <= '9')) {
for (k = 0; k < codebarre.charAt(loop) - '0'; k++) {
if (flip == 0) {
bin.append('0');
} else {
bin.append('1');
}
}
if (flip == 0) {
flip = 1;
} else {
flip = 0;
}
} else {
bin.append(PDF_TTF[positionOf(codebarre.charAt(loop), BR_SET)]);
}
}
pattern[i] = bin2pat(bin);
row_height[i] = 2;
/* Set up RAPs and Cluster for next row */
LeftRAP++;
CentreRAP++;
RightRAP++;
Cluster++;
if (LeftRAP == 53) {
LeftRAP = 1;
}
if (CentreRAP == 53) {
CentreRAP = 1;
}
if (RightRAP == 53) {
RightRAP = 1;
}
if (Cluster == 3) {
Cluster = 0;
}
}
}
private void cc_c() {
/* CC-C 2D component - byte compressed PDF417 */
int length, i, binloc, k;
int offset, longueur, loop, total, j;
int[] mccorrection = new int[520];
int c1, c2, c3;
int[] dummy = new int[35];
String codebarre;
StringBuilder bin = new StringBuilder();
inputData = new int[(binary_string.length() / 8) + 4];
length = binary_string.length() / 8;
for (i = 0; i < length; i++) {
binloc = i * 8;
inputData[i] = 0;
for (j = 0; j < 8; j++) {
if (binary_string.charAt(binloc + j) == '1') {
inputData[i] += 0x80 >> j;
}
}
}
codeWordCount = 0;
codeWords[codeWordCount] = 0; /* space for length descriptor */
codeWordCount++;
codeWords[codeWordCount] = 920; /* CC-C identifier */
codeWordCount++;
byteprocess(0, length);
codeWords[0] = codeWordCount;
k = 1;
for (i = 1; i <= (ecc + 1); i++) {
k *= 2;
}
/* 796 - we now take care of the Reed Solomon codes */
switch (ecc) {
case 1:
offset = 2;
break;
case 2:
offset = 6;
break;
case 3:
offset = 14;
break;
case 4:
offset = 30;
break;
case 5:
offset = 62;
break;
case 6:
offset = 126;
break;
case 7:
offset = 254;
break;
case 8:
offset = 510;
break;
default:
offset = 0;
break;
}
longueur = codeWordCount;
for (loop = 0; loop < 520; loop++) {
mccorrection[loop] = 0;
}
total = 0;
info("Composite Codewords: ");
for (i = 0; i < longueur; i++) {
total = (codeWords[i] + mccorrection[k - 1]) % 929;
for (j = k - 1; j >= 0; j--) {
if (j == 0) {
mccorrection[j] = (929 - (total * COEFRS[offset + j]) % 929) % 929;
} else {
mccorrection[j] = (mccorrection[j - 1] + 929 - (total * COEFRS[offset + j]) % 929) % 929;
}
}
infoSpace(codeWords[i]);
}
infoLine();
for (j = 0; j < k; j++) {
if (mccorrection[j] != 0) {
mccorrection[j] = 929 - mccorrection[j];
}
}
/* we add these codes to the string */
for (i = k - 1; i >= 0; i--) {
codeWords[codeWordCount] = mccorrection[i];
codeWordCount++;
}
/* 818 - The CW string is finished */
c1 = (codeWordCount / cc_width - 1) / 3;
c2 = ecc * 3 + (codeWordCount / cc_width - 1) % 3;
c3 = cc_width - 1;
readable = "";
row_count = codeWordCount / cc_width;
pattern = new String[row_count];
row_height = new int[row_count];
/* we now encode each row */
for (i = 0; i <= (codeWordCount / cc_width) - 1; i++) {
for (j = 0; j < cc_width; j++) {
dummy[j + 1] = codeWords[i * cc_width + j];
}
k = (i / 3) * 30;
switch (i % 3) {
/* follows this pattern from US Patent 5,243,655:
Row 0: L0 (row #, # of rows) R0 (row #, # of columns)
Row 1: L1 (row #, security level) R1 (row #, # of rows)
Row 2: L2 (row #, # of columns) R2 (row #, security level)
Row 3: L3 (row #, # of rows) R3 (row #, # of columns)
etc. */
case 0:
dummy[0] = k + c1;
dummy[cc_width + 1] = k + c3;
break;
case 1:
dummy[0] = k + c2;
dummy[cc_width + 1] = k + c1;
break;
case 2:
dummy[0] = k + c3;
dummy[cc_width + 1] = k + c2;
break;
}
codebarre = "+*"; /* Start with a start char and a separator */
for (j = 0; j <= cc_width + 1; j++) {
switch (i % 3) {
case 1:
offset = 929; /* cluster(3) */ break;
case 2:
offset = 1858; /* cluster(6) */ break;
default:
offset = 0; /* cluster(0) */ break;
}
codebarre += CODAGEMC[offset + dummy[j]];
codebarre += "*";
}
codebarre += "-";
bin.setLength(0);
for (loop = 0; loop < codebarre.length(); loop++) {
bin.append(PDF_TTF[positionOf(codebarre.charAt(loop), BR_SET)]);
}
pattern[i] = bin2pat(bin);
row_height[i] = 3;
}
}
private void byteprocess(int start, int length) {
int len = 0;
int chunkLen = 0;
BigInteger mantisa;
BigInteger total;
BigInteger word;
/* select the switch for multiple of 6 bytes */
if ((binary_string.length() % 6) == 0) {
codeWords[codeWordCount++] = 924;
} else {
codeWords[codeWordCount++] = 901;
}
while (len < length) {
chunkLen = length - len;
if (6 <= chunkLen) /* Take groups of 6 */ {
chunkLen = 6;
len += chunkLen;
total = BigInteger.valueOf(0);
while ((chunkLen--) != 0) {
mantisa = BigInteger.valueOf(inputData[start++]);
total = total.or(mantisa.shiftLeft(chunkLen * 8));
}
chunkLen = 5;
while ((chunkLen--) != 0) {
word = total.mod(BigInteger.valueOf(900));
codeWords[codeWordCount + chunkLen] = word.intValue();
total = total.divide(BigInteger.valueOf(900));
}
codeWordCount += 5;
} else /* If it remain a group of less than 6 bytes */ {
len += chunkLen;
while ((chunkLen--) != 0) {
codeWords[codeWordCount++] = inputData[start++];
}
}
}
}
}