OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
181 ilm 1
/*
2
 * Copyright 2014-2017 Robin Stuart, Daniel Gredler
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5
 * in compliance with the License. You may obtain a copy of the License at
6
 *
7
 * http://www.apache.org/licenses/LICENSE-2.0
8
 *
9
 * Unless required by applicable law or agreed to in writing, software distributed under the License
10
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11
 * or implied. See the License for the specific language governing permissions and limitations under
12
 * the License.
13
 */
14
 
15
package uk.org.okapibarcode.backend;
16
 
17
import static uk.org.okapibarcode.util.Arrays.positionOf;
18
 
19
import java.math.BigInteger;
20
import java.util.ArrayList;
21
import java.util.List;
22
 
23
/**
24
 * <p>
25
 * Implements PDF417 bar code symbology and MicroPDF417 bar code symbology according to ISO/IEC
26
 * 15438:2006 and ISO/IEC 24728:2006 respectively.
27
 *
28
 * <p>
29
 * PDF417 supports encoding up to the ISO standard maximum symbol size of 925 codewords which (at
30
 * error correction level 0) allows a maximum data size of 1850 text characters, or 2710 digits. The
31
 * maximum size MicroPDF417 symbol can hold 250 alphanumeric characters or 366 digits.
32
 *
33
 * @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
34
 * @author Daniel Gredler
35
 */
36
public class Pdf417 extends Symbol {
37
 
38
    public enum Mode {
39
        /** Normal PDF417. */
40
        NORMAL,
41
        /** Truncated PDF417. */
42
        TRUNCATED,
43
        /** MicroPDF417. */
44
        MICRO
45
    }
46
 
47
    private enum EncodingMode {
48
        FALSE, TEX, BYT, NUM
49
    }
50
 
51
    private final int[] codeWords = new int[2700];
52
    private int codeWordCount;
53
    private Mode symbolMode = Mode.NORMAL;
54
    private Integer columns;
55
    private Integer rows;
56
    private int preferredEccLevel = -1;
57
    private int structuredAppendFileId = 0;
58
    private int structuredAppendPosition = 1;
59
    private int structuredAppendTotal = 1;
60
 
61
    private static final int MAX_NUMERIC_COMPACTION_BLOCK_SIZE = 44;
62
 
63
    private static final int[] COEFRS = {
64
            /* k = 2 */
65
            27, 917,
66
 
67
            /* k = 4 */
68
            522, 568, 723, 809,
69
 
70
            /* k = 8 */
71
            237, 308, 436, 284, 646, 653, 428, 379,
72
 
73
            /* k = 16 */
74
            274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65,
75
 
76
            /* k = 32 */
77
            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,
78
 
79
            /* k = 64 */
80
            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,
81
            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,
82
 
83
            /* k = 128 */
84
            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,
85
            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,
86
            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,
87
            539, 297, 827, 865, 37, 517, 834, 315, 550, 86, 801, 4, 108, 539,
88
 
89
            /* k = 256 */
90
            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,
91
            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,
92
            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,
93
            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,
94
            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,
95
            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,
96
            83, 402, 41, 656, 505, 579, 481, 173, 404, 251, 688, 95, 497, 555, 642, 543, 307, 159, 924, 558, 648, 55, 497, 10,
97
 
98
            /* k = 512 */
99
            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,
100
            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,
101
            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,
102
            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,
103
            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,
104
            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,
105
            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,
106
            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,
107
            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,
108
            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,
109
            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,
110
            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,
111
            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,
112
            647, 63, 310, 863, 251, 366, 304, 282, 738, 675, 410, 389, 244, 31, 121, 303, 263 };
113
 
114
    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",
115
            "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",
116
            "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",
117
            "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",
118
            "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",
119
            "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",
120
            "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",
121
            "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",
122
            "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",
123
            "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",
124
            "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",
125
            "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",
126
            "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",
127
            "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",
128
            "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",
129
            "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",
130
            "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",
131
            "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",
132
            "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",
133
            "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",
134
            "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",
135
            "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",
136
            "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",
137
            "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",
138
            "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",
139
            "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",
140
            "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",
141
            "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",
142
            "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",
143
            "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",
144
            "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",
145
            "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",
146
            "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",
147
            "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",
148
            "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",
149
            "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",
150
            "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",
151
            "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",
152
            "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",
153
            "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",
154
            "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",
155
            "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",
156
            "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",
157
            "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",
158
            "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",
159
            "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",
160
            "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",
161
            "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",
162
            "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",
163
            "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",
164
            "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",
165
            "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",
166
            "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",
167
            "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",
168
            "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",
169
            "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",
170
            "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",
171
            "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",
172
            "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",
173
            "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",
174
            "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",
175
            "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",
176
            "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",
177
            "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",
178
            "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",
179
            "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",
180
            "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",
181
            "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",
182
            "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",
183
            "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",
184
            "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",
185
            "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",
186
            "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",
187
            "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",
188
            "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",
189
            "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",
190
            "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",
191
            "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",
192
            "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",
193
            "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",
194
            "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",
195
            "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",
196
            "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",
197
            "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",
198
            "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",
199
            "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",
200
            "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",
201
            "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",
202
            "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",
203
            "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",
204
            "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",
205
            "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",
206
            "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",
207
            "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",
208
            "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",
209
            "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",
210
            "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",
211
            "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",
212
            "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",
213
            "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",
214
            "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",
215
            "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",
216
            "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",
217
            "Aym", "Ayl", "Aif", "Ayv", "kze", "kzd", "Aye", "Byu", "Ayd", "Byt", "szp" };
218
 
219
    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',
220
            'z', '*', '+', '-' };
221
 
222
    private static final String[] PDF_TTF = { "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", "01000", "01001", "01010", "01011", "01100", "01101", "01110", "01111", "10000",
223
            "10001", "10010", "10011", "10100", "10101", "10110", "10111", "11000", "11001", "11010", "11011", "11100", "11101", "11110", "11111", "01", "1111111101010100", "11111101000101001" };
224
 
225
    private static final int[] ASCII_X = { 7, 8, 8, 4, 12, 4, 4, 8, 8, 8, 12, 4, 12, 12, 12, 12, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 12, 8, 8, 4, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
226
            1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 8, 8, 4, 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 8, 8, 8, 8 };
227
 
228
    private static final int[] ASCII_Y = { 26, 10, 20, 15, 18, 21, 10, 28, 23, 24, 22, 20, 13, 16, 17, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 14, 0, 1, 23, 2, 25, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
229
            12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 4, 5, 6, 24, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 21, 27, 9 };
230
 
231
    private static final int[] MICRO_AUTOSIZE = { 4, 6, 7, 8, 8, 10, 10, 12, 12, 13, 14, 16, 18, 18, 19, 20, 24, 24, 24, 29, 30, 33, 34, 37, 39, 46, 54, 58, 70, 72, 82, 90, 108, 126, // max
232
                                                                                                                                                                                       // codeword
233
                                                                                                                                                                                       // counts
234
            1, 14, 2, 7, 24, 3, 15, 25, 4, 8, 16, 5, 17, 26, 9, 6, 10, 18, 27, 11, 28, 12, 19, 13, 29, 20, 30, 21, 22, 31, 23, 32, 33, 34 // corresponding
235
                                                                                                                                          // variant
236
    };
237
 
238
    /*
239
     * Rows, columns, error codewords, k-offset of valid MicroPDF417 sizes from ISO/IEC 24728:2006
240
     */
241
    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, // columns
242
            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, // rows
243
            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, // k
244
                                                                                                                                          // (EC
245
                                                                                                                                          // codewords)
246
            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 // offset
247
    };
248
 
249
    /*
250
     * Following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24728:2006 tables
251
     * 10, 11 and 12
252
     */
253
    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, // left
254
                                                                                                                                                                    // RAP
255
            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, // centre
256
                                                                                                                                 // RAP
257
            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, // right
258
                                                                                                                                           // RAP
259
            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 // start
260
                                                                                                                 // cluster
261
    };
262
 
263
    /* Left and Right Row Address Pattern from Table 2 */
264
    private static final String[] RAPLR = { "", "221311", "311311", "312211", "222211", "213211", "214111", "223111", "313111", "322111", "412111", "421111", "331111", "241111", "232111", "231211",
265
            "321211", "411211", "411121", "411112", "321112", "312112", "311212", "311221", "311131", "311122", "311113", "221113", "221122", "221131", "221221", "222121", "312121", "321121",
266
            "231121", "231112", "222112", "213112", "212212", "212221", "212131", "212122", "212113", "211213", "211123", "211132", "211141", "211231", "211222", "211312", "211321", "211411",
267
            "212311" };
268
 
269
    /* Centre Row Address Pattern from Table 2 */
270
    private static final String[] RAPC = { "", "112231", "121231", "122131", "131131", "131221", "132121", "141121", "141211", "142111", "133111", "132211", "131311", "122311", "123211", "124111",
271
            "115111", "114211", "114121", "123121", "123112", "122212", "122221", "121321", "121411", "112411", "113311", "113221", "113212", "113122", "122122", "131122", "131113", "122113",
272
            "113113", "112213", "112222", "112312", "112321", "111421", "111331", "111322", "111232", "111223", "111133", "111124", "111214", "112114", "121114", "121123", "121132", "112132",
273
            "112141" };
274
 
275
    /* MicroPDF417 coefficients from ISO/IEC 24728:2006 Annex F */
276
    private static final int[] MICRO_COEFFS = {
277
            /* k = 7 */
278
            76, 925, 537, 597, 784, 691, 437,
279
 
280
            /* k = 8 */
281
            237, 308, 436, 284, 646, 653, 428, 379,
282
 
283
            /* k = 9 */
284
            567, 527, 622, 257, 289, 362, 501, 441, 205,
285
 
286
            /* k = 10 */
287
            377, 457, 64, 244, 826, 841, 818, 691, 266, 612,
288
 
289
            /* k = 11 */
290
            462, 45, 565, 708, 825, 213, 15, 68, 327, 602, 904,
291
 
292
            /* k = 12 */
293
            597, 864, 757, 201, 646, 684, 347, 127, 388, 7, 69, 851,
294
 
295
            /* k = 13 */
296
            764, 713, 342, 384, 606, 583, 322, 592, 678, 204, 184, 394, 692,
297
 
298
            /* k = 14 */
299
            669, 677, 154, 187, 241, 286, 274, 354, 478, 915, 691, 833, 105, 215,
300
 
301
            /* k = 15 */
302
            460, 829, 476, 109, 904, 664, 230, 5, 80, 74, 550, 575, 147, 868, 642,
303
 
304
            /* k = 16 */
305
            274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65,
306
 
307
            /* k = 18 */
308
            279, 577, 315, 624, 37, 855, 275, 739, 120, 297, 312, 202, 560, 321, 233, 756, 760, 573,
309
 
310
            /* k = 21 */
311
            108, 519, 781, 534, 129, 425, 681, 553, 422, 716, 763, 693, 624, 610, 310, 691, 347, 165, 193, 259, 568,
312
 
313
            /* k = 26 */
314
            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,
315
 
316
            /* k = 32 */
317
            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,
318
 
319
            /* k = 38 */
320
            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,
321
 
322
            /* k = 44 */
323
            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,
324
            436, 339, 730, 82, 285,
325
 
326
            /* k = 50 */
327
            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,
328
            127, 71, 244, 211, 477, 920, 876, 427, 820, 718, 435 };
329
 
330
    /**
331
     * Creates a new PDF417 symbol instance.
332
     */
333
    public Pdf417() {
334
        setBarHeight(3);
335
    }
336
 
337
    /**
338
     * Sets the default bar height (height of a single row) for this symbol (default value is
339
     * <code>3</code>).
340
     *
341
     * @param barHeight the default bar height for this symbol
342
     */
343
    @Override
344
    public void setBarHeight(final int barHeight) {
345
        super.setBarHeight(barHeight);
346
    }
347
 
348
    /**
349
     * Sets the width of the symbol by specifying the number of columns of data codewords. Valid
350
     * values are 1-30 for PDF417 and 1-4 for MicroPDF417.
351
     *
352
     * @param columns the number of data columns in the symbol
353
     */
354
    public void setDataColumns(final int columns) {
355
        this.columns = columns;
356
    }
357
 
358
    /**
359
     * Returns the number of data columns used by this symbol, or {@code null} if the number of data
360
     * columns has not been set.
361
     *
362
     * @return the number of data columns used by this symbol
363
     */
364
    public Integer getDataColumns() {
365
        return this.columns;
366
    }
367
 
368
    /**
369
     * Sets the height of the symbol by specifying the number of rows of data codewords. Valid
370
     * values are 3-90 for PDF417 and 4-44 for MicroPDF417.
371
     *
372
     * @param rows the number of rows in the symbol
373
     */
374
    public void setRows(final int rows) {
375
        this.rows = rows;
376
    }
377
 
378
    /**
379
     * Returns the number of rows used by this symbol, or {@code null} if the number of rows has not
380
     * been set.
381
     *
382
     * @return the number of rows used by this symbol
383
     */
384
    public Integer getRows() {
385
        return this.rows;
386
    }
387
 
388
    /**
389
     * Set the amount of the symbol which is dedicated to error correction codewords. The number of
390
     * codewords of error correction data is determined by 2<sup>(eccLevel + 1)</sup>. This
391
     * attribute is ignored when using {@link Mode#MICRO micro} mode.
392
     *
393
     * @param eccLevel level of error correction (0-8)
394
     */
395
    public void setPreferredEccLevel(final int eccLevel) {
396
        if (eccLevel < 0 || eccLevel > 8) {
397
            throw new IllegalArgumentException("ECC level must be between 0 and 8.");
398
        }
399
        this.preferredEccLevel = eccLevel;
400
    }
401
 
402
    /**
403
     * Returns the preferred error correction level.
404
     *
405
     * @return the preferred error correction level
406
     */
407
    public int getPreferredEccLevel() {
408
        return this.preferredEccLevel;
409
    }
410
 
411
    /**
412
     * Forces the use of the specified MicroPDF417 variant. Only valid when using {@link Mode#MICRO
413
     * micro} mode.
414
     *
415
     * @param variant the MicroPDF417 variant to use
416
     */
417
    public void setVariant(final int variant) {
418
        if (this.symbolMode != Mode.MICRO) {
419
            throw new IllegalArgumentException("Can only set variant when using MICRO mode.");
420
        }
421
        if (variant < 1 || variant > 34) {
422
            throw new IllegalArgumentException("Variant must be between 1 and 34.");
423
        }
424
        this.columns = MICRO_VARIANTS[variant - 1];
425
        this.rows = MICRO_VARIANTS[variant - 1 + 34];
426
    }
427
 
428
    /**
429
     * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format
430
     * (Macro PDF417), this method sets the position of this symbol in the series. Valid values are
431
     * 1 through 99,999 inclusive.
432
     *
433
     * @param position the position of this PDF417 symbol in the structured append series
434
     */
435
    public void setStructuredAppendPosition(final int position) {
436
        if (position < 1 || position > 99_999) {
437
            throw new IllegalArgumentException("Invalid PDF417 structured append position: " + position);
438
        }
439
        this.structuredAppendPosition = position;
440
    }
441
 
442
    /**
443
     * Returns the position of this PDF417 symbol in a series of symbols using structured append
444
     * (Macro PDF417). If this symbol is not part of such a series, this method will return
445
     * <code>1</code>.
446
     *
447
     * @return the position of this PDF417 symbol in a series of symbols using structured append
448
     */
449
    public int getStructuredAppendPosition() {
450
        return this.structuredAppendPosition;
451
    }
452
 
453
    /**
454
     * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format
455
     * (Macro PDF417), this method sets the total number of symbols in the series. Valid values are
456
     * 1 through 99,999 inclusive. A value of 1 indicates that this symbol is not part of a
457
     * structured append series.
458
     *
459
     * @param total the total number of PDF417 symbols in the structured append series
460
     */
461
    public void setStructuredAppendTotal(final int total) {
462
        if (total < 1 || total > 99_999) {
463
            throw new IllegalArgumentException("Invalid PDF417 structured append total: " + total);
464
        }
465
        this.structuredAppendTotal = total;
466
    }
467
 
468
    /**
469
     * Returns the size of the series of PDF417 symbols using structured append (Macro PDF417) that
470
     * this symbol is part of. If this symbol is not part of a structured append series, this method
471
     * will return <code>1</code>.
472
     *
473
     * @return size of the series that this symbol is part of
474
     */
475
    public int getStructuredAppendTotal() {
476
        return this.structuredAppendTotal;
477
    }
478
 
479
    /**
480
     * If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format
481
     * (Macro PDF417), this method sets the unique file ID for the series. Valid values are 0
482
     * through 899 inclusive.
483
     *
484
     * @param fileId the unique file ID for the series that this symbol is part of
485
     */
486
    public void setStructuredAppendFileId(final int fileId) {
487
        if (fileId < 0 || fileId > 899) {
488
            throw new IllegalArgumentException("Invalid PDF417 structured append file ID: " + fileId);
489
        }
490
        this.structuredAppendFileId = fileId;
491
    }
492
 
493
    /**
494
     * Returns the unique file ID of the series of PDF417 symbols using structured append (Macro
495
     * PDF417) that this symbol is part of. If this symbol is not part of a structured append
496
     * series, this method will return <code>0</code>.
497
     *
498
     * @return the unique file ID for the series that this symbol is part of
499
     */
500
    public int getStructuredAppendFileId() {
501
        return this.structuredAppendFileId;
502
    }
503
 
504
    public void setMode(final Mode mode) {
505
        this.symbolMode = mode;
506
    }
507
 
508
    public Mode getMode() {
509
        return this.symbolMode;
510
    }
511
 
512
    @Override
513
    protected void encode() {
514
 
515
        eciProcess();
516
 
517
        switch (this.symbolMode) {
518
        case MICRO:
519
            processMicroPdf417();
520
            break;
521
        case NORMAL:
522
        case TRUNCATED:
523
        default:
524
            processPdf417();
525
            break;
526
        }
527
    }
528
 
529
    private void processPdf417() {
530
        int j, loop, offset;
531
        final int[] mccorrection = new int[520];
532
        int total;
533
        int c1, c2, c3;
534
        final int[] dummy = new int[35];
535
        int selectedECCLevel;
536
        final StringBuilder codebarre = new StringBuilder();
537
        final StringBuilder bin = new StringBuilder();
538
 
539
        final List<Block> blocks = createBlocks(this.inputData);
540
 
541
        /* now compress the data */
542
        this.codeWordCount = 0;
543
 
544
        if (this.readerInit) {
545
            this.codeWords[this.codeWordCount] = 921; /* Reader Initialisation */
546
            this.codeWordCount++;
547
        }
548
 
549
        if (this.eciMode != 3) {
550
            /* Encoding ECI assignment number, from ISO/IEC 15438 Table 8 */
551
            if (this.eciMode <= 899) {
552
                this.codeWords[this.codeWordCount] = 927;
553
                this.codeWordCount++;
554
                this.codeWords[this.codeWordCount] = this.eciMode;
555
                this.codeWordCount++;
556
            }
557
 
558
            if (this.eciMode >= 900 && this.eciMode <= 810899) {
559
                this.codeWords[this.codeWordCount] = 926;
560
                this.codeWordCount++;
561
                this.codeWords[this.codeWordCount] = this.eciMode / 900 - 1;
562
                this.codeWordCount++;
563
                this.codeWords[this.codeWordCount] = this.eciMode % 900;
564
                this.codeWordCount++;
565
            }
566
 
567
            if (this.eciMode >= 810900 && this.eciMode <= 811799) {
568
                this.codeWords[this.codeWordCount] = 925;
569
                this.codeWordCount++;
570
                this.codeWords[this.codeWordCount] = this.eciMode - 810900;
571
                this.codeWordCount++;
572
            }
573
        }
574
 
575
        int blockCount = 0;
576
        for (int i = 0; i < blocks.size(); i++) {
577
            final Block block = blocks.get(i);
578
            switch (block.mode) {
579
            case TEX:
580
                /* text mode */
581
                final boolean firstBlock = i == 0;
582
                processText(blockCount, block.length, firstBlock);
583
                break;
584
            case BYT:
585
                /* octet stream mode */
586
                final EncodingMode lastMode = i == 0 ? EncodingMode.TEX : blocks.get(i - 1).mode;
587
                processBytes(blockCount, block.length, lastMode);
588
                break;
589
            case NUM:
590
                /* numeric mode */
591
                processNumbers(this.inputData, blockCount, block.length, false);
592
                break;
593
            default:
594
                throw new OkapiException("Unknown block type: " + block.mode);
595
            }
596
            blockCount += block.length;
597
        }
598
 
599
        addMacroCodewords();
600
 
601
        info("Codewords: ");
602
        for (int i = 0; i < this.codeWordCount; i++) {
603
            infoSpace(this.codeWords[i]);
604
        }
605
        infoLine();
606
 
607
        /* Now take care of the number of CWs per row */
608
 
609
        // if we have to default the ECC level, do so per the
610
        // recommendations in the specification (Table E.1)
611
        selectedECCLevel = this.preferredEccLevel;
612
        if (selectedECCLevel < 0) {
613
            if (this.codeWordCount <= 40) {
614
                selectedECCLevel = 2;
615
            } else if (this.codeWordCount <= 160) {
616
                selectedECCLevel = 3;
617
            } else if (this.codeWordCount <= 320) {
618
                selectedECCLevel = 4;
619
            } else if (this.codeWordCount <= 863) {
620
                selectedECCLevel = 5;
621
            } else {
622
                selectedECCLevel = 6;
623
            }
624
        }
625
 
626
        int k = 1 << selectedECCLevel + 1; // error correction codeword count
627
        final int dataCodeWordCount = this.codeWordCount + k + 1; // not including padding
628
 
629
        validateRows(3, 90);
630
        validateColumns(1, 30);
631
 
632
        if (this.columns != null) {
633
            if (this.rows != null) {
634
                // user specified both columns and rows; make sure the data fits
635
                if (this.columns * this.rows < dataCodeWordCount) {
636
                    throw new OkapiException("Too few rows (" + this.rows + ") and columns (" + this.columns + ") to hold codewords (" + dataCodeWordCount + ")");
637
                }
638
            } else {
639
                // user only specified column count; figure out row count
640
                this.rows = (int) Math.ceil(dataCodeWordCount / (double) this.columns);
641
            }
642
        } else {
643
            if (this.rows != null) {
644
                // user only specified row count; figure out column count
645
                this.columns = (int) Math.ceil(dataCodeWordCount / (double) this.rows);
646
            } else {
647
                // user didn't specify columns or rows; figure both out
648
                this.columns = (int) (0.5 + Math.sqrt((dataCodeWordCount - 1) / 3.0));
649
                this.rows = (int) Math.ceil(dataCodeWordCount / (double) this.columns);
650
            }
651
        }
652
 
653
        validateRows(3, 90);
654
        validateColumns(1, 30);
655
 
656
        /* add the padding */
657
        int paddingCount = this.columns * this.rows - this.codeWordCount - k - 1;
658
        while (paddingCount > 0) {
659
            this.codeWords[this.codeWordCount] = 900;
660
            this.codeWordCount++;
661
            paddingCount--;
662
        }
663
 
664
        /* add the length descriptor */
665
        for (int i = this.codeWordCount; i > 0; i--) {
666
            this.codeWords[i] = this.codeWords[i - 1];
667
        }
668
        this.codeWordCount++;
669
        this.codeWords[0] = this.codeWordCount;
670
 
671
        /* 796 - we now take care of the Reed Solomon codes */
672
        switch (selectedECCLevel) {
673
        case 1:
674
            offset = 2;
675
            break;
676
        case 2:
677
            offset = 6;
678
            break;
679
        case 3:
680
            offset = 14;
681
            break;
682
        case 4:
683
            offset = 30;
684
            break;
685
        case 5:
686
            offset = 62;
687
            break;
688
        case 6:
689
            offset = 126;
690
            break;
691
        case 7:
692
            offset = 254;
693
            break;
694
        case 8:
695
            offset = 510;
696
            break;
697
        default:
698
            offset = 0;
699
            break;
700
        }
701
 
702
        for (loop = 0; loop < 520; loop++) {
703
            mccorrection[loop] = 0;
704
        }
705
 
706
        for (int i = 0; i < this.codeWordCount; i++) {
707
            total = (this.codeWords[i] + mccorrection[k - 1]) % 929;
708
            for (j = k - 1; j > 0; j--) {
709
                mccorrection[j] = (mccorrection[j - 1] + 929 - total * COEFRS[offset + j] % 929) % 929;
710
            }
711
            mccorrection[0] = (929 - total * COEFRS[offset + j] % 929) % 929;
712
        }
713
 
714
        infoLine("Data Codewords: " + this.codeWordCount);
715
        infoLine("ECC Codewords: " + k);
716
 
717
        /* we add these codes to the string */
718
        for (int i = k - 1; i >= 0; i--) {
719
            this.codeWords[this.codeWordCount++] = mccorrection[i] != 0 ? 929 - mccorrection[i] : 0;
720
        }
721
 
722
        /* make sure total codeword count isn't too high */
723
        if (this.codeWordCount > 929) {
724
            throw new OkapiException("Too many codewords required (" + this.codeWordCount + ", but max is 929)");
725
        }
726
 
727
        /* 818 - The CW string is finished */
728
        c1 = (this.rows - 1) / 3;
729
        c2 = selectedECCLevel * 3 + (this.rows - 1) % 3;
730
        c3 = this.columns - 1;
731
 
732
        this.readable = "";
733
        this.row_count = this.rows;
734
        this.pattern = new String[this.rows];
735
        this.row_height = new int[this.rows];
736
        infoLine("Grid Size: " + this.columns + " X " + this.rows);
737
 
738
        /* we now encode each row */
739
        for (int i = 0; i < this.rows; i++) {
740
            for (j = 0; j < this.columns; j++) {
741
                dummy[j + 1] = this.codeWords[i * this.columns + j];
742
            }
743
            k = i / 3 * 30;
744
            switch (i % 3) {
745
            case 0:
746
                offset = 0; // cluster 0
747
                dummy[0] = k + c1; // left row indicator
748
                dummy[this.columns + 1] = k + c3; // right row indicator
749
                break;
750
            case 1:
751
                offset = 929; // cluster 3
752
                dummy[0] = k + c2; // left row indicator
753
                dummy[this.columns + 1] = k + c1; // right row indicator
754
                break;
755
            case 2:
756
                offset = 1858; // cluster 6
757
                dummy[0] = k + c3; // left row indicator
758
                dummy[this.columns + 1] = k + c2; // right row indicator
759
                break;
760
            }
761
            codebarre.setLength(0);
762
            codebarre.append("+*");
763
            for (j = 0; j <= this.columns + 1; j++) {
764
                if (!(this.symbolMode == Mode.TRUNCATED && j > this.columns)) {
765
                    codebarre.append(CODAGEMC[offset + dummy[j]]);
766
                    codebarre.append('*');
767
                }
768
            }
769
            if (this.symbolMode != Mode.TRUNCATED) {
770
                codebarre.append('-');
771
            }
772
            bin.setLength(0);
773
            for (j = 0; j < codebarre.length(); j++) {
774
                bin.append(PDF_TTF[positionOf(codebarre.charAt(j), BR_SET)]);
775
            }
776
            this.pattern[i] = bin2pat(bin);
777
            this.row_height[i] = this.default_height;
778
        }
779
    }
780
 
781
    private void processMicroPdf417() { /* like PDF417 only much smaller! */
782
 
783
        int k, j, longueur, offset;
784
        int total;
785
        int LeftRAPStart, CentreRAPStart, RightRAPStart, StartCluster;
786
        int LeftRAP, CentreRAP, RightRAP, Cluster, flip, loop;
787
        final int[] dummy = new int[5];
788
        final int[] mccorrection = new int[50];
789
        final StringBuilder codebarre = new StringBuilder();
790
        final StringBuilder bin = new StringBuilder();
791
 
792
        /* Encoding starts out the same as PDF417, so use the same code */
793
 
794
        final List<Block> blocks = createBlocks(this.inputData);
795
 
796
        /* 541 - now compress the data */
797
        this.codeWordCount = 0;
798
        if (this.readerInit) {
799
            this.codeWords[this.codeWordCount] = 921; /* Reader Initialisation */
800
            this.codeWordCount++;
801
        }
802
 
803
        if (this.eciMode != 3) {
804
            /* Encoding ECI assignment number, from ISO/IEC 15438 Table 8 */
805
            if (this.eciMode <= 899) {
806
                this.codeWords[this.codeWordCount] = 927;
807
                this.codeWordCount++;
808
                this.codeWords[this.codeWordCount] = this.eciMode;
809
                this.codeWordCount++;
810
            }
811
 
812
            if (this.eciMode >= 900 && this.eciMode <= 810899) {
813
                this.codeWords[this.codeWordCount] = 926;
814
                this.codeWordCount++;
815
                this.codeWords[this.codeWordCount] = this.eciMode / 900 - 1;
816
                this.codeWordCount++;
817
                this.codeWords[this.codeWordCount] = this.eciMode % 900;
818
                this.codeWordCount++;
819
            }
820
 
821
            if (this.eciMode >= 810900 && this.eciMode <= 811799) {
822
                this.codeWords[this.codeWordCount] = 925;
823
                this.codeWordCount++;
824
                this.codeWords[this.codeWordCount] = this.eciMode - 810900;
825
                this.codeWordCount++;
826
            }
827
        }
828
 
829
        int blockCount = 0;
830
        for (int i = 0; i < blocks.size(); i++) {
831
            final Block block = blocks.get(i);
832
            switch (block.mode) {
833
            case TEX:
834
                /* text mode */
835
                processText(blockCount, block.length, false); // TODO: this shouldn't always be
836
                                                              // false?
837
                break;
838
            case BYT:
839
                /* octet stream mode */
840
                final EncodingMode lastMode = i == 0 ? EncodingMode.TEX : blocks.get(i - 1).mode;
841
                processBytes(blockCount, block.length, lastMode);
842
                break;
843
            case NUM:
844
                /* numeric mode */
845
                processNumbers(this.inputData, blockCount, block.length, false);
846
                break;
847
            default:
848
                throw new OkapiException("Unknown block type: " + block.mode);
849
            }
850
            blockCount += block.length;
851
        }
852
 
853
        addMacroCodewords();
854
 
855
        info("Codewords: ");
856
        for (int i = 0; i < this.codeWordCount; i++) {
857
            infoSpace(this.codeWords[i]);
858
        }
859
        infoLine();
860
 
861
        /* This is where it all changes! */
862
 
863
        validateRows(4, 44);
864
        validateColumns(1, 4);
865
 
866
        if (this.columns != null) {
867
            int max;
868
            switch (this.columns) {
869
            case 1:
870
                max = 20;
871
                break;
872
            case 2:
873
                max = 37;
874
                break;
875
            case 3:
876
                max = 82;
877
                break;
878
            case 4:
879
                max = 126;
880
                break;
881
            default:
882
                throw new OkapiException("Invalid column count: " + this.columns);
883
            }
884
            if (this.codeWordCount > max) {
885
                throw new OkapiException("Too few columns (" + this.columns + ") to hold data codewords (" + this.codeWordCount + ")");
886
            }
887
        }
888
 
889
        /* Now figure out which variant of the symbol to use and load values accordingly */
890
 
891
        int variant = getMicroPdf417Variant(this.codeWordCount, this.columns, this.rows);
892
 
893
        /* Now we have the variant we can load the data */
894
 
895
        variant--;
896
        this.columns = MICRO_VARIANTS[variant]; /* columns */
897
        this.rows = MICRO_VARIANTS[variant + 34]; /* rows */
898
        k = MICRO_VARIANTS[variant + 68]; /* number of EC CWs */
899
        longueur = this.columns * this.rows - k; /* number of non-EC CWs */
900
        int padding = longueur - this.codeWordCount; /* amount of padding required */
901
        offset = MICRO_VARIANTS[variant + 102]; /* coefficient offset */
902
 
903
        infoLine("Data Codewords: " + longueur);
904
        infoLine("ECC Codewords: " + k);
905
 
906
        /* We add the padding */
907
        while (padding > 0) {
908
            this.codeWords[this.codeWordCount] = 900;
909
            this.codeWordCount++;
910
            padding--;
911
        }
912
 
913
        /* Reed-Solomon error correction */
914
        longueur = this.codeWordCount;
915
        for (loop = 0; loop < 50; loop++) {
916
            mccorrection[loop] = 0;
917
        }
918
 
919
        for (int i = 0; i < longueur; i++) {
920
            total = (this.codeWords[i] + mccorrection[k - 1]) % 929;
921
            for (j = k - 1; j >= 0; j--) {
922
                if (j == 0) {
923
                    mccorrection[j] = (929 - total * MICRO_COEFFS[offset + j] % 929) % 929;
924
                } else {
925
                    mccorrection[j] = (mccorrection[j - 1] + 929 - total * MICRO_COEFFS[offset + j] % 929) % 929;
926
                }
927
            }
928
        }
929
 
930
        for (j = 0; j < k; j++) {
931
            if (mccorrection[j] != 0) {
932
                mccorrection[j] = 929 - mccorrection[j];
933
            }
934
        }
935
        /* we add these codes to the string */
936
        for (int i = k - 1; i >= 0; i--) {
937
            this.codeWords[this.codeWordCount] = mccorrection[i];
938
            this.codeWordCount++;
939
        }
940
 
941
        /* Now get the RAP (Row Address Pattern) start values */
942
        LeftRAPStart = RAP_TABLE[variant];
943
        CentreRAPStart = RAP_TABLE[variant + 34];
944
        RightRAPStart = RAP_TABLE[variant + 68];
945
        StartCluster = RAP_TABLE[variant + 102] / 3;
946
 
947
        /* That's all values loaded, get on with the encoding */
948
 
949
        LeftRAP = LeftRAPStart;
950
        CentreRAP = CentreRAPStart;
951
        RightRAP = RightRAPStart;
952
        Cluster = StartCluster; /*
953
                                 * Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and
954
                                 * Cluster(6)
955
                                 */
956
 
957
        this.readable = "";
958
        this.pattern = new String[this.rows];
959
        this.row_count = this.rows;
960
        this.row_height = new int[this.rows];
961
 
962
        infoLine("Grid Size: " + this.columns + " X " + this.row_count);
963
 
964
        for (int i = 0; i < this.rows; i++) {
965
            codebarre.setLength(0);
966
            offset = 929 * Cluster;
967
            for (j = 0; j < 5; j++) {
968
                dummy[j] = 0;
969
            }
970
            for (j = 0; j < this.columns; j++) {
971
                dummy[j + 1] = this.codeWords[i * this.columns + j];
972
            }
973
 
974
            /* Copy the data into codebarre */
975
            codebarre.append(RAPLR[LeftRAP]);
976
            codebarre.append('1');
977
            codebarre.append(CODAGEMC[offset + dummy[1]]);
978
            codebarre.append('1');
979
            if (this.columns == 3) {
980
                codebarre.append(RAPC[CentreRAP]);
981
            }
982
            if (this.columns >= 2) {
983
                codebarre.append('1');
984
                codebarre.append(CODAGEMC[offset + dummy[2]]);
985
                codebarre.append('1');
986
            }
987
            if (this.columns == 4) {
988
                codebarre.append(RAPC[CentreRAP]);
989
            }
990
            if (this.columns >= 3) {
991
                codebarre.append('1');
992
                codebarre.append(CODAGEMC[offset + dummy[3]]);
993
                codebarre.append('1');
994
            }
995
            if (this.columns == 4) {
996
                codebarre.append('1');
997
                codebarre.append(CODAGEMC[offset + dummy[4]]);
998
                codebarre.append('1');
999
            }
1000
            codebarre.append(RAPLR[RightRAP]);
1001
            codebarre.append('1'); /* stop */
1002
 
1003
            /* Now codebarre is a mixture of letters and numbers */
1004
 
1005
            flip = 1;
1006
            bin.setLength(0);
1007
            for (loop = 0; loop < codebarre.length(); loop++) {
1008
                if (codebarre.charAt(loop) >= '0' && codebarre.charAt(loop) <= '9') {
1009
                    for (k = 0; k < Character.getNumericValue(codebarre.charAt(loop)); k++) {
1010
                        if (flip == 0) {
1011
                            bin.append('0');
1012
                        } else {
1013
                            bin.append('1');
1014
                        }
1015
                    }
1016
                    if (flip == 0) {
1017
                        flip = 1;
1018
                    } else {
1019
                        flip = 0;
1020
                    }
1021
                } else {
1022
                    bin.append(PDF_TTF[positionOf(codebarre.charAt(loop), BR_SET)]);
1023
                }
1024
            }
1025
 
1026
            /* so now pattern[] holds the string of '1's and '0's. - copy this to the symbol */
1027
            this.pattern[i] = bin2pat(bin);
1028
            this.row_height[i] = this.default_height;
1029
 
1030
            /* Set up RAPs and Cluster for next row */
1031
            LeftRAP++;
1032
            CentreRAP++;
1033
            RightRAP++;
1034
            Cluster++;
1035
 
1036
            if (LeftRAP == 53) {
1037
                LeftRAP = 1;
1038
            }
1039
            if (CentreRAP == 53) {
1040
                CentreRAP = 1;
1041
            }
1042
            if (RightRAP == 53) {
1043
                RightRAP = 1;
1044
            }
1045
            if (Cluster == 3) {
1046
                Cluster = 0;
1047
            }
1048
        }
1049
    }
1050
 
1051
    private void validateRows(final int min, final int max) {
1052
        if (this.rows != null) {
1053
            if (this.rows < min) {
1054
                throw new OkapiException("Too few rows (" + this.rows + ")");
1055
            } else if (this.rows > max) {
1056
                throw new OkapiException("Too many rows (" + this.rows + ")");
1057
            }
1058
        }
1059
    }
1060
 
1061
    private void validateColumns(final int min, final int max) {
1062
        if (this.columns != null) {
1063
            if (this.columns < min) {
1064
                throw new OkapiException("Too few columns (" + this.columns + ")");
1065
            } else if (this.columns > max) {
1066
                throw new OkapiException("Too many columns (" + this.columns + ")");
1067
            }
1068
        }
1069
    }
1070
 
1071
    private static EncodingMode chooseMode(final int codeascii) {
1072
        if (codeascii >= '0' && codeascii <= '9') {
1073
            return EncodingMode.NUM;
1074
        } else if (codeascii == '\t' || codeascii == '\n' || codeascii == '\r' || codeascii >= ' ' && codeascii <= '~') {
1075
            return EncodingMode.TEX;
1076
        } else {
1077
            return EncodingMode.BYT;
1078
        }
1079
    }
1080
 
1081
    private static int getMicroPdf417Variant(final int codeWordCount, final Integer columns, final Integer rows) {
1082
        for (int i = 0; i < 34; i++) {
1083
            final int maxCodewordCount = MICRO_AUTOSIZE[i];
1084
            if (codeWordCount <= maxCodewordCount) {
1085
                final int variant = MICRO_AUTOSIZE[i + 34];
1086
                final int columnsForThisVariant = MICRO_VARIANTS[variant - 1];
1087
                final int rowsForThisVariant = MICRO_VARIANTS[variant - 1 + 34];
1088
                if ((columns == null || columns == columnsForThisVariant) && (rows == null || rows == rowsForThisVariant)) {
1089
                    return variant;
1090
                }
1091
            }
1092
        }
1093
        throw new OkapiException("Unable to determine MicroPDF417 variant for " + codeWordCount + " codewords");
1094
    }
1095
 
1096
    /** Determines the encoding block groups for the specified data. */
1097
    private static List<Block> createBlocks(final int[] data) {
1098
 
1099
        final List<Block> blocks = new ArrayList<>();
1100
        Block current = null;
1101
 
1102
        for (int i = 0; i < data.length; i++) {
1103
            final EncodingMode mode = chooseMode(data[i]);
1104
            if (current != null && current.mode == mode && (mode != EncodingMode.NUM || current.length < MAX_NUMERIC_COMPACTION_BLOCK_SIZE)) {
1105
                current.length++;
1106
            } else {
1107
                current = new Block(mode);
1108
                blocks.add(current);
1109
            }
1110
        }
1111
 
1112
        smoothBlocks(blocks);
1113
 
1114
        return blocks;
1115
    }
1116
 
1117
    /** Combines adjacent blocks of different types in very specific scenarios. */
1118
    private static void smoothBlocks(final List<Block> blocks) {
1119
 
1120
        for (int i = 0; i < blocks.size(); i++) {
1121
            final Block block = blocks.get(i);
1122
            final EncodingMode last = i > 0 ? blocks.get(i - 1).mode : EncodingMode.FALSE;
1123
            final EncodingMode next = i < blocks.size() - 1 ? blocks.get(i + 1).mode : EncodingMode.FALSE;
1124
            if (block.mode == EncodingMode.NUM) {
1125
                if (i == 0) { /* first block */
1126
                    if (next == EncodingMode.TEX && block.length < 8) {
1127
                        block.mode = EncodingMode.TEX;
1128
                    } else if (next == EncodingMode.BYT && block.length == 1) {
1129
                        block.mode = EncodingMode.BYT;
1130
                    }
1131
                } else if (i == blocks.size() - 1) { /* last block */
1132
                    if (last == EncodingMode.TEX && block.length < 7) {
1133
                        block.mode = EncodingMode.TEX;
1134
                    } else if (last == EncodingMode.BYT && block.length == 1) {
1135
                        block.mode = EncodingMode.BYT;
1136
                    }
1137
                } else { /* not first or last block */
1138
                    if (last == EncodingMode.BYT && next == EncodingMode.BYT && block.length < 4) {
1139
                        block.mode = EncodingMode.BYT;
1140
                    } else if (last == EncodingMode.BYT && next == EncodingMode.TEX && block.length < 4) {
1141
                        block.mode = EncodingMode.TEX;
1142
                    } else if (last == EncodingMode.TEX && next == EncodingMode.BYT && block.length < 5) {
1143
                        block.mode = EncodingMode.TEX;
1144
                    } else if (last == EncodingMode.TEX && next == EncodingMode.TEX && block.length < 8) {
1145
                        block.mode = EncodingMode.TEX;
1146
                    } else if (last == EncodingMode.NUM && next == EncodingMode.TEX && block.length < 8) {
1147
                        block.mode = EncodingMode.TEX;
1148
                    }
1149
                }
1150
            }
1151
        }
1152
 
1153
        mergeBlocks(blocks);
1154
 
1155
        for (int i = 0; i < blocks.size(); i++) {
1156
            final Block block = blocks.get(i);
1157
            final EncodingMode last = i > 0 ? blocks.get(i - 1).mode : EncodingMode.FALSE;
1158
            final EncodingMode next = i < blocks.size() - 1 ? blocks.get(i + 1).mode : EncodingMode.FALSE;
1159
            if (block.mode == EncodingMode.TEX && i > 0) { /* not the first */
1160
                if (i == blocks.size() - 1) { /* the last one */
1161
                    if (last == EncodingMode.BYT && block.length == 1) {
1162
                        block.mode = EncodingMode.BYT;
1163
                    }
1164
                } else { /* not the last one */
1165
                    if (last == EncodingMode.BYT && next == EncodingMode.BYT && block.length < 5) {
1166
                        block.mode = EncodingMode.BYT;
1167
                    }
1168
                    if ((last == EncodingMode.BYT && next != EncodingMode.BYT || last != EncodingMode.BYT && next == EncodingMode.BYT) && block.length < 3) {
1169
                        block.mode = EncodingMode.BYT;
1170
                    }
1171
                }
1172
            }
1173
        }
1174
 
1175
        mergeBlocks(blocks);
1176
    }
1177
 
1178
    /** Combines adjacent blocks of the same type. */
1179
    private static void mergeBlocks(final List<Block> blocks) {
1180
        for (int i = 1; i < blocks.size(); i++) {
1181
            final Block b1 = blocks.get(i - 1);
1182
            final Block b2 = blocks.get(i);
1183
            if (b1.mode == b2.mode && (b1.mode != EncodingMode.NUM || b1.length + b2.length <= MAX_NUMERIC_COMPACTION_BLOCK_SIZE)) {
1184
                b1.length += b2.length;
1185
                blocks.remove(i);
1186
                i--;
1187
            }
1188
        }
1189
    }
1190
 
1191
    private void processText(final int start, final int length, final boolean skipLatch) {
1192
        int j, blockIndext, curtable;
1193
        int codeascii;
1194
        int wnet = 0;
1195
        final int[] listet0 = new int[length];
1196
        final int[] listet1 = new int[length];
1197
        final int[] chainet = new int[length * 4];
1198
 
1199
        /* listet will contain the table numbers and the value of each characters */
1200
        for (blockIndext = 0; blockIndext < length; blockIndext++) {
1201
            codeascii = this.inputData[start + blockIndext];
1202
            switch (codeascii) {
1203
            case '\t':
1204
                listet0[blockIndext] = 12;
1205
                listet1[blockIndext] = 12;
1206
                break;
1207
            case '\n':
1208
                listet0[blockIndext] = 8;
1209
                listet1[blockIndext] = 15;
1210
                break;
1211
            case 13:
1212
                listet0[blockIndext] = 12;
1213
                listet1[blockIndext] = 11;
1214
                break;
1215
            default:
1216
                listet0[blockIndext] = ASCII_X[codeascii - 32];
1217
                listet1[blockIndext] = ASCII_Y[codeascii - 32];
1218
                break;
1219
            }
1220
        }
1221
 
1222
        curtable = 1; /* default table */
1223
        for (j = 0; j < length; j++) {
1224
            if ((listet0[j] & curtable) != 0) { /* The character is in the current table */
1225
                chainet[wnet] = listet1[j];
1226
                wnet++;
1227
            } else { /* Obliged to change table */
1228
                boolean flag = false; /* True if we change table for only one character */
1229
                if (j == length - 1) {
1230
                    flag = true;
1231
                } else {
1232
                    if ((listet0[j] & listet0[j + 1]) == 0) {
1233
                        flag = true;
1234
                    }
1235
                }
1236
 
1237
                if (flag) { /* we change only one character - look for temporary switch */
1238
                    if ((listet0[j] & 1) != 0 && curtable == 2) { /* T_UPP */
1239
                        chainet[wnet] = 27;
1240
                        chainet[wnet + 1] = listet1[j];
1241
                        wnet += 2;
1242
                    }
1243
                    if ((listet0[j] & 8) != 0) { /* T_PUN */
1244
                        chainet[wnet] = 29;
1245
                        chainet[wnet + 1] = listet1[j];
1246
                        wnet += 2;
1247
                    }
1248
                    if (!((listet0[j] & 1) != 0 && curtable == 2 || (listet0[j] & 8) != 0)) {
1249
                        /* No temporary switch available */
1250
                        flag = false;
1251
                    }
1252
                }
1253
 
1254
                if (!flag) {
1255
                    int newtable;
1256
 
1257
                    if (j == length - 1) {
1258
                        newtable = listet0[j];
1259
                    } else {
1260
                        if ((listet0[j] & listet0[j + 1]) == 0) {
1261
                            newtable = listet0[j];
1262
                        } else {
1263
                            newtable = listet0[j] & listet0[j + 1];
1264
                        }
1265
                    }
1266
 
1267
                    /* Maintain the first if several tables are possible */
1268
                    switch (newtable) {
1269
                    case 3:
1270
                    case 5:
1271
                    case 7:
1272
                    case 9:
1273
                    case 11:
1274
                    case 13:
1275
                    case 15:
1276
                        newtable = 1;
1277
                        break;
1278
                    case 6:
1279
                    case 10:
1280
                    case 14:
1281
                        newtable = 2;
1282
                        break;
1283
                    case 12:
1284
                        newtable = 4;
1285
                        break;
1286
                    }
1287
 
1288
                    /* select the switch */
1289
                    switch (curtable) {
1290
                    case 1:
1291
                        switch (newtable) {
1292
                        case 2:
1293
                            chainet[wnet] = 27;
1294
                            wnet++;
1295
                            break;
1296
                        case 4:
1297
                            chainet[wnet] = 28;
1298
                            wnet++;
1299
                            break;
1300
                        case 8:
1301
                            chainet[wnet] = 28;
1302
                            wnet++;
1303
                            chainet[wnet] = 25;
1304
                            wnet++;
1305
                            break;
1306
                        }
1307
                        break;
1308
                    case 2:
1309
                        switch (newtable) {
1310
                        case 1:
1311
                            chainet[wnet] = 28;
1312
                            wnet++;
1313
                            chainet[wnet] = 28;
1314
                            wnet++;
1315
                            break;
1316
                        case 4:
1317
                            chainet[wnet] = 28;
1318
                            wnet++;
1319
                            break;
1320
                        case 8:
1321
                            chainet[wnet] = 28;
1322
                            wnet++;
1323
                            chainet[wnet] = 25;
1324
                            wnet++;
1325
                            break;
1326
                        }
1327
                        break;
1328
                    case 4:
1329
                        switch (newtable) {
1330
                        case 1:
1331
                            chainet[wnet] = 28;
1332
                            wnet++;
1333
                            break;
1334
                        case 2:
1335
                            chainet[wnet] = 27;
1336
                            wnet++;
1337
                            break;
1338
                        case 8:
1339
                            chainet[wnet] = 25;
1340
                            wnet++;
1341
                            break;
1342
                        }
1343
                        break;
1344
                    case 8:
1345
                        switch (newtable) {
1346
                        case 1:
1347
                            chainet[wnet] = 29;
1348
                            wnet++;
1349
                            break;
1350
                        case 2:
1351
                            chainet[wnet] = 29;
1352
                            wnet++;
1353
                            chainet[wnet] = 27;
1354
                            wnet++;
1355
                            break;
1356
                        case 4:
1357
                            chainet[wnet] = 29;
1358
                            wnet++;
1359
                            chainet[wnet] = 28;
1360
                            wnet++;
1361
                            break;
1362
                        }
1363
                        break;
1364
                    }
1365
                    curtable = newtable;
1366
                    /* at last we add the character */
1367
                    chainet[wnet] = listet1[j];
1368
                    wnet++;
1369
                }
1370
            }
1371
        }
1372
 
1373
        if ((wnet & 1) != 0) {
1374
            chainet[wnet] = 29;
1375
            wnet++;
1376
        }
1377
 
1378
        /* Now translate the string chainet into codewords */
1379
 
1380
        if (!skipLatch) {
1381
            // text compaction mode is the default mode for PDF417,
1382
            // so no need for an explicit latch if this is the first block
1383
            this.codeWords[this.codeWordCount] = 900;
1384
            this.codeWordCount++;
1385
        }
1386
 
1387
        for (j = 0; j < wnet; j += 2) {
1388
            final int cw_number = 30 * chainet[j] + chainet[j + 1];
1389
            this.codeWords[this.codeWordCount] = cw_number;
1390
            this.codeWordCount++;
1391
        }
1392
    }
1393
 
1394
    private void processBytes(int start, final int length, final EncodingMode lastMode) {
1395
        int len = 0;
1396
        int chunkLen = 0;
1397
        BigInteger mantisa;
1398
        BigInteger total;
1399
        BigInteger word;
1400
 
1401
        mantisa = new BigInteger("0");
1402
        total = new BigInteger("0");
1403
 
1404
        if (length == 1 && lastMode == EncodingMode.TEX) {
1405
            this.codeWords[this.codeWordCount++] = 913;
1406
            this.codeWords[this.codeWordCount++] = this.inputData[start];
1407
        } else {
1408
            /* select the switch for multiple of 6 bytes */
1409
            if (length % 6 == 0) {
1410
                this.codeWords[this.codeWordCount++] = 924;
1411
            } else {
1412
                this.codeWords[this.codeWordCount++] = 901;
1413
            }
1414
 
1415
            while (len < length) {
1416
                chunkLen = length - len;
1417
                if (6 <= chunkLen) /* Take groups of 6 */ {
1418
                    chunkLen = 6;
1419
                    len += chunkLen;
1420
                    total = BigInteger.valueOf(0);
1421
 
1422
                    while (chunkLen-- != 0) {
1423
                        mantisa = BigInteger.valueOf(this.inputData[start++]);
1424
                        total = total.or(mantisa.shiftLeft(chunkLen * 8));
1425
                    }
1426
 
1427
                    chunkLen = 5;
1428
 
1429
                    while (chunkLen-- != 0) {
1430
 
1431
                        word = total.mod(BigInteger.valueOf(900));
1432
                        this.codeWords[this.codeWordCount + chunkLen] = word.intValue();
1433
                        total = total.divide(BigInteger.valueOf(900));
1434
                    }
1435
                    this.codeWordCount += 5;
1436
                } else /* If it remain a group of less than 6 bytes */ {
1437
                    len += chunkLen;
1438
                    while (chunkLen-- != 0) {
1439
                        this.codeWords[this.codeWordCount++] = this.inputData[start++];
1440
                    }
1441
                }
1442
            }
1443
        }
1444
    }
1445
 
1446
    private void processNumbers(final int[] data, final int start, final int length, final boolean skipLatch) {
1447
 
1448
        BigInteger tVal, dVal;
1449
        final int[] d = new int[16];
1450
        int cw_count;
1451
 
1452
        if (!skipLatch) {
1453
            // we don't need to latch to numeric mode in some cases, e.g.
1454
            // during numeric compaction of the Macro PDF417 segment index
1455
            this.codeWords[this.codeWordCount++] = 902;
1456
        }
1457
 
1458
        final StringBuilder t = new StringBuilder(length + 1);
1459
        t.append('1');
1460
        for (int i = 0; i < length; i++) {
1461
            t.append((char) data[start + i]);
1462
        }
1463
 
1464
        tVal = new BigInteger(t.toString());
1465
 
1466
        cw_count = 0;
1467
        do {
1468
            dVal = tVal.mod(BigInteger.valueOf(900));
1469
            d[cw_count] = dVal.intValue();
1470
            tVal = tVal.divide(BigInteger.valueOf(900));
1471
            cw_count++;
1472
        } while (tVal.compareTo(BigInteger.ZERO) == 1);
1473
 
1474
        for (int i = cw_count - 1; i >= 0; i--) {
1475
            this.codeWords[this.codeWordCount++] = d[i];
1476
        }
1477
    }
1478
 
1479
    /** Adds the Macro PDF417 control block codewords (if any). */
1480
    private void addMacroCodewords() {
1481
 
1482
        // if the structured append series size is 1, this isn't
1483
        // actually part of a structured append series
1484
        if (this.structuredAppendTotal == 1) {
1485
            return;
1486
        }
1487
 
1488
        // add the Macro marker codeword
1489
        this.codeWords[this.codeWordCount++] = 928;
1490
 
1491
        // add the segment index, padded with leading zeros to five digits
1492
        // use numeric compaction, but no latch
1493
        int segmentIndex = this.structuredAppendPosition - 1;
1494
        final int[] data = new int[5];
1495
        for (int x = data.length - 1; x >= 0; x--) {
1496
            data[x] = '0' + segmentIndex % 10;
1497
            segmentIndex /= 10;
1498
        }
1499
        processNumbers(data, 0, data.length, true);
1500
 
1501
        // add the file ID (base 900, which is easy since we limit
1502
        // file ID values to the range 0 to 899)
1503
        this.codeWords[this.codeWordCount++] = this.structuredAppendFileId;
1504
 
1505
        // NOTE: we could add the optional segment count field here, but
1506
        // it doesn't appear to be necessary... if we do eventually decide
1507
        // to add it, it will probably be [923, 001, count1, count2]
1508
 
1509
        // add the terminator to the last symbol of the series
1510
        final boolean last = this.structuredAppendPosition == this.structuredAppendTotal;
1511
        if (last) {
1512
            this.codeWords[this.codeWordCount++] = 922;
1513
        }
1514
    }
1515
 
1516
    private static class Block {
1517
 
1518
        public EncodingMode mode;
1519
        public int length;
1520
 
1521
        public Block(final EncodingMode mode) {
1522
            this.mode = mode;
1523
            this.length = 1;
1524
        }
1525
 
1526
        @Override
1527
        public String toString() {
1528
            return this.mode + "x" + this.length;
1529
        }
1530
    }
1531
}