diff options
Diffstat (limited to 'zbar/decoder/codabar.c')
-rw-r--r-- | zbar/decoder/codabar.c | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/zbar/decoder/codabar.c b/zbar/decoder/codabar.c new file mode 100644 index 0000000..6e2c74a --- /dev/null +++ b/zbar/decoder/codabar.c @@ -0,0 +1,397 @@ +/*------------------------------------------------------------------------ + * Copyright 2011 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <string.h> /* memmove */ + +#include <zbar.h> + +#ifdef DEBUG_CODABAR +#define DEBUG_LEVEL (DEBUG_CODABAR) +#endif +#include "debug.h" +#include "decoder.h" + +#define NIBUF 6 /* initial scan buffer size */ + +static const signed char codabar_lo[12] = { 0x0, 0x1, 0x4, 0x5, 0x2, 0xa, + 0xb, 0x9, 0x6, 0x7, 0x8, 0x3 }; + +static const unsigned char codabar_hi[8] = { 0x1, 0x4, 0x7, 0x6, + 0x2, 0x3, 0x0, 0x5 }; + +static const unsigned char codabar_characters[20] = "0123456789-$:/.+ABCD"; + +static inline int check_width(unsigned ref, unsigned w) +{ + unsigned dref = ref; + ref *= 4; + w *= 4; + return (ref - dref <= w && w <= ref + dref); +} + +static inline signed char codabar_decode7(zbar_decoder_t *dcode) +{ + unsigned ibar, wbmax, wbmin, wb1, wb2; + unsigned long b0b3, b1b2; + unsigned ispc, wsmax, wsmid, wsmin; + unsigned long s0s2, s1s1; + codabar_decoder_t *codabar = &dcode->codabar; + unsigned s = codabar->s7; + dbprintf(2, " s=%d", s); + if (s < 7) + return (-1); + + /* check width */ + if (!check_width(codabar->width, s)) { + dbprintf(2, " [width]"); + return (-1); + } + + /* extract min/max bar */ + ibar = decode_sortn(dcode, 4, 1); + dbprintf(2, " bar=%04x", ibar); + + wbmax = get_width(dcode, ibar & 0xf); + wbmin = get_width(dcode, ibar >> 12); + if (8 * wbmin < wbmax || 3 * wbmin > 2 * wbmax) { + dbprintf(2, " [bar outer ratio]"); + return (-1); + } + + wb1 = get_width(dcode, (ibar >> 8) & 0xf); + wb2 = get_width(dcode, (ibar >> 4) & 0xf); + b0b3 = wbmin * wbmax; + b1b2 = wb1 * wb2; + if (b1b2 + b1b2 / 8 < b0b3) { + /* single wide bar combinations */ + if (8 * wbmin < 5 * wb1 || 8 * wb1 < 5 * wb2 || 4 * wb2 > 3 * wbmax || + wb2 * wb2 >= wb1 * wbmax) { + dbprintf(2, " [1bar inner ratios]"); + return (-1); + } + ibar = (ibar >> 1) & 0x3; + } else if (b1b2 > b0b3 + b0b3 / 8) { + /* three wide bars, no wide spaces */ + if (4 * wbmin > 3 * wb1 || 8 * wb1 < 5 * wb2 || 8 * wb2 < 5 * wbmax || + wbmin * wb2 >= wb1 * wb1) { + dbprintf(2, " [3bar inner ratios]"); + return (-1); + } + ibar = (ibar >> 13) + 4; + } else { + dbprintf(2, " [bar inner ratios]"); + return (-1); + } + + ispc = decode_sort3(dcode, 2); + dbprintf(2, "(%x) spc=%03x", ibar, ispc); + + wsmax = get_width(dcode, ispc & 0xf); + wsmid = get_width(dcode, (ispc >> 4) & 0xf); + wsmin = get_width(dcode, (ispc >> 8) & 0xf); + if (ibar >> 2) { + int c; + /* verify no wide spaces */ + if (8 * wsmin < wsmax || 8 * wsmin < 5 * wsmid || + 8 * wsmid < 5 * wsmax) { + dbprintf(2, " [0space inner ratios]"); + return (-1); + } + ibar &= 0x3; + if (codabar->direction) + ibar = 3 - ibar; + c = (0xfcde >> (ibar << 2)) & 0xf; + dbprintf(2, " ex[%d]=%x", ibar, c); + return (c); + } else if (8 * wsmin < wsmax || 3 * wsmin > 2 * wsmax) { + dbprintf(2, " [space outer ratio]"); + return (-1); + } + + s0s2 = wsmin * wsmax; + s1s1 = wsmid * wsmid; + if (s1s1 + s1s1 / 8 < s0s2) { + unsigned ic; + int c; + /* single wide space */ + if (8 * wsmin < 5 * wsmid || 4 * wsmid > 3 * wsmax) { + dbprintf(2, " [1space inner ratios]"); + return (-1); + } + ispc = ((ispc & 0xf) >> 1) - 1; + ic = (ispc << 2) | ibar; + if (codabar->direction) + ic = 11 - ic; + c = codabar_lo[ic]; + dbprintf(2, "(%d) lo[%d]=%x", ispc, ic, c); + return (c); + } else if (s1s1 > s0s2 + s0s2 / 8) { + unsigned ic; + unsigned char c; + /* two wide spaces, check start/stop */ + if (4 * wsmin > 3 * wsmid || 8 * wsmid < 5 * wsmax) { + dbprintf(2, " [2space inner ratios]"); + return (-1); + } + if ((ispc >> 8) == 4) { + dbprintf(2, " [space comb]"); + return (-1); + } + ispc >>= 10; + dbprintf(2, "(%d)", ispc); + ic = ispc * 4 + ibar; + zassert(ic < 8, -1, "ic=%d ispc=%d ibar=%d", ic, ispc, ibar); + c = codabar_hi[ic]; + if (c >> 2 != codabar->direction) { + dbprintf(2, " [invalid stop]"); + return (-1); + } + c = (c & 0x3) | 0x10; + dbprintf(2, " hi[%d]=%x", ic, c); + return (c); + } else { + dbprintf(2, " [space inner ratios]"); + return (-1); + } +} + +static inline signed char codabar_decode_start(zbar_decoder_t *dcode) +{ + unsigned qz, ispc, wsmax, wsmin, wsmid, ibar, wbmax, wbmin, wb1, wb2; + int ic, c; + codabar_decoder_t *codabar = &dcode->codabar; + unsigned s = codabar->s7; + if (s < 8) + return (ZBAR_NONE); + dbprintf(2, " codabar: s=%d", s); + + /* check leading quiet zone - spec is 10x */ + qz = get_width(dcode, 8); + if ((qz && qz * 2 < s) || 4 * get_width(dcode, 0) > 3 * s) { + dbprintf(2, " [invalid qz/ics]\n"); + return (ZBAR_NONE); + } + + /* check space ratios first */ + ispc = decode_sort3(dcode, 2); + dbprintf(2, " spc=%03x", ispc); + if ((ispc >> 8) == 4) { + dbprintf(2, " [space comb]\n"); + return (ZBAR_NONE); + } + + /* require 2 wide and 1 narrow spaces */ + wsmax = get_width(dcode, ispc & 0xf); + wsmin = get_width(dcode, ispc >> 8); + wsmid = get_width(dcode, (ispc >> 4) & 0xf); + if (8 * wsmin < wsmax || 3 * wsmin > 2 * wsmax || 4 * wsmin > 3 * wsmid || + 8 * wsmid < 5 * wsmax || wsmid * wsmid <= wsmax * wsmin) { + dbprintf(2, " [space ratio]\n"); + return (ZBAR_NONE); + } + ispc >>= 10; + dbprintf(2, "(%d)", ispc); + + /* check bar ratios */ + ibar = decode_sortn(dcode, 4, 1); + dbprintf(2, " bar=%04x", ibar); + + wbmax = get_width(dcode, ibar & 0xf); + wbmin = get_width(dcode, ibar >> 12); + if (8 * wbmin < wbmax || 3 * wbmin > 2 * wbmax) { + dbprintf(2, " [bar outer ratio]\n"); + return (ZBAR_NONE); + } + + /* require 1 wide & 3 narrow bars */ + wb1 = get_width(dcode, (ibar >> 8) & 0xf); + wb2 = get_width(dcode, (ibar >> 4) & 0xf); + if (8 * wbmin < 5 * wb1 || 8 * wb1 < 5 * wb2 || 4 * wb2 > 3 * wbmax || + wb1 * wb2 >= wbmin * wbmax || wb2 * wb2 >= wb1 * wbmax) { + dbprintf(2, " [bar inner ratios]\n"); + return (ZBAR_NONE); + } + ibar = ((ibar & 0xf) - 1) >> 1; + dbprintf(2, "(%d)", ibar); + + /* decode combination */ + ic = ispc * 4 + ibar; + zassert(ic < 8, ZBAR_NONE, "ic=%d ispc=%d ibar=%d", ic, ispc, ibar); + c = codabar_hi[ic]; + codabar->buf[0] = (c & 0x3) | 0x10; + + /* set character direction */ + codabar->direction = c >> 2; + + codabar->element = 4; + codabar->character = 1; + codabar->width = codabar->s7; + dbprintf(1, " start=%c dir=%x [valid start]\n", codabar->buf[0] + 0x31, + codabar->direction); + return (ZBAR_PARTIAL); +} + +static inline int codabar_checksum(zbar_decoder_t *dcode, unsigned n) +{ + unsigned chk = 0; + unsigned char *buf = dcode->buf; + while (n--) + chk += *(buf++); + return (!!(chk & 0xf)); +} + +static inline zbar_symbol_type_t codabar_postprocess(zbar_decoder_t *dcode) +{ + int dir, i, n; + codabar_decoder_t *codabar = &dcode->codabar; + dir = codabar->direction; + dcode->direction = 1 - 2 * dir; + n = codabar->character; + for (i = 0; i < NIBUF; i++) + dcode->buf[i] = codabar->buf[i]; + if (dir) + /* reverse buffer */ + for (i = 0; i < n / 2; i++) { + unsigned j = n - 1 - i; + char code = dcode->buf[i]; + dcode->buf[i] = dcode->buf[j]; + dcode->buf[j] = code; + } + + if (TEST_CFG(codabar->config, ZBAR_CFG_ADD_CHECK)) { + /* validate checksum */ + if (codabar_checksum(dcode, n)) + return (ZBAR_NONE); + if (!TEST_CFG(codabar->config, ZBAR_CFG_EMIT_CHECK)) { + dcode->buf[n - 2] = dcode->buf[n - 1]; + n--; + } + } + + for (i = 0; i < n; i++) { + unsigned c = dcode->buf[i]; + dcode->buf[i] = ((c < 0x14) ? codabar_characters[c] : '?'); + } + dcode->buflen = i; + dcode->buf[i] = '\0'; + dcode->modifiers = 0; + + codabar->character = -1; + return (ZBAR_CODABAR); +} + +zbar_symbol_type_t _zbar_decode_codabar(zbar_decoder_t *dcode) +{ + signed char c; + unsigned char *buf; + unsigned s; + codabar_decoder_t *codabar = &dcode->codabar; + + /* update latest character width */ + codabar->s7 -= get_width(dcode, 8); + codabar->s7 += get_width(dcode, 1); + + if (get_color(dcode) != ZBAR_SPACE) + return (ZBAR_NONE); + if (codabar->character < 0) + return (codabar_decode_start(dcode)); + if (codabar->character < 2 && codabar_decode_start(dcode)) + return (ZBAR_PARTIAL); + if (--codabar->element) + return (ZBAR_NONE); + codabar->element = 4; + + dbprintf(1, " codabar[%c%02d+%x]", (codabar->direction) ? '<' : '>', + codabar->character, codabar->element); + + c = codabar_decode7(dcode); + dbprintf(1, " %d", c); + if (c < 0) { + dbprintf(1, " [aborted]\n"); + goto reset; + } + + if (codabar->character < NIBUF) + buf = codabar->buf; + else { + if (codabar->character >= BUFFER_MIN && + size_buf(dcode, codabar->character + 1)) { + dbprintf(1, " [overflow]\n"); + goto reset; + } + buf = dcode->buf; + } + buf[codabar->character++] = c; + + /* lock shared resources */ + if (codabar->character == NIBUF && acquire_lock(dcode, ZBAR_CODABAR)) { + codabar->character = -1; + return (ZBAR_PARTIAL); + } + + s = codabar->s7; + if (c & 0x10) { + zbar_symbol_type_t sym; + unsigned n; + unsigned qz = get_width(dcode, 0); + if (qz && qz * 2 < s) { + dbprintf(2, " [invalid qz]\n"); + goto reset; + } + n = codabar->character; + if (n < CFG(*codabar, ZBAR_CFG_MIN_LEN) || + (CFG(*codabar, ZBAR_CFG_MAX_LEN) > 0 && + n > CFG(*codabar, ZBAR_CFG_MAX_LEN))) { + dbprintf(2, " [invalid len]\n"); + goto reset; + } + if (codabar->character < NIBUF && acquire_lock(dcode, ZBAR_CODABAR)) { + codabar->character = -1; + return (ZBAR_PARTIAL); + } + dbprintf(2, " stop=%c", c + 0x31); + + sym = codabar_postprocess(dcode); + if (sym > ZBAR_PARTIAL) + dbprintf(2, " [valid stop]"); + else { + release_lock(dcode, ZBAR_CODABAR); + codabar->character = -1; + } + dbprintf(2, "\n"); + return (sym); + } else if (4 * get_width(dcode, 0) > 3 * s) { + dbprintf(2, " [ics]\n"); + goto reset; + } + + dbprintf(2, "\n"); + return (ZBAR_NONE); + +reset: + if (codabar->character >= NIBUF) + release_lock(dcode, ZBAR_CODABAR); + codabar->character = -1; + return (ZBAR_NONE); +} |