diff options
Diffstat (limited to 'zbar/decoder/ean.c')
-rw-r--r-- | zbar/decoder/ean.c | 735 |
1 files changed, 735 insertions, 0 deletions
diff --git a/zbar/decoder/ean.c b/zbar/decoder/ean.c new file mode 100644 index 0000000..024d7f8 --- /dev/null +++ b/zbar/decoder/ean.c @@ -0,0 +1,735 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2010 (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 <zbar.h> + +#ifdef DEBUG_EAN +#define DEBUG_LEVEL (DEBUG_EAN) +#endif +#include "debug.h" +#include "decoder.h" + +/* partial decode symbol location */ +typedef enum symbol_partial_e +{ + EAN_LEFT = 0x0000, + EAN_RIGHT = 0x1000, +} symbol_partial_t; + +/* convert compact encoded D2E1E2 to character (bit4 is parity) */ +static const unsigned char digits[] = { + /* E1 E2 */ + 0x06, 0x10, 0x04, 0x13, /* 2 2-5 */ + 0x19, 0x08, 0x11, 0x05, /* 3 2-5 (d2 <= thr) */ + 0x09, 0x12, 0x07, 0x15, /* 4 2-5 (d2 <= thr) */ + 0x16, 0x00, 0x14, 0x03, /* 5 2-5 */ + 0x18, 0x01, 0x02, 0x17, /* E1E2=43,44,33,34 (d2 > thr) */ +}; + +static const unsigned char parity_decode[] = { + 0xf0, /* [xx] BBBBBB = RIGHT half EAN-13 */ + + /* UPC-E check digit encoding */ + 0xff, 0xff, 0x0f, /* [07] BBBAAA = 0 */ + 0xff, 0x1f, /* [0b] BBABAA = 1 */ + 0x2f, /* [0d] BBAABA = 2 */ + 0xf3, /* [0e] BBAAAB = 3 */ + 0xff, 0x4f, /* [13] BABBAA = 4 */ + 0x7f, /* [15] BABABA = 7 */ + 0xf8, /* [16] BABAAB = 8 */ + 0x5f, /* [19] BAABBA = 5 */ + 0xf9, /* [1a] BAABAB = 9 */ + 0xf6, /* [1c] BAAABB = 6 */ + 0xff, + + /* LEFT half EAN-13 leading digit */ + 0xff, 0x6f, /* [23] ABBBAA = 6 */ + 0x9f, /* [25] ABBABA = 9 */ + 0xf5, /* [26] ABBAAB = 5 */ + 0x8f, /* [29] ABABBA = 8 */ + 0xf7, /* [2a] ABABAB = 7 */ + 0xf4, /* [2c] ABAABB = 4 */ + 0xff, 0x3f, /* [31] AABBBA = 3 */ + 0xf2, /* [32] AABBAB = 2 */ + 0xf1, /* [34] AABABB = 1 */ + 0xff, 0xff, 0xff, 0xff, 0x0f, /* [3f] AAAAAA = 0 */ +}; + +#ifdef DEBUG_EAN +static unsigned char debug_buf[0x18]; + +static inline const unsigned char *dsprintbuf(ean_decoder_t *ean) +{ + int i; + for (i = 0; i < 7; i++) + debug_buf[i] = + ((ean->buf[0] < 0 || ean->buf[i] < 0) ? '-' : ean->buf[i] + '0'); + debug_buf[i] = ' '; + for (; i < 13; i++) + debug_buf[i + 1] = + ((ean->buf[7] < 0 || ean->buf[i] < 0) ? '-' : ean->buf[i] + '0'); + debug_buf[i + 1] = ' '; + for (; i < 18; i++) + debug_buf[i + 2] = + ((ean->buf[13] < 0 || ean->buf[i] < 0) ? '-' : ean->buf[i] + '0'); + debug_buf[i + 2] = '\0'; + return (debug_buf); +} +#endif + +static inline int check_width(unsigned w0, unsigned w1) +{ + unsigned dw0 = w0; + w0 *= 8; + w1 *= 8; + return (w0 - dw0 <= w1 && w1 <= w0 + dw0); +} + +/* evaluate previous N (>= 2) widths as auxiliary pattern, + * using preceding 4 as character width + */ +static inline signed char aux_end(zbar_decoder_t *dcode, unsigned char fwd) +{ + signed char code, i; + + /* reference width from previous character */ + unsigned s = calc_s(dcode, 4 + fwd, 4); + + /* check quiet zone */ + unsigned qz = get_width(dcode, 0); + if (!fwd && qz && qz <= s * 3 / 4) { + dbprintf(2, " [invalid quiet]"); + return (-1); + } + + dbprintf(2, " ("); + code = 0; + for (i = 1 - fwd; i < 3 + fwd; i++) { + unsigned e = get_width(dcode, i) + get_width(dcode, i + 1); + dbprintf(2, " %d", e); + code = (code << 2) | decode_e(e, s, 7); + if (code < 0) { + dbprintf(2, " [invalid end guard]"); + return (-1); + } + } + dbprintf(2, ") s=%d aux=%x", s, code); + return (code); +} + +/* determine possible auxiliary pattern + * using current 4 as possible character + */ +static inline signed char aux_start(zbar_decoder_t *dcode) +{ + /* FIXME NB add-on has no guard in reverse */ + unsigned e1, e2 = get_width(dcode, 5) + get_width(dcode, 6); + unsigned char E1; + if (dcode->ean.s4 < 6) + return (-1); + + if (decode_e(e2, dcode->ean.s4, 7)) { + dbprintf(2, " [invalid any]"); + return (-1); + } + + e1 = get_width(dcode, 4) + get_width(dcode, 5); + E1 = decode_e(e1, dcode->ean.s4, 7); + + if (get_color(dcode) == ZBAR_BAR) { + /* check for quiet-zone */ + unsigned qz = get_width(dcode, 7); + if (!qz || qz > dcode->ean.s4 * 3 / 4) { + if (!E1) { + dbprintf(2, " [valid normal]"); + return (0); /* normal symbol start */ + } else if (E1 == 1) { + dbprintf(2, " [valid add-on]"); + return (STATE_ADDON); /* add-on symbol start */ + } + } + dbprintf(2, " [invalid start]"); + return (-1); + } + + if (!E1) { + /* attempting decode from SPACE => validate center guard */ + unsigned e3 = get_width(dcode, 6) + get_width(dcode, 7); + unsigned e4 = get_width(dcode, 7) + get_width(dcode, 8); + if (!decode_e(e3, dcode->ean.s4, 7) && + !decode_e(e4, dcode->ean.s4, 7)) { + dbprintf(2, " [valid center]"); + return (0); /* start after center guard */ + } + } + dbprintf(2, " [invalid center]"); + return (-1); +} + +/* check addon delimiter using current 4 as character + */ +static inline signed char aux_mid(zbar_decoder_t *dcode) +{ + unsigned e = get_width(dcode, 4) + get_width(dcode, 5); + return (decode_e(e, dcode->ean.s4, 7)); +} + +/* attempt to decode previous 4 widths (2 bars and 2 spaces) as a character */ +static inline signed char decode4(zbar_decoder_t *dcode) +{ + signed char code; + + /* calculate similar edge measurements */ + unsigned e1 = ((get_color(dcode) == ZBAR_BAR) ? + get_width(dcode, 0) + get_width(dcode, 1) : + get_width(dcode, 2) + get_width(dcode, 3)); + unsigned e2 = get_width(dcode, 1) + get_width(dcode, 2); + dbprintf(2, "\n e1=%d e2=%d", e1, e2); + + if (dcode->ean.s4 < 6) + return (-1); + + /* create compacted encoding for direct lookup */ + code = ((decode_e(e1, dcode->ean.s4, 7) << 2) | + decode_e(e2, dcode->ean.s4, 7)); + if (code < 0) + return (-1); + dbprintf(2, " code=%x", code); + + /* 4 combinations require additional determinant (D2) + E1E2 == 34 (0110) + E1E2 == 43 (1001) + E1E2 == 33 (0101) + E1E2 == 44 (1010) + */ + if ((1 << code) & 0x0660) { + unsigned char mid, alt; + /* use sum of bar widths */ + unsigned d2 = ((get_color(dcode) == ZBAR_BAR) ? + get_width(dcode, 0) + get_width(dcode, 2) : + get_width(dcode, 1) + get_width(dcode, 3)); + d2 *= 7; + mid = (((1 << code) & 0x0420) ? 3 /* E1E2 in 33,44 */ + : + 4); /* E1E2 in 34,43 */ + alt = d2 > (mid * dcode->ean.s4); + if (alt) + code = ((code >> 1) & 3) | 0x10; /* compress code space */ + dbprintf(2, " (d2=%d(%d) alt=%d)", d2, mid * dcode->ean.s4, alt); + } + dbprintf(2, " char=%02x", digits[(unsigned char)code]); + zassert(code < 0x14, -1, "code=%02x e1=%x e2=%x s4=%x color=%x\n", code, e1, + e2, dcode->ean.s4, get_color(dcode)); + return (code); +} + +static inline char ean_part_end2(ean_decoder_t *ean, ean_pass_t *pass) +{ + unsigned char par, chk; + if (!TEST_CFG(ean->ean2_config, ZBAR_CFG_ENABLE)) + return (ZBAR_NONE); + + /* extract parity bits */ + par = ((pass->raw[1] & 0x10) >> 3 | (pass->raw[2] & 0x10) >> 4); + /* calculate "checksum" */ + chk = ~((pass->raw[1] & 0xf) * 10 + (pass->raw[2] & 0xf)) & 0x3; + dbprintf(2, " par=%x chk=%x", par, chk); + if (par != chk) + return (ZBAR_NONE); + + dbprintf(2, "\n"); + dbprintf(1, "decode2=%x%x\n", pass->raw[1] & 0xf, pass->raw[2] & 0xf); + return (ZBAR_EAN2); +} + +static inline zbar_symbol_type_t ean_part_end4(ean_pass_t *pass, + unsigned char fwd) +{ + /* extract parity bits */ + unsigned char par = + ((pass->raw[1] & 0x10) >> 1 | (pass->raw[2] & 0x10) >> 2 | + (pass->raw[3] & 0x10) >> 3 | (pass->raw[4] & 0x10) >> 4); + + dbprintf(2, " par=%x", par); + if (par && par != 0xf) + /* invalid parity combination */ + return (ZBAR_NONE); + + if ((!par) == fwd) { + /* reverse sampled digits */ + unsigned char tmp = pass->raw[1]; + pass->state |= STATE_REV; + pass->raw[1] = pass->raw[4]; + pass->raw[4] = tmp; + tmp = pass->raw[2]; + pass->raw[2] = pass->raw[3]; + pass->raw[3] = tmp; + } + + dbprintf(2, "\n"); + dbprintf(1, "decode4=%x%x%x%x\n", pass->raw[1] & 0xf, pass->raw[2] & 0xf, + pass->raw[3] & 0xf, pass->raw[4] & 0xf); + if (!par) + return (ZBAR_EAN8 | EAN_RIGHT); + return (ZBAR_EAN8 | EAN_LEFT); +} + +static inline char ean_part_end5(ean_decoder_t *ean, ean_pass_t *pass) +{ + unsigned char par, chk, parchk; + if (!TEST_CFG(ean->ean5_config, ZBAR_CFG_ENABLE)) + return (ZBAR_NONE); + + /* extract parity bits */ + par = ((pass->raw[1] & 0x10) | (pass->raw[2] & 0x10) >> 1 | + (pass->raw[3] & 0x10) >> 2 | (pass->raw[4] & 0x10) >> 3 | + (pass->raw[5] & 0x10) >> 4); + /* calculate checksum */ + chk = (((pass->raw[1] & 0x0f) + (pass->raw[2] & 0x0f) * 3 + + (pass->raw[3] & 0x0f) + (pass->raw[4] & 0x0f) * 3 + + (pass->raw[5] & 0x0f)) * + 3) % + 10; + + parchk = parity_decode[par >> 1]; + if (par & 1) + parchk >>= 4; + parchk &= 0xf; + dbprintf(2, " par=%x(%d) chk=%d", par, parchk, chk); + if (parchk != chk) + return (ZBAR_NONE); + + dbprintf(2, "\n"); + dbprintf(1, "decode5=%x%x%x%x%x\n", pass->raw[1] & 0xf, pass->raw[2] & 0xf, + pass->raw[3] & 0xf, pass->raw[4] & 0xf, pass->raw[5] & 0xf); + + return (ZBAR_EAN5); +} + +static inline zbar_symbol_type_t +ean_part_end7(ean_decoder_t *ean, ean_pass_t *pass, unsigned char fwd) +{ + /* calculate parity index */ + unsigned char par = + ((fwd) ? ((pass->raw[1] & 0x10) << 1 | (pass->raw[2] & 0x10) | + (pass->raw[3] & 0x10) >> 1 | (pass->raw[4] & 0x10) >> 2 | + (pass->raw[5] & 0x10) >> 3 | (pass->raw[6] & 0x10) >> 4) : + ((pass->raw[1] & 0x10) >> 4 | (pass->raw[2] & 0x10) >> 3 | + (pass->raw[3] & 0x10) >> 2 | (pass->raw[4] & 0x10) >> 1 | + (pass->raw[5] & 0x10) | (pass->raw[6] & 0x10) << 1)); + + /* lookup parity combination */ + pass->raw[0] = parity_decode[par >> 1]; + if (par & 1) + pass->raw[0] >>= 4; + pass->raw[0] &= 0xf; + dbprintf(2, " par=%02x(%x)", par, pass->raw[0]); + + if (pass->raw[0] == 0xf) + /* invalid parity combination */ + return (ZBAR_NONE); + + if ((!par) == fwd) { + unsigned char i; + pass->state |= STATE_REV; + /* reverse sampled digits */ + for (i = 1; i < 4; i++) { + unsigned char tmp = pass->raw[i]; + pass->raw[i] = pass->raw[7 - i]; + pass->raw[7 - i] = tmp; + } + } + + dbprintf(2, "\n"); + dbprintf(1, "decode=%x%x%x%x%x%x%x(%02x)\n", pass->raw[0] & 0xf, + pass->raw[1] & 0xf, pass->raw[2] & 0xf, pass->raw[3] & 0xf, + pass->raw[4] & 0xf, pass->raw[5] & 0xf, pass->raw[6] & 0xf, par); + + if (TEST_CFG(ean->ean13_config, ZBAR_CFG_ENABLE)) { + if (!par) + return (ZBAR_EAN13 | EAN_RIGHT); + if (par & 0x20) + return (ZBAR_EAN13 | EAN_LEFT); + } + if (par && !(par & 0x20)) + return (ZBAR_UPCE); + + return (ZBAR_NONE); +} + +/* update state for one of 4 parallel passes */ +static inline zbar_symbol_type_t decode_pass(zbar_decoder_t *dcode, + ean_pass_t *pass) +{ + unsigned char idx, fwd; + pass->state++; + idx = pass->state & STATE_IDX; + fwd = pass->state & 1; + + if (get_color(dcode) == ZBAR_SPACE) { + if (pass->state & STATE_ADDON) { + dbprintf(2, " i=%d", idx); + if (idx == 0x09 || idx == 0x21) { + unsigned qz = get_width(dcode, 0); + unsigned s = calc_s(dcode, 1, 4); + zbar_symbol_type_t part = !qz || (qz >= s * 3 / 4); + if (part && idx == 0x09) + part = ean_part_end2(&dcode->ean, pass); + else if (part) + part = ean_part_end5(&dcode->ean, pass); + + if (part || idx == 0x21) { + dcode->ean.direction = 0; + pass->state = -1; + return (part); + } + } + if ((idx & 7) == 1) { + dbprintf(2, " +"); + pass->state += 2; + idx += 2; + } + } else if ((idx == 0x10 || idx == 0x11) && + TEST_CFG(dcode->ean.ean8_config, ZBAR_CFG_ENABLE) && + !aux_end(dcode, fwd)) { + zbar_symbol_type_t part; + dbprintf(2, " fwd=%x", fwd); + part = ean_part_end4(pass, fwd); + if (part) + dcode->ean.direction = (pass->state & STATE_REV) != 0; + pass->state = -1; + return (part); + } else if ((idx == 0x18 || idx == 0x19)) { + zbar_symbol_type_t part = ZBAR_NONE; + dbprintf(2, " fwd=%x", fwd); + if (!aux_end(dcode, fwd) && pass->raw[5] != 0xff) + part = ean_part_end7(&dcode->ean, pass, fwd); + if (part) + dcode->ean.direction = (pass->state & STATE_REV) != 0; + pass->state = -1; + return (part); + } + } + + if (pass->state & STATE_ADDON) + idx >>= 1; + + if (!(idx & 0x03) && idx <= 0x14) { + signed char code = -1; + unsigned w = pass->width; + if (!dcode->ean.s4) + return (0); + /* validate guard bars before decoding first char of symbol */ + if (!pass->state) { + pass->state = aux_start(dcode); + pass->width = dcode->ean.s4; + if (pass->state < 0) + return (0); + idx = pass->state & STATE_IDX; + } else { + w = check_width(w, dcode->ean.s4); + if (w) + pass->width = (pass->width + dcode->ean.s4 * 3) / 4; + } + + if (w) + code = decode4(dcode); + else + dbprintf(2, " [bad width]"); + + if ((code < 0 && idx != 0x10) || + (idx > 0 && (pass->state & STATE_ADDON) && aux_mid(dcode))) + pass->state = -1; + else if (code < 0) + pass->raw[5] = 0xff; + else { + dbprintf(2, "\n raw[%x]=%02x =>", idx >> 2, + digits[(unsigned char)code]); + pass->raw[(idx >> 2) + 1] = digits[(unsigned char)code]; + dbprintf(2, " raw=%d%d%d%d%d%d%d", pass->raw[0] & 0xf, + pass->raw[1] & 0xf, pass->raw[2] & 0xf, pass->raw[3] & 0xf, + pass->raw[4] & 0xf, pass->raw[5] & 0xf, + pass->raw[6] & 0xf); + } + } + return (0); +} + +static inline signed char ean_verify_checksum(ean_decoder_t *ean, int n) +{ + unsigned char chk = 0; + unsigned char i, d; + for (i = 0; i < n; i++) { + unsigned char d = ean->buf[i]; + zassert(d < 10, -1, "i=%x d=%x chk=%x %s\n", i, d, chk, + _zbar_decoder_buf_dump((void *)ean->buf, 18)); + chk += d; + if ((i ^ n) & 1) { + chk += d << 1; + if (chk >= 20) + chk -= 20; + } + if (chk >= 10) + chk -= 10; + } + zassert(chk < 10, -1, "chk=%x n=%x %s", chk, n, + _zbar_decoder_buf_dump((void *)ean->buf, 18)); + if (chk) + chk = 10 - chk; + d = ean->buf[n]; + zassert(d < 10, -1, "n=%x d=%x chk=%x %s\n", n, d, chk, + _zbar_decoder_buf_dump((void *)ean->buf, 18)); + if (chk != d) { + dbprintf(1, "\nchecksum mismatch %d != %d (%s)\n", chk, d, + dsprintbuf(ean)); + return (-1); + } + return (0); +} + +static inline unsigned char isbn10_calc_checksum(ean_decoder_t *ean) +{ + unsigned int chk = 0; + unsigned char w; + for (w = 10; w > 1; w--) { + unsigned char d = ean->buf[13 - w]; + zassert(d < 10, '?', "w=%x d=%x chk=%x %s\n", w, d, chk, + _zbar_decoder_buf_dump((void *)ean->buf, 18)); + chk += d * w; + } + chk = chk % 11; + if (!chk) + return ('0'); + chk = 11 - chk; + if (chk < 10) + return (chk + '0'); + return ('X'); +} + +static inline void ean_expand_upce(ean_decoder_t *ean, ean_pass_t *pass) +{ + int i = 0; + unsigned char decode; + /* parity encoded digit is checksum */ + ean->buf[12] = pass->raw[i++]; + + decode = pass->raw[6] & 0xf; + ean->buf[0] = 0; + ean->buf[1] = 0; + ean->buf[2] = pass->raw[i++] & 0xf; + ean->buf[3] = pass->raw[i++] & 0xf; + ean->buf[4] = (decode < 3) ? decode : pass->raw[i++] & 0xf; + ean->buf[5] = (decode < 4) ? 0 : pass->raw[i++] & 0xf; + ean->buf[6] = (decode < 5) ? 0 : pass->raw[i++] & 0xf; + ean->buf[7] = 0; + ean->buf[8] = 0; + ean->buf[9] = (decode < 3) ? pass->raw[i++] & 0xf : 0; + ean->buf[10] = (decode < 4) ? pass->raw[i++] & 0xf : 0; + ean->buf[11] = (decode < 5) ? pass->raw[i] & 0xf : decode; +} + +static inline zbar_symbol_type_t +integrate_partial(ean_decoder_t *ean, ean_pass_t *pass, zbar_symbol_type_t part) +{ + signed char i, j; + /* copy raw data into holding buffer */ + /* if same partial is not consistent, reset others */ + dbprintf(2, " integrate part=%x (%s)", part, dsprintbuf(ean)); + + if ((ean->left && ((part & ZBAR_SYMBOL) != ean->left)) || + (ean->right && ((part & ZBAR_SYMBOL) != ean->right))) { + /* partial mismatch - reset collected parts */ + dbprintf(2, " rst(type %x %x)", ean->left, ean->right); + ean->left = ean->right = ZBAR_NONE; + } + + if ((ean->left || ean->right) && !check_width(ean->width, pass->width)) { + dbprintf(2, " rst(width %d)", pass->width); + ean->left = ean->right = ZBAR_NONE; + } + + if (part & EAN_RIGHT) { + part &= ZBAR_SYMBOL; + j = part - 1; + for (i = part >> 1; i; i--, j--) { + unsigned char digit = pass->raw[i] & 0xf; + if (ean->right && ean->buf[j] != digit) { + /* partial mismatch - reset collected parts */ + dbprintf(2, " rst(right)"); + ean->left = ean->right = ZBAR_NONE; + } + ean->buf[j] = digit; + } + ean->right = part; + part &= ean->left; /* FIXME!? */ + } else if (part == ZBAR_EAN13 || part == ZBAR_EAN8) /* EAN_LEFT */ { + j = (part - 1) >> 1; + for (i = part >> 1; j >= 0; i--, j--) { + unsigned char digit = pass->raw[i] & 0xf; + if (ean->left && ean->buf[j] != digit) { + /* partial mismatch - reset collected parts */ + dbprintf(2, " rst(left)"); + ean->left = ean->right = ZBAR_NONE; + } + ean->buf[j] = digit; + } + ean->left = part; + part &= ean->right; /* FIXME!? */ + } else if (part != ZBAR_UPCE) /* add-ons */ { + for (i = part; i > 0; i--) + ean->buf[i - 1] = pass->raw[i] & 0xf; + ean->left = part; + } else + ean_expand_upce(ean, pass); + + ean->width = pass->width; + + if (!part) + part = ZBAR_PARTIAL; + + if (((part == ZBAR_EAN13 || part == ZBAR_UPCE) && + ean_verify_checksum(ean, 12)) || + (part == ZBAR_EAN8 && ean_verify_checksum(ean, 7))) { + /* invalid checksum */ + if (ean->right) + ean->left = ZBAR_NONE; + else + ean->right = ZBAR_NONE; + part = ZBAR_NONE; + } + + if (part == ZBAR_EAN13) { + /* special case EAN-13 subsets */ + if (!ean->buf[0] && TEST_CFG(ean->upca_config, ZBAR_CFG_ENABLE)) + part = ZBAR_UPCA; + else if (ean->buf[0] == 9 && ean->buf[1] == 7) { + if ((ean->buf[2] == 8 || ean->buf[2] == 9) && + TEST_CFG(ean->isbn13_config, ZBAR_CFG_ENABLE)) { + part = ZBAR_ISBN13; + } else if (ean->buf[2] == 8 && + TEST_CFG(ean->isbn10_config, ZBAR_CFG_ENABLE)) { + part = ZBAR_ISBN10; + } + } + } else if (part == ZBAR_UPCE) { + if (TEST_CFG(ean->upce_config, ZBAR_CFG_ENABLE)) { + /* UPC-E was decompressed for checksum verification, + * but user requested compressed result + */ + ean->buf[0] = ean->buf[1] = 0; + for (i = 2; i < 8; i++) + ean->buf[i] = pass->raw[i - 1] & 0xf; + ean->buf[i] = pass->raw[0] & 0xf; + } else if (TEST_CFG(ean->upca_config, ZBAR_CFG_ENABLE)) + /* UPC-E reported as UPC-A has priority over EAN-13 */ + part = ZBAR_UPCA; + else if (TEST_CFG(ean->ean13_config, ZBAR_CFG_ENABLE)) + part = ZBAR_EAN13; + else + part = ZBAR_NONE; + } + + dbprintf(2, " dir=%d %x/%x=%x", ean->direction, ean->left, ean->right, + part); + return (part); +} + +/* copy result to output buffer */ +static inline void postprocess(zbar_decoder_t *dcode, zbar_symbol_type_t sym) +{ + ean_decoder_t *ean = &dcode->ean; + zbar_symbol_type_t base = sym; + int i = 0, j = 0; + if (base > ZBAR_PARTIAL) { + if (base == ZBAR_UPCA) + i = 1; + else if (base == ZBAR_UPCE) { + i = 1; + base--; + } else if (base == ZBAR_ISBN13) + base = ZBAR_EAN13; + else if (base == ZBAR_ISBN10) + i = 3; + + if (base == ZBAR_ISBN10 || + (base > ZBAR_EAN5 && + !TEST_CFG(ean_get_config(ean, sym), ZBAR_CFG_EMIT_CHECK))) + base--; + + for (; j < base && ean->buf[i] >= 0; i++, j++) + dcode->buf[j] = ean->buf[i] + '0'; + + if (sym == ZBAR_ISBN10 && j == 9 && + TEST_CFG(ean->isbn10_config, ZBAR_CFG_EMIT_CHECK)) + /* recalculate ISBN-10 check digit */ + dcode->buf[j++] = isbn10_calc_checksum(ean); + } + dcode->buflen = j; + dcode->buf[j] = '\0'; + dcode->direction = 1 - 2 * ean->direction; + dcode->modifiers = 0; + dbprintf(2, " base=%d j=%d (%s)", base, j, dcode->buf); +} + +zbar_symbol_type_t _zbar_decode_ean(zbar_decoder_t *dcode) +{ + /* process upto 4 separate passes */ + zbar_symbol_type_t sym = ZBAR_NONE; + unsigned char pass_idx = dcode->idx & 3; + unsigned char i; + + /* update latest character width */ + dcode->ean.s4 -= get_width(dcode, 4); + dcode->ean.s4 += get_width(dcode, 0); + + for (i = 0; i < 4; i++) { + ean_pass_t *pass = &dcode->ean.pass[i]; + if (pass->state >= 0 || i == pass_idx) { + zbar_symbol_type_t part; + dbprintf(2, " ean[%x/%x]: idx=%x st=%d s=%d", i, pass_idx, + dcode->idx, pass->state, dcode->ean.s4); + part = decode_pass(dcode, pass); + if (part) { + /* update accumulated data from new partial decode */ + sym = integrate_partial(&dcode->ean, pass, part); + if (sym) { + /* this pass valid => _reset_ all passes */ + dbprintf(2, " sym=%x", sym); + dcode->ean.pass[0].state = dcode->ean.pass[1].state = -1; + dcode->ean.pass[2].state = dcode->ean.pass[3].state = -1; + if (sym > ZBAR_PARTIAL) { + if (!acquire_lock(dcode, sym)) + postprocess(dcode, sym); + else { + dbprintf(1, " [locked %d]", dcode->lock); + sym = ZBAR_PARTIAL; + } + } + } + } + dbprintf(2, "\n"); + } + } + return (sym); +} |