File: code128.hh

package info (click to toggle)
exactimage 1.0.2-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 2,940 kB
  • sloc: cpp: 35,715; ansic: 1,730; xml: 1,447; makefile: 323; perl: 138; sh: 108; python: 45; php: 37; ruby: 12
file content (458 lines) | stat: -rw-r--r-- 16,596 bytes parent folder | download | duplicates (2)
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
/*
 * Copyright (C) 2009 - 2015 René Rebe, ExactCODE GmbH Germany.
 * Copyright (C) 2007 - 2008 Lars Kuhtz, ExactCODE GmbH Germany.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2. A copy of the GNU General
 * Public License can be found in the file LICENSE.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT-
 * ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 *
 * Alternatively, commercial licensing options are available from the
 * copyright holder ExactCODE GmbH Germany.
 */

#ifndef _CODE128_HH_
#define _CODE128_HH_

#include <stdio.h>
#include <list>

#include "scanner_utils.hh"

namespace BarDecode
{
    struct code128_t
    {
        enum { FNC1, FNC2, FNC3, FNC4, SHIFT, CODEA, CODEB, CODEC, STARTA, STARTB, STARTC, END };
        static const char no_entry = 255;
        enum code_set_t { code_set_a, code_set_b, code_set_c };
     
        static const usize_t min_quiet_usize = 5; 
        static const usize_t min_quiet_usize_right = 5;

        code128_t();

        template<class TIT>
        scanner_result_t scan(TIT& start, TIT end, pos_t x, pos_t y, psize_t) const;
        
        template<class TIT>
        scanner_result_t reverse_scan(TIT& start, TIT end, pos_t x, pos_t y, psize_t) const;

        std::string decode128(code_set_t code_set, module_word_t mw) const; 
        code_set_t shift_code_set(code_set_t code_set) const;
        module_word_t get_key(module_word_t mw) const;

        bool is_end_key(module_word_t key) const;
        bool is_start_key(module_word_t key) const;
        bool is_no_entry(module_word_t key) const;
        scanner_result_t decode_key_list(const std::list<module_word_t>& list, pos_t x, pos_t y) const;

        DECLARE_TABLE(table,512);
        DECLARE_TABLE(aaux,10);
        DECLARE_TABLE(baux,10);
        DECLARE_TABLE(caux,10);

    };

    inline code128_t::code128_t()
    {
        // Code128 (255 indicates invalid module_word)
        // Based on 11 bits, where the first bit is 1 and the last bit is 0,
        // hence only 9 bit are used for lookup.
        INIT_TABLE(table,512,no_entry);
        PUT_IN_TABLE(table,0x166,0);
        PUT_IN_TABLE(table,0x136,1);
        PUT_IN_TABLE(table,0x133,2);
        PUT_IN_TABLE(table,0x4C,3);
        PUT_IN_TABLE(table,0x46,4);
        PUT_IN_TABLE(table,0x26,5);
        PUT_IN_TABLE(table,0x64,6);
        PUT_IN_TABLE(table,0x62,7);
        PUT_IN_TABLE(table,0x32,8);
        PUT_IN_TABLE(table,0x124,9);
        PUT_IN_TABLE(table,0x122,10);
        PUT_IN_TABLE(table,0x112,11);
        PUT_IN_TABLE(table,0xCE,12);
        PUT_IN_TABLE(table,0x6E,13);
        PUT_IN_TABLE(table,0x67,14);
        PUT_IN_TABLE(table,0xE6,15);
        PUT_IN_TABLE(table,0x76,16);
        PUT_IN_TABLE(table,0x73,17);
        PUT_IN_TABLE(table,0x139,18);
        PUT_IN_TABLE(table,0x12E,19);
        PUT_IN_TABLE(table,0x127,20);
        PUT_IN_TABLE(table,0x172,21);
        PUT_IN_TABLE(table,0x13A,22);
        PUT_IN_TABLE(table,0x1B7,23);
        PUT_IN_TABLE(table,0x1A6,24);
        PUT_IN_TABLE(table,0x196,25);
        PUT_IN_TABLE(table,0x193,26);
        PUT_IN_TABLE(table,0x1B2,27);
        PUT_IN_TABLE(table,0x19A,28);
        PUT_IN_TABLE(table,0x199,29);
        PUT_IN_TABLE(table,0x16C,30);
        PUT_IN_TABLE(table,0x163,31);
        PUT_IN_TABLE(table,0x11B,32);
        PUT_IN_TABLE(table,0x8C,33);
        PUT_IN_TABLE(table,0x2C,34);
        PUT_IN_TABLE(table,0x23,35);
        PUT_IN_TABLE(table,0xC4,36);
        PUT_IN_TABLE(table,0x34,37);
        PUT_IN_TABLE(table,0x31,38);
        PUT_IN_TABLE(table,0x144,39);
        PUT_IN_TABLE(table,0x114,40);
        PUT_IN_TABLE(table,0x111,41);
        PUT_IN_TABLE(table,0xDC,42);
        PUT_IN_TABLE(table,0xC7,43);
        PUT_IN_TABLE(table,0x37,44);
        PUT_IN_TABLE(table,0xEC,45);
        PUT_IN_TABLE(table,0xE3,46);
        PUT_IN_TABLE(table,0x3B,47);
        PUT_IN_TABLE(table,0x1BB,48);
        PUT_IN_TABLE(table,0x147,49);
        PUT_IN_TABLE(table,0x117,50);
        PUT_IN_TABLE(table,0x174,51);
        PUT_IN_TABLE(table,0x171,52);
        PUT_IN_TABLE(table,0x177,53);
        PUT_IN_TABLE(table,0x1AC,54);
        PUT_IN_TABLE(table,0x1A3,55);
        PUT_IN_TABLE(table,0x18B,56);
        PUT_IN_TABLE(table,0x1B4,57);
        PUT_IN_TABLE(table,0x1B1,58);
        PUT_IN_TABLE(table,0x18D,59);
        PUT_IN_TABLE(table,0x1BD,60);
        PUT_IN_TABLE(table,0x121,61);
        PUT_IN_TABLE(table,0x1C5,62);
        PUT_IN_TABLE(table,0x98,63);
        PUT_IN_TABLE(table,0x86,64);
        PUT_IN_TABLE(table,0x58,65);
        PUT_IN_TABLE(table,0x43,66);
        PUT_IN_TABLE(table,0x16,67);
        PUT_IN_TABLE(table,0x13,68);
        PUT_IN_TABLE(table,0xC8,69);
        PUT_IN_TABLE(table,0xC2,70);
        PUT_IN_TABLE(table,0x68,71);
        PUT_IN_TABLE(table,0x61,72);
        PUT_IN_TABLE(table,0x1A,73);
        PUT_IN_TABLE(table,0x19,74);
        PUT_IN_TABLE(table,0x109,75);
        PUT_IN_TABLE(table,0x128,76);
        PUT_IN_TABLE(table,0x1DD,77);
        PUT_IN_TABLE(table,0x10A,78);
        PUT_IN_TABLE(table,0x3D,79);
        PUT_IN_TABLE(table,0x9E,80);
        PUT_IN_TABLE(table,0x5E,81);
        PUT_IN_TABLE(table,0x4F,82);
        PUT_IN_TABLE(table,0xF2,83);
        PUT_IN_TABLE(table,0x7A,84);
        PUT_IN_TABLE(table,0x79,85);
        PUT_IN_TABLE(table,0x1D2,86);
        PUT_IN_TABLE(table,0x1CA,87);
        PUT_IN_TABLE(table,0x1C9,88);
        PUT_IN_TABLE(table,0x16F,89);
        PUT_IN_TABLE(table,0x17B,90);
        PUT_IN_TABLE(table,0x1DB,91);
        PUT_IN_TABLE(table,0xBC,92);
        PUT_IN_TABLE(table,0x8F,93);
        PUT_IN_TABLE(table,0x2F,94);
        PUT_IN_TABLE(table,0xF4,95);
        PUT_IN_TABLE(table,0xF1,96);
        PUT_IN_TABLE(table,0x1D4,97);
        PUT_IN_TABLE(table,0x1D1,98);
        PUT_IN_TABLE(table,0xEF,99);
        PUT_IN_TABLE(table,0xF7,100);
        PUT_IN_TABLE(table,0x1AF,101);
        PUT_IN_TABLE(table,0x1D7,102);
        PUT_IN_TABLE(table,0x142,103);
        PUT_IN_TABLE(table,0x148,104);
        PUT_IN_TABLE(table,0x14e,105);
        PUT_IN_TABLE(table,0x11d,106);

        // Range 96-105. Use offset -96
        INIT_TABLE(aaux,10,255);
        PUT_IN_TABLE(aaux,0,FNC3); // 96
        PUT_IN_TABLE(aaux,1,FNC2); // 97
        PUT_IN_TABLE(aaux,2,SHIFT); // 98
        PUT_IN_TABLE(aaux,3,CODEC); // 99
        PUT_IN_TABLE(aaux,4,CODEB); // 100
        PUT_IN_TABLE(aaux,5,FNC4); // 101
        PUT_IN_TABLE(aaux,6,FNC1); // 102
        PUT_IN_TABLE(aaux,7,STARTA); // 103
        PUT_IN_TABLE(aaux,8,STARTB); // 104
        PUT_IN_TABLE(aaux,9,STARTC); // 105

        INIT_TABLE(baux,10,255);
        PUT_IN_TABLE(baux,0,FNC3); // 96
        PUT_IN_TABLE(baux,1,FNC2); // 97
        PUT_IN_TABLE(baux,2,SHIFT); // 98
        PUT_IN_TABLE(baux,3,CODEC); // 99
        PUT_IN_TABLE(baux,4,FNC4); // 100
        PUT_IN_TABLE(baux,5,CODEA); // 101
        PUT_IN_TABLE(baux,6,FNC1); // 102
        PUT_IN_TABLE(baux,7,STARTA); // 103
        PUT_IN_TABLE(baux,8,STARTB); // 104
        PUT_IN_TABLE(baux,9,STARTC); // 105

        INIT_TABLE(caux,10,255);
        PUT_IN_TABLE(caux,0,no_entry); // 96
        PUT_IN_TABLE(caux,1,no_entry); // 97
        PUT_IN_TABLE(caux,2,no_entry); // 98
        PUT_IN_TABLE(caux,3,no_entry); // 99
        PUT_IN_TABLE(caux,4,CODEB); // 100
        PUT_IN_TABLE(caux,5,CODEA); // 101
        PUT_IN_TABLE(caux,6,FNC1); // 102
        PUT_IN_TABLE(caux,7,STARTA); // 103
        PUT_IN_TABLE(caux,8,STARTB); // 104
        PUT_IN_TABLE(caux,9,STARTC); // 105
    };

    inline bool code128_t::is_end_key(module_word_t key) const
    {
        return table[key] == 106;
    }

    inline bool code128_t::is_no_entry(module_word_t key) const
    {
        return table[key] == no_entry;
    }

    inline bool code128_t::is_start_key(module_word_t key) const
    {
        return table[key] >= 103 && table[key] <= 105;
    }


    // "" indicates no_entry
    inline std::string code128_t::decode128(code_set_t code_set, module_word_t key) const
    {
        int c = table[key];
        if (c == no_entry) return "";
        if (c == 106) return std::string(1,END);
        switch (code_set) {
        case code_set_c:
            if (c < 100) {
                char str[3];
                snprintf(str,sizeof(str),"%02d",c);
                return std::string(str);
            } else {
                return std::string(1,caux[c-96]);
            }
            break;
        case code_set_b:
            if (c < 96) {
                return std::string(1,c+32);
            } else {
                return std::string(1,baux[c-96]);
            }
            break;
        case code_set_a:
            if (c < 64) {
                return std::string(1,c+32);
            } else if ( c < 96 ) {
                return std::string(1,c-64);
            } else {
                return std::string(1,aaux[c-96]);
            }
            break;
        default: assert(false); return "";
        }
    }

    inline module_word_t code128_t::get_key(module_word_t mw) const
    {
        // assume first bit is 1 and last bit is 0
        // use only the 9 bits inbetween for lookup
        if ( ! ((1<<10)&mw) || (mw&1)) {
            return 0;
        }
        mw &= ((1<<10)-1);
        mw >>= 1;
        
        return mw;
    }

    inline code128_t::code_set_t code128_t::shift_code_set(code_set_t code_set) const
    {
       switch (code_set) {
       case code_set_a: return code_set_b;
       case code_set_b: return code_set_a;
       default: return code_set;
       }
    }

    // FNC1 as first symbol indicates GS1-128
    //      in third or subsequent position: ascii 29.
    // TODO FNC2 (anywhere) concatenate barcode with next barcode
    // TODO FNC3 initialize or reprogram the barcode reader with the current code
    // TODO FNC4 switch to extended ascii (latin-1 as default)
    //      (quiet complicated usage refer to GS1 spec 5.3.3.4.2)
    template<class TIT>
    scanner_result_t code128_t::scan(TIT& start, TIT end, pos_t x, pos_t y, psize_t quiet_psize) const
    {
        using namespace scanner_utilities;

        // try to match start marker
        bar_vector_t b(6);
        if (get_bars(start,end,b,2) != 2 ) return scanner_result_t();

        if (b[0].second > 3 * b[1].second || b[0].second < 1.2 * b[1].second) return scanner_result_t();
        if ( add_bars(start,end,b,4) != 4) return scanner_result_t();

        // get a first guess for u
        u_t u = (double) b.psize / 11; // 11 is the number of modules of the start sequence

        // check if u is within max_u imposed by quiet_zone
        if ( u > max_u<code128_t>(quiet_psize) ) return scanner_result_t();

        // expect start sequence
        code_set_t cur_code_set;
        module_word_t key = get_key(get_module_word_adjust_u(b,u,11));

        std::string result = decode128(code_set_a,key);

        switch (result[0]) {
        case STARTA: cur_code_set = code_set_a; break;
        case STARTB: cur_code_set = code_set_b; break;
        case STARTC: cur_code_set = code_set_c; break;
        default: return scanner_result_t();
        }

        std::list<module_word_t> key_list;
        key_list.push_back(key);

        while (! is_end_key(key) ) { 
            // get symbol
            if ( get_bars(start,end,b,6) != 6 ) return scanner_result_t();
            key = get_key(get_module_word_adjust_u(b,u,11));
            if ( ! key || is_no_entry(key) ) return scanner_result_t();
            else key_list.push_back(key);
        }

        // remove end_key
        key_list.pop_back();

        if (key_list.size() <= 1) return scanner_result_t();

        // decode key_list (including check-summing)
        return decode_key_list(key_list,x,y);
    }

    // Backward algorithm:
    // 1. match end
    // loop:
    // 2. get key
    // 3. check if key does not decode to no_entry (independent of table type!)
    // 4. store key
    // 5. until: starta, startb, or startc is found
    // 6. decode keys in reverted order and compute checksum (using code from loop body above)
    // return
    template<class TIT>
    scanner_result_t code128_t::reverse_scan(TIT& start, TIT end, pos_t x, pos_t y, psize_t quiet_psize) const
    {
        using namespace scanner_utilities;

        // try to match end marker

        // expect a black bar of 2 modules
        bar_vector_t b(7);
        if ( get_bars(start,end,b,2) != 2) return scanner_result_t();
        if (b[0].second > 3 * b[1].second || b[0].second < 1.2 * b[1].second) return scanner_result_t();

        if ( add_bars(start,end,b,5) != 5) return scanner_result_t();

        // get a first guess for u
        u_t u = (double) b.psize / 13; // 13 is the number of modules of the end sequence

        // check if u is within max_u imposed by quiet_zone
        if ( u > max_u<code128_t>(quiet_psize) ) return scanner_result_t();

        // expect end sequence (well, the first 6 bars (or 7 modules) of it)
        b.erase(b.begin(),++b.begin());
        module_word_t key = get_key(reverse_get_module_word(b,u,11));
        if ( ! is_end_key(key)) return scanner_result_t();

        std::list<module_word_t> key_list;
        while (! is_start_key(key) ) { 
            // get symbol
            if ( get_bars(start,end,b,6) != 6 ) return scanner_result_t();
            key = get_key(reverse_get_module_word_adjust_u(b,u,11));
            if ( ! key || is_no_entry(key) ) return scanner_result_t();
            else key_list.push_front(key);
        } 

        if (key_list.size() <= 1) return scanner_result_t();

        // decode key_list (including check-summing)
        return decode_key_list(key_list,x,y);
    }

    inline scanner_result_t code128_t::decode_key_list(const std::list<module_word_t>& list, pos_t x, pos_t y) const
    {
        // initialize checksum:
        long checksum = table[list.front()];
        long pos = 0;
        std::string code = "";
        code_t type = code128;
        bool shift = false;
        code_set_t cur_code_set = code_set_a;

        for (std::list<module_word_t>::const_iterator it = list.begin(); it != --list.end(); ++it) {

            const module_word_t& key = *it;

            std::string result = decode128( (shift ? shift_code_set(cur_code_set) : cur_code_set), key);
            shift = false;
            switch (result.size()) {
            case 0: return scanner_result_t();
            case 2: code += result; break;
            case 1: 
                    switch (result[0]) {
                    case END: return scanner_result_t();
                    case SHIFT: shift = true; break;
                    case STARTA:
                    case CODEA: cur_code_set = code_set_a; break;
                    case STARTB:
                    case CODEB: cur_code_set = code_set_b; break;
                    case STARTC:
                    case CODEC: cur_code_set = code_set_c; break;
                    case FNC1: 
                                if (pos == 1) {
                                    type = gs1_128;
                                } else {
                                  code.push_back(29);
                                }
                                break;
                    case FNC2:
                    case FNC3:
                    case FNC4: std::cerr << "WARNING: Function characters for code128 are not yet supported." << std::endl;
                                break;
                    default: code += result;
                    }
                    break;
            default: return scanner_result_t();
            }

            // first run will be 0, last run (check-digit is ommitted in the loop)
            checksum += pos * (long) table[key];
            ++pos;

        }

        // Checksum and return result
        if (  (checksum % 103) != (long) table[list.back()] ) {
            //std::cerr << "WARNING: checksum test for code128 failed on \""<< code << "\"." << std::endl;
            //std::cerr << "         checksum: " << checksum << " % 103 = " << checksum % 103 << " != " << (long) table[list.back()] << std::endl;
            return scanner_result_t();
        } else {
            return scanner_result_t(type,code,x,y);
        }
    }

}; // namespace BarDecode

#endif // _CODE128_HH_