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 Robin Stuart
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
package uk.org.okapibarcode.backend;
15
 
16
import java.awt.geom.Rectangle2D;
17
import java.nio.charset.StandardCharsets;
18
 
19
/**
20
 * <p>
21
 * Implements Code 16K symbology according to BS EN 12323:2005.
22
 *
23
 * <p>
24
 * Encodes using a stacked symbology based on Code 128. Supports encoding of any 8-bit ISO 8859-1
25
 * (Latin-1) data with a maximum data capacity of 77 alpha-numeric characters or 154 numerical
26
 * digits.
27
 *
28
 * @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
29
 */
30
public class Code16k extends Symbol {
31
 
32
    private enum Mode {
33
        NULL, SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB
34
    }
35
 
36
    /* EN 12323 Table 1 - "Code 16K" character encodations */
37
    private static final String[] C16K_TABLE = { "212222", "222122", "222221", "121223", "121322", "131222", "122213", "122312", "132212", "221213", "221312", "231212", "112232", "122132", "122231",
38
            "113222", "123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222", "321122", "321221", "312212", "322112", "322211", "212123", "212321", "232121",
39
            "111323", "131123", "131321", "112313", "132113", "132311", "211313", "231113", "231311", "112133", "112331", "132131", "113123", "113321", "133121", "313121", "211331", "231131",
40
            "213113", "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", "314111", "221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221",
41
            "112214", "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", "134111", "111242", "121142", "121241", "114212", "124112", "124211", "411212",
42
            "421112", "421211", "212141", "214121", "412121", "111143", "111341", "131141", "114113", "114311", "411113", "411311", "113141", "114131", "311141", "411131", "211412", "211214",
43
            "211232", "211133" };
44
 
45
    /* EN 12323 Table 3 and Table 4 - Start patterns and stop patterns */
46
    private static final String[] C16K_START_STOP = { "3211", "2221", "2122", "1411", "1132", "1231", "1114", "3112" };
47
 
48
    /* EN 12323 Table 5 - Start and stop values defining row numbers */
49
    private static final int[] C16K_START_VALUES = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 };
50
    private static final int[] C16K_STOP_VALUES = { 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 0, 1, 2, 3 };
51
 
52
    private final Mode[] block_mode = new Mode[170]; /* RENAME block_mode */
53
    private final int[] block_length = new int[170]; /* RENAME block_length */
54
    private int block_count;
55
 
56
    @Override
57
    protected boolean gs1Supported() {
58
        return true;
59
    }
60
 
61
    @Override
62
    protected void encode() {
63
 
64
        // TODO: is it possible to share any of this code with Code128, which is more up to date?
65
 
66
        String width_pattern;
67
        int current_row, rows_needed, first_check, second_check;
68
        int indexchaine, pads_needed;
69
        char[] set, fset;
70
        Mode mode;
71
        char last_set, current_set;
72
        int i, j, k, m, read;
73
        int[] values;
74
        int bar_characters;
75
        double glyph_count;
76
        int first_sum, second_sum;
77
        int input_length;
78
        int c_count;
79
        boolean f_state;
80
 
81
        if (!this.content.matches("[\u0000-\u00FF]+")) {
82
            throw new OkapiException("Invalid characters in input data");
83
        }
84
 
85
        this.inputData = toBytes(this.content, StandardCharsets.ISO_8859_1);
86
        input_length = this.inputData.length;
87
 
88
        bar_characters = 0;
89
        set = new char[160];
90
        fset = new char[160];
91
        values = new int[160];
92
 
93
        if (input_length > 157) {
94
            throw new OkapiException("Input too long");
95
        }
96
 
97
        /* Detect extended ASCII characters */
98
        for (i = 0; i < input_length; i++) {
99
            if (this.inputData[i] >= 128) {
100
                fset[i] = 'f';
101
            } else {
102
                fset[i] = ' ';
103
            }
104
        }
105
 
106
        /* Decide when to latch to extended mode */
107
        for (i = 0; i < input_length; i++) {
108
            j = 0;
109
            if (fset[i] == 'f') {
110
                do {
111
                    j++;
112
                } while (fset[i + j] == 'f');
113
                if (j >= 5 || j >= 3 && i + j == input_length - 1) {
114
                    for (k = 0; k <= j; k++) {
115
                        fset[i + k] = 'F';
116
                    }
117
                }
118
            }
119
        }
120
 
121
        /* Decide if it is worth reverting to 646 encodation for a few characters */
122
        if (input_length > 1) {
123
            for (i = 1; i < input_length; i++) {
124
                if (fset[i - 1] == 'F' && fset[i] == ' ') {
125
                    /* Detected a change from 8859-1 to 646 - count how long for */
126
                    for (j = 0; fset[i + j] == ' ' && i + j < input_length; j++) {
127
                        ;
128
                    }
129
                    if (j < 5 || j < 3 && i + j == input_length - 1) {
130
                        /* Change to shifting back rather than latching back */
131
                        for (k = 0; k < j; k++) {
132
                            fset[i + k] = 'n';
133
                        }
134
                    }
135
                }
136
            }
137
        }
138
 
139
        /* Detect mode A, B and C characters */
140
        this.block_count = 0;
141
        indexchaine = 0;
142
 
143
        mode = findSubset(this.inputData[indexchaine]);
144
        if (this.inputData[indexchaine] == FNC1) {
145
            mode = Mode.ABORC;
146
        } /* FNC1 */
147
 
148
        for (i = 0; i < 160; i++) {
149
            this.block_length[i] = 0;
150
        }
151
 
152
        do {
153
            this.block_mode[this.block_count] = mode;
154
            while (this.block_mode[this.block_count] == mode && indexchaine < input_length) {
155
                this.block_length[this.block_count]++;
156
                indexchaine++;
157
                if (indexchaine < input_length) {
158
                    mode = findSubset(this.inputData[indexchaine]);
159
                    if (this.inputData[indexchaine] == FNC1) {
160
                        mode = Mode.ABORC;
161
                    } /* FNC1 */
162
                }
163
            }
164
            this.block_count++;
165
        } while (indexchaine < input_length);
166
 
167
        reduceSubsetChanges(this.block_count);
168
 
169
        /* Put set data into set[] */
170
        read = 0;
171
        for (i = 0; i < this.block_count; i++) {
172
            for (j = 0; j < this.block_length[i]; j++) {
173
                switch (this.block_mode[i]) {
174
                case SHIFTA:
175
                    set[read] = 'a';
176
                    break;
177
                case LATCHA:
178
                    set[read] = 'A';
179
                    break;
180
                case SHIFTB:
181
                    set[read] = 'b';
182
                    break;
183
                case LATCHB:
184
                    set[read] = 'B';
185
                    break;
186
                case LATCHC:
187
                    set[read] = 'C';
188
                    break;
189
                }
190
                read++;
191
            }
192
        }
193
 
194
        /* Adjust for strings which start with shift characters - make them latch instead */
195
        if (set[0] == 'a') {
196
            i = 0;
197
            do {
198
                set[i] = 'A';
199
                i++;
200
            } while (set[i] == 'a');
201
        }
202
 
203
        if (set[0] == 'b') {
204
            i = 0;
205
            do {
206
                set[i] = 'B';
207
                i++;
208
            } while (set[i] == 'b');
209
        }
210
 
211
        /* Watch out for odd-length Mode C blocks */
212
        c_count = 0;
213
        for (i = 0; i < read; i++) {
214
            if (set[i] == 'C') {
215
                if (this.inputData[i] == FNC1) {
216
                    if ((c_count & 1) != 0) {
217
                        if (i - c_count != 0) {
218
                            set[i - c_count] = 'B';
219
                        } else {
220
                            set[i - 1] = 'B';
221
                        }
222
                    }
223
                    c_count = 0;
224
                } else {
225
                    c_count++;
226
                }
227
            } else {
228
                if ((c_count & 1) != 0) {
229
                    if (i - c_count != 0) {
230
                        set[i - c_count] = 'B';
231
                    } else {
232
                        set[i - 1] = 'B';
233
                    }
234
                }
235
                c_count = 0;
236
            }
237
        }
238
        if ((c_count & 1) != 0) {
239
            if (i - c_count != 0) {
240
                set[i - c_count] = 'B';
241
            } else {
242
                set[i - 1] = 'B';
243
            }
244
        }
245
        for (i = 1; i < read - 1; i++) {
246
            if (set[i] == 'C' && set[i - 1] == 'B' && set[i + 1] == 'B') {
247
                set[i] = 'B';
248
            }
249
        }
250
 
251
        /* Make sure the data will fit in the symbol */
252
        last_set = ' ';
253
        glyph_count = 0.0;
254
        for (i = 0; i < input_length; i++) {
255
            if (set[i] == 'a' || set[i] == 'b') {
256
                glyph_count = glyph_count + 1.0;
257
            }
258
            if (fset[i] == 'f' || fset[i] == 'n') {
259
                glyph_count = glyph_count + 1.0;
260
            }
261
            if (set[i] == 'A' || set[i] == 'B' || set[i] == 'C') {
262
                if (set[i] != last_set) {
263
                    last_set = set[i];
264
                    glyph_count = glyph_count + 1.0;
265
                }
266
            }
267
            if (i == 0) {
268
                if (set[i] == 'B' && set[1] == 'C') {
269
                    glyph_count = glyph_count - 1.0;
270
                }
271
                if (set[i] == 'B' && set[1] == 'B' && set[2] == 'C') {
272
                    glyph_count = glyph_count - 1.0;
273
                }
274
                if (fset[i] == 'F') {
275
                    glyph_count = glyph_count + 2.0;
276
                }
277
            } else {
278
                if (fset[i] == 'F' && fset[i - 1] != 'F') {
279
                    glyph_count = glyph_count + 2.0;
280
                }
281
                if (fset[i] != 'F' && fset[i - 1] == 'F') {
282
                    glyph_count = glyph_count + 2.0;
283
                }
284
            }
285
 
286
            if (set[i] == 'C' && this.inputData[i] != FNC1) {
287
                glyph_count = glyph_count + 0.5;
288
            } else {
289
                glyph_count = glyph_count + 1.0;
290
            }
291
        }
292
 
293
        if (this.inputDataType == DataType.GS1 && set[0] != 'A') {
294
            /* FNC1 can be integrated with mode character */
295
            glyph_count--;
296
        }
297
 
298
        if (glyph_count > 77.0) {
299
            throw new OkapiException("Input too long");
300
        }
301
 
302
        /* Calculate how tall the symbol will be */
303
        glyph_count = glyph_count + 2.0;
304
        i = (int) glyph_count;
305
        rows_needed = i / 5;
306
        if (i % 5 > 0) {
307
            rows_needed++;
308
        }
309
 
310
        if (rows_needed == 1) {
311
            rows_needed = 2;
312
        }
313
 
314
        /* start with the mode character - Table 2 */
315
        m = 0;
316
        switch (set[0]) {
317
        case 'A':
318
            m = 0;
319
            break;
320
        case 'B':
321
            m = 1;
322
            break;
323
        case 'C':
324
            m = 2;
325
            break;
326
        }
327
 
328
        if (this.readerInit) {
329
            if (m == 2) {
330
                m = 5;
331
            }
332
            if (this.inputDataType == DataType.GS1) {
333
                throw new OkapiException("Cannot use both GS1 mode and Reader Initialisation");
334
            } else {
335
                if (set[0] == 'B' && set[1] == 'C') {
336
                    m = 6;
337
                }
338
            }
339
            values[bar_characters] = 7 * (rows_needed - 2) + m; /* see 4.3.4.2 */
340
            values[bar_characters + 1] = 96; /* FNC3 */
341
            bar_characters += 2;
342
        } else {
343
            if (this.inputDataType == DataType.GS1) {
344
                /* Integrate FNC1 */
345
                switch (set[0]) {
346
                case 'B':
347
                    m = 3;
348
                    break;
349
                case 'C':
350
                    m = 4;
351
                    break;
352
                }
353
            } else {
354
                if (set[0] == 'B' && set[1] == 'C') {
355
                    m = 5;
356
                }
357
                if (set[0] == 'B' && set[1] == 'B' && set[2] == 'C') {
358
                    m = 6;
359
                }
360
            }
361
        }
362
        values[bar_characters] = 7 * (rows_needed - 2) + m; /* see 4.3.4.2 */
363
        bar_characters++;
364
        // }
365
        current_set = set[0];
366
        f_state = false;
367
        /*
368
         * f_state remembers if we are in Extended ASCII mode (value 1) or in ISO/IEC 646 mode
369
         * (value 0)
370
         */
371
        if (fset[0] == 'F') {
372
            switch (current_set) {
373
            case 'A':
374
                values[bar_characters] = 101;
375
                values[bar_characters + 1] = 101;
376
                break;
377
            case 'B':
378
                values[bar_characters] = 100;
379
                values[bar_characters + 1] = 100;
380
                break;
381
            }
382
            bar_characters += 2;
383
            f_state = true;
384
        }
385
 
386
        read = 0;
387
 
388
        /* Encode the data */
389
        do {
390
 
391
            if (read != 0 && set[read] != set[read - 1]) { /* Latch different code set */
392
                switch (set[read]) {
393
                case 'A':
394
                    values[bar_characters] = 101;
395
                    bar_characters++;
396
                    current_set = 'A';
397
                    break;
398
                case 'B':
399
                    values[bar_characters] = 100;
400
                    bar_characters++;
401
                    current_set = 'B';
402
                    break;
403
                case 'C':
404
                    if (!(read == 1 && set[0] == 'B')) { /* Not Mode C/Shift B */
405
                        if (!(read == 2 && set[0] == 'B' && set[1] == 'B')) {
406
                            /* Not Mode C/Double Shift B */
407
                            values[bar_characters] = 99;
408
                            bar_characters++;
409
                        }
410
                    }
411
                    current_set = 'C';
412
                    break;
413
                }
414
            }
415
            if (read != 0) {
416
                if (fset[read] == 'F' && !f_state) {
417
                    /* Latch beginning of extended mode */
418
                    switch (current_set) {
419
                    case 'A':
420
                        values[bar_characters] = 101;
421
                        values[bar_characters + 1] = 101;
422
                        break;
423
                    case 'B':
424
                        values[bar_characters] = 100;
425
                        values[bar_characters + 1] = 100;
426
                        break;
427
                    }
428
                    bar_characters += 2;
429
                    f_state = true;
430
                }
431
                if (fset[read] == ' ' && f_state) {
432
                    /* Latch end of extended mode */
433
                    switch (current_set) {
434
                    case 'A':
435
                        values[bar_characters] = 101;
436
                        values[bar_characters + 1] = 101;
437
                        break;
438
                    case 'B':
439
                        values[bar_characters] = 100;
440
                        values[bar_characters + 1] = 100;
441
                        break;
442
                    }
443
                    bar_characters += 2;
444
                    f_state = false;
445
                }
446
            }
447
 
448
            if (fset[i] == 'f' || fset[i] == 'n') {
449
                /* Shift extended mode */
450
                switch (current_set) {
451
                case 'A':
452
                    values[bar_characters] = 101; /* FNC 4 */
453
                    break;
454
                case 'B':
455
                    values[bar_characters] = 100; /* FNC 4 */
456
                    break;
457
                }
458
                bar_characters++;
459
            }
460
 
461
            if (set[i] == 'a' || set[i] == 'b') {
462
                /* Insert shift character */
463
                values[bar_characters] = 98;
464
                bar_characters++;
465
            }
466
 
467
            if (this.inputData[read] != FNC1) {
468
                switch (set[read]) { /* Encode data characters */
469
                case 'A':
470
                case 'a':
471
                    getValueSubsetA(this.inputData[read], values, bar_characters);
472
                    bar_characters++;
473
                    read++;
474
                    break;
475
                case 'B':
476
                case 'b':
477
                    getValueSubsetB(this.inputData[read], values, bar_characters);
478
                    bar_characters++;
479
                    read++;
480
                    break;
481
                case 'C':
482
                    getValueSubsetC(this.inputData[read], this.inputData[read + 1], values, bar_characters);
483
                    bar_characters++;
484
                    read += 2;
485
                    break;
486
                }
487
            } else {
488
                values[bar_characters] = 102;
489
                bar_characters++;
490
                read++;
491
            }
492
 
493
        } while (read < input_length);
494
 
495
        pads_needed = 5 - (bar_characters + 2) % 5;
496
        if (pads_needed == 5) {
497
            pads_needed = 0;
498
        }
499
        if (bar_characters + pads_needed < 8) {
500
            pads_needed += 8 - (bar_characters + pads_needed);
501
        }
502
        for (i = 0; i < pads_needed; i++) {
503
            values[bar_characters] = 106;
504
            bar_characters++;
505
        }
506
 
507
        /* Calculate check digits */
508
        first_sum = 0;
509
        second_sum = 0;
510
        for (i = 0; i < bar_characters; i++) {
511
            first_sum += (i + 2) * values[i];
512
            second_sum += (i + 1) * values[i];
513
        }
514
        first_check = first_sum % 107;
515
        second_sum += first_check * (bar_characters + 1);
516
        second_check = second_sum % 107;
517
        values[bar_characters] = first_check;
518
        values[bar_characters + 1] = second_check;
519
        bar_characters += 2;
520
 
521
        this.readable = "";
522
        this.pattern = new String[rows_needed];
523
        this.row_count = rows_needed;
524
        this.row_height = new int[rows_needed];
525
 
526
        infoLine("Symbol Rows: " + rows_needed);
527
        infoLine("First Check Digit: " + first_check);
528
        infoLine("Second Check Digit: " + second_check);
529
        info("Codewords: ");
530
 
531
        for (current_row = 0; current_row < rows_needed; current_row++) {
532
 
533
            width_pattern = "";
534
            width_pattern += C16K_START_STOP[C16K_START_VALUES[current_row]];
535
            width_pattern += "1";
536
            for (i = 0; i < 5; i++) {
537
                width_pattern += C16K_TABLE[values[current_row * 5 + i]];
538
                infoSpace(values[current_row * 5 + i]);
539
            }
540
            width_pattern += C16K_START_STOP[C16K_STOP_VALUES[current_row]];
541
 
542
            this.pattern[current_row] = width_pattern;
543
            this.row_height[current_row] = 10;
544
        }
545
        infoLine();
546
    }
547
 
548
    private void getValueSubsetA(final int source, final int[] values, final int bar_chars) {
549
        if (source > 127) {
550
            if (source < 160) {
551
                values[bar_chars] = source + 64 - 128;
552
            } else {
553
                values[bar_chars] = source - 32 - 128;
554
            }
555
        } else {
556
            if (source < 32) {
557
                values[bar_chars] = source + 64;
558
            } else {
559
                values[bar_chars] = source - 32;
560
            }
561
        }
562
    }
563
 
564
    private void getValueSubsetB(final int source, final int[] values, final int bar_chars) {
565
        if (source > 127) {
566
            values[bar_chars] = source - 32 - 128;
567
        } else {
568
            values[bar_chars] = source - 32;
569
        }
570
    }
571
 
572
    private void getValueSubsetC(final int source_a, final int source_b, final int[] values, final int bar_chars) {
573
        int weight;
574
 
575
        weight = 10 * Character.getNumericValue(source_a) + Character.getNumericValue(source_b);
576
        values[bar_chars] = weight;
577
    }
578
 
579
    private Mode findSubset(final int letter) {
580
        Mode mode;
581
 
582
        if (letter <= 31) {
583
            mode = Mode.SHIFTA;
584
        } else if (letter >= 48 && letter <= 57) {
585
            mode = Mode.ABORC;
586
        } else if (letter <= 95) {
587
            mode = Mode.AORB;
588
        } else if (letter <= 127) {
589
            mode = Mode.SHIFTB;
590
        } else if (letter <= 159) {
591
            mode = Mode.SHIFTA;
592
        } else if (letter <= 223) {
593
            mode = Mode.AORB;
594
        } else {
595
            mode = Mode.SHIFTB;
596
        }
597
 
598
        return mode;
599
    }
600
 
601
    private void reduceSubsetChanges(
602
            final int block_count) { /* Implements rules from ISO 15417 Annex E */
603
        int i, length;
604
        Mode current, last, next;
605
 
606
        for (i = 0; i < block_count; i++) {
607
            current = this.block_mode[i];
608
            length = this.block_length[i];
609
            if (i != 0) {
610
                last = this.block_mode[i - 1];
611
            } else {
612
                last = Mode.NULL;
613
            }
614
            if (i != block_count - 1) {
615
                next = this.block_mode[i + 1];
616
            } else {
617
                next = Mode.NULL;
618
            }
619
 
620
            if (i == 0) { /* first block */
621
                if (block_count == 1 && length == 2 && current == Mode.ABORC) { /* Rule 1a */
622
                    this.block_mode[i] = Mode.LATCHC;
623
                }
624
                if (current == Mode.ABORC) {
625
                    if (length >= 4) { /* Rule 1b */
626
                        this.block_mode[i] = Mode.LATCHC;
627
                    } else {
628
                        this.block_mode[i] = Mode.AORB;
629
                        current = Mode.AORB;
630
                    }
631
                }
632
                if (current == Mode.SHIFTA) { /* Rule 1c */
633
                    this.block_mode[i] = Mode.LATCHA;
634
                }
635
                if (current == Mode.AORB && next == Mode.SHIFTA) { /* Rule 1c */
636
                    this.block_mode[i] = Mode.LATCHA;
637
                    current = Mode.LATCHA;
638
                }
639
                if (current == Mode.AORB) { /* Rule 1d */
640
                    this.block_mode[i] = Mode.LATCHB;
641
                }
642
            } else {
643
                if (current == Mode.ABORC && length >= 4) { /* Rule 3 */
644
                    this.block_mode[i] = Mode.LATCHC;
645
                    current = Mode.LATCHC;
646
                }
647
                if (current == Mode.ABORC) {
648
                    this.block_mode[i] = Mode.AORB;
649
                    current = Mode.AORB;
650
                }
651
                if (current == Mode.AORB && last == Mode.LATCHA) {
652
                    this.block_mode[i] = Mode.LATCHA;
653
                    current = Mode.LATCHA;
654
                }
655
                if (current == Mode.AORB && last == Mode.LATCHB) {
656
                    this.block_mode[i] = Mode.LATCHB;
657
                    current = Mode.LATCHB;
658
                }
659
                if (current == Mode.AORB && next == Mode.SHIFTA) {
660
                    this.block_mode[i] = Mode.LATCHA;
661
                    current = Mode.LATCHA;
662
                }
663
                if (current == Mode.AORB && next == Mode.SHIFTB) {
664
                    this.block_mode[i] = Mode.LATCHB;
665
                    current = Mode.LATCHB;
666
                }
667
                if (current == Mode.AORB) {
668
                    this.block_mode[i] = Mode.LATCHB;
669
                    current = Mode.LATCHB;
670
                }
671
                if (current == Mode.SHIFTA && length > 1) { /* Rule 4 */
672
                    this.block_mode[i] = Mode.LATCHA;
673
                    current = Mode.LATCHA;
674
                }
675
                if (current == Mode.SHIFTB && length > 1) { /* Rule 5 */
676
                    this.block_mode[i] = Mode.LATCHB;
677
                    current = Mode.LATCHB;
678
                }
679
                if (current == Mode.SHIFTA && last == Mode.LATCHA) {
680
                    this.block_mode[i] = Mode.LATCHA;
681
                    current = Mode.LATCHA;
682
                }
683
                if (current == Mode.SHIFTB && last == Mode.LATCHB) {
684
                    this.block_mode[i] = Mode.LATCHB;
685
                    current = Mode.LATCHB;
686
                }
687
                if (current == Mode.SHIFTA && last == Mode.LATCHC) {
688
                    this.block_mode[i] = Mode.LATCHA;
689
                    current = Mode.LATCHA;
690
                }
691
                if (current == Mode.SHIFTB && last == Mode.LATCHC) {
692
                    this.block_mode[i] = Mode.LATCHB;
693
                    current = Mode.LATCHB;
694
                }
695
            } /* Rule 2 is implimented elsewhere, Rule 6 is implied */
696
        }
697
        combineSubsetBlocks(block_count);
698
 
699
    }
700
 
701
    private void combineSubsetBlocks(int block_count) {
702
        int i, j;
703
 
704
        /* bring together same type blocks */
705
        if (block_count > 1) {
706
            i = 1;
707
            while (i < block_count) {
708
                if (this.block_mode[i - 1] == this.block_mode[i]) {
709
                    /* bring together */
710
                    this.block_length[i - 1] = this.block_length[i - 1] + this.block_length[i];
711
                    j = i + 1;
712
 
713
                    /* decreace the list */
714
                    while (j < block_count) {
715
                        this.block_length[j - 1] = this.block_length[j];
716
                        this.block_mode[j - 1] = this.block_mode[j];
717
                        j++;
718
                    }
719
                    block_count = block_count - 1;
720
                    i--;
721
                }
722
                i++;
723
            }
724
        }
725
    }
726
 
727
    @Override
728
    protected void plotSymbol() {
729
        int xBlock, yBlock;
730
        int x, y, w, h;
731
        boolean black;
732
 
733
        this.rectangles.clear();
734
        y = 1;
735
        h = 1;
736
        for (yBlock = 0; yBlock < this.row_count; yBlock++) {
737
            black = true;
738
            x = 15;
739
            for (xBlock = 0; xBlock < this.pattern[yBlock].length(); xBlock++) {
740
                if (black) {
741
                    black = false;
742
                    w = this.pattern[yBlock].charAt(xBlock) - '0';
743
                    if (this.row_height[yBlock] == -1) {
744
                        h = this.default_height;
745
                    } else {
746
                        h = this.row_height[yBlock];
747
                    }
748
                    if (w != 0 && h != 0) {
749
                        final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
750
                        this.rectangles.add(rect);
751
                    }
752
                    if (x + w > this.symbol_width) {
753
                        this.symbol_width = x + w;
754
                    }
755
                } else {
756
                    black = true;
757
                }
758
                x += this.pattern[yBlock].charAt(xBlock) - '0';
759
            }
760
            y += h;
761
            if (y > this.symbol_height) {
762
                this.symbol_height = y;
763
            }
764
            /* Add bars between rows */
765
            if (yBlock != this.row_count - 1) {
766
                final Rectangle2D.Double rect = new Rectangle2D.Double(15, y - 1, this.symbol_width - 15, 2);
767
                this.rectangles.add(rect);
768
            }
769
        }
770
 
771
        /* Add top and bottom binding bars */
772
        final Rectangle2D.Double top = new Rectangle2D.Double(0, 0, this.symbol_width + 15, 2);
773
        this.rectangles.add(top);
774
        final Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, this.symbol_width + 15, 2);
775
        this.rectangles.add(bottom);
776
        this.symbol_width += 15;
777
        this.symbol_height += 1;
778
    }
779
}