summaryrefslogtreecommitdiffstats
path: root/zbar/decoder/codabar.c
diff options
context:
space:
mode:
Diffstat (limited to 'zbar/decoder/codabar.c')
-rw-r--r--zbar/decoder/codabar.c397
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);
+}