diff mupdf-source/thirdparty/leptonica/src/bardecode.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/leptonica/src/bardecode.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,1027 @@
+/*====================================================================*
+ -  Copyright (C) 2001 Leptonica.  All rights reserved.
+ -
+ -  Redistribution and use in source and binary forms, with or without
+ -  modification, are permitted provided that the following conditions
+ -  are met:
+ -  1. Redistributions of source code must retain the above copyright
+ -     notice, this list of conditions and the following disclaimer.
+ -  2. Redistributions in binary form must reproduce the above
+ -     copyright notice, this list of conditions and the following
+ -     disclaimer in the documentation and/or other materials
+ -     provided with the distribution.
+ -
+ -  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ -  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ -  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ -  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY
+ -  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ -  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ -  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ -  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ -  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ -  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ -  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *====================================================================*/
+
+/*!
+ * \file bardecode.c
+ * <pre>
+ *
+ *      Dispatcher
+ *          char            *barcodeDispatchDecoder()
+ *
+ *      Format Determination
+ *          static l_int32   barcodeFindFormat()
+ *          l_int32          barcodeFormatIsSupported()
+ *          static l_int32   barcodeVerifyFormat()
+ *
+ *      Decode 2 of 5
+ *          static char     *barcodeDecode2of5()
+ *
+ *      Decode Interleaved 2 of 5
+ *          static char     *barcodeDecodeI2of5()
+ *
+ *      Decode Code 93
+ *          static char     *barcodeDecode93()
+ *
+ *      Decode Code 39
+ *          static char     *barcodeDecode39()
+ *
+ *      Decode Codabar
+ *          static char     *barcodeDecodeCodabar()
+ *
+ *      Decode UPC-A
+ *          static char     *barcodeDecodeUpca()
+ *
+ *      Decode EAN 13
+ *          static char     *barcodeDecodeEan13()
+ * </pre>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config_auto.h>
+#endif  /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include "allheaders.h"
+#include "readbarcode.h"
+
+static l_int32 barcodeFindFormat(char *barstr);
+static l_int32 barcodeVerifyFormat(char *barstr, l_int32 format,
+                                   l_int32 *pvalid, l_int32 *preverse);
+static char *barcodeDecode2of5(char *barstr, l_int32 debugflag);
+static char *barcodeDecodeI2of5(char *barstr, l_int32 debugflag);
+static char *barcodeDecode93(char *barstr, l_int32 debugflag);
+static char *barcodeDecode39(char *barstr, l_int32 debugflag);
+static char *barcodeDecodeCodabar(char *barstr, l_int32 debugflag);
+static char *barcodeDecodeUpca(char *barstr, l_int32 debugflag);
+static char *barcodeDecodeEan13(char *barstr, l_int32 first, l_int32 debugflag);
+
+#ifndef  NO_CONSOLE_IO
+#define  DEBUG_CODES       0
+#endif  /* ~NO_CONSOLE_IO */
+
+/*------------------------------------------------------------------------*
+ *                           Decoding dispatcher                          *
+ *------------------------------------------------------------------------*/
+/*!
+ * \brief   barcodeDispatchDecoder()
+ *
+ * \param[in]    barstr      string of integers in set {1,2,3,4} of bar widths
+ * \param[in]    format      L_BF_ANY, L_BF_CODEI2OF5, L_BF_CODE93, ...
+ * \param[in]    debugflag   use 1 to generate debug output
+ * \return  data string of decoded barcode data, or NULL on error
+ */
+char *
+barcodeDispatchDecoder(char    *barstr,
+                       l_int32  format,
+                       l_int32  debugflag)
+{
+char  *data = NULL;
+
+    if (!barstr)
+        return (char *)ERROR_PTR("barstr not defined", __func__, NULL);
+
+    debugflag = FALSE;  /* not used yet */
+
+    if (format == L_BF_ANY)
+        format = barcodeFindFormat(barstr);
+
+    if (format == L_BF_CODE2OF5)
+        data = barcodeDecode2of5(barstr, debugflag);
+    else if (format == L_BF_CODEI2OF5)
+        data = barcodeDecodeI2of5(barstr, debugflag);
+    else if (format == L_BF_CODE93)
+        data = barcodeDecode93(barstr, debugflag);
+    else if (format == L_BF_CODE39)
+        data = barcodeDecode39(barstr, debugflag);
+    else if (format == L_BF_CODABAR)
+        data = barcodeDecodeCodabar(barstr, debugflag);
+    else if (format == L_BF_UPCA)
+        data = barcodeDecodeUpca(barstr, debugflag);
+    else if (format == L_BF_EAN13)
+        data = barcodeDecodeEan13(barstr, 0, debugflag);
+    else
+        return (char *)ERROR_PTR("format not implemented", __func__, NULL);
+
+    return data;
+}
+
+
+/*------------------------------------------------------------------------*
+ *                      Barcode format determination                      *
+ *------------------------------------------------------------------------*/
+/*!
+ * \brief   barcodeFindFormat()
+ *
+ * \param[in]    barstr    of barcode widths, in set {1,2,3,4}
+ * \return  format for barcode, or L_BF_UNKNOWN if not recognized
+ */
+static l_int32
+barcodeFindFormat(char    *barstr)
+{
+l_int32  i, format, valid;
+
+   if (!barstr)
+       return ERROR_INT("barstr not defined", __func__, L_BF_UNKNOWN);
+
+   for (i = 0; i < NumSupportedBarcodeFormats; i++) {
+       format = SupportedBarcodeFormat[i];
+       barcodeVerifyFormat(barstr, format, &valid, NULL);
+       if (valid) {
+           L_INFO("Barcode format: %s\n", __func__,
+                   SupportedBarcodeFormatName[i]);
+           return format;
+       }
+   }
+   return L_BF_UNKNOWN;
+}
+
+
+/*!
+ * \brief   barcodeFormatIsSupported()
+ *
+ * \param[in]    format
+ * \return  1 if format is one of those supported; 0 otherwise
+ *
+ */
+l_int32
+barcodeFormatIsSupported(l_int32  format)
+{
+l_int32  i;
+
+   for (i = 0; i < NumSupportedBarcodeFormats; i++) {
+       if (format == SupportedBarcodeFormat[i])
+           return 1;
+   }
+   return 0;
+}
+
+
+/*!
+ * \brief   barcodeVerifyFormat()
+ *
+ * \param[in]    barstr     of barcode widths, in set {1,2,3,4}
+ * \param[in]    format     L_BF_CODEI2OF5, L_BF_CODE93, ...
+ * \param[out]   pvalid     0 if not valid, 1 and 2 if valid
+ * \param[out]   preverse   [optional] 1 if reversed; 0 otherwise
+ * \return  0 if OK, 1 on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) If valid == 1, the barcode is of the given format in the
+ *          forward order; if valid == 2, it is backwards.
+ *      (2) If the barcode needs to be reversed to read it, and &reverse
+ *          is provided, a 1 is put into %reverse.
+ *      (3) Require at least 12 data bits, in addition to format identifiers.
+ *          (TODO) If the barcode has a fixed length, this should be used
+ *          explicitly, as is done for L_BF_UPCA and L_BF_EAN13.
+ *      (4) (TODO) Add to this as more formats are supported.
+ * </pre>
+ */
+static l_int32
+barcodeVerifyFormat(char     *barstr,
+                    l_int32   format,
+                    l_int32  *pvalid,
+                    l_int32  *preverse)
+{
+char    *revbarstr;
+l_int32  i, start, len, stop, mid;
+
+    if (!pvalid)
+        return ERROR_INT("barstr not defined", __func__, 1);
+    *pvalid = 0;
+    if (preverse) *preverse = 0;
+    if (!barstr)
+        return ERROR_INT("barstr not defined", __func__, 1);
+
+    switch (format)
+    {
+    case L_BF_CODE2OF5:
+        start = !strncmp(barstr, Code2of5[C25_START], 3);
+        len = strlen(barstr);
+        if (len < 20)
+            return ERROR_INT("barstr too short for CODE2OF5", __func__, 1);
+        stop = !strncmp(&barstr[len - 5], Code2of5[C25_STOP], 5);
+        if (start && stop) {
+            *pvalid = 1;
+        } else {
+            revbarstr = stringReverse(barstr);
+            start = !strncmp(revbarstr, Code2of5[C25_START], 3);
+            stop = !strncmp(&revbarstr[len - 5], Code2of5[C25_STOP], 5);
+            LEPT_FREE(revbarstr);
+            if (start && stop) {
+                *pvalid = 1;
+                if (preverse) *preverse = 1;
+            }
+        }
+        break;
+    case L_BF_CODEI2OF5:
+        start = !strncmp(barstr, CodeI2of5[CI25_START], 4);
+        len = strlen(barstr);
+        if (len < 20)
+            return ERROR_INT("barstr too short for CODEI2OF5", __func__, 1);
+        stop = !strncmp(&barstr[len - 3], CodeI2of5[CI25_STOP], 3);
+        if (start && stop) {
+            *pvalid = 1;
+        } else {
+            revbarstr = stringReverse(barstr);
+            start = !strncmp(revbarstr, CodeI2of5[CI25_START], 4);
+            stop = !strncmp(&revbarstr[len - 3], CodeI2of5[CI25_STOP], 3);
+            LEPT_FREE(revbarstr);
+            if (start && stop) {
+                *pvalid = 1;
+                if (preverse) *preverse = 1;
+            }
+        }
+        break;
+    case L_BF_CODE93:
+        start = !strncmp(barstr, Code93[C93_START], 6);
+        len = strlen(barstr);
+        if (len < 28)
+            return ERROR_INT("barstr too short for CODE93", __func__, 1);
+        stop = !strncmp(&barstr[len - 7], Code93[C93_STOP], 6);
+        if (start && stop) {
+            *pvalid = 1;
+        } else {
+            revbarstr = stringReverse(barstr);
+            start = !strncmp(revbarstr, Code93[C93_START], 6);
+            stop = !strncmp(&revbarstr[len - 7], Code93[C93_STOP], 6);
+            LEPT_FREE(revbarstr);
+            if (start && stop) {
+                *pvalid = 1;
+                if (preverse) *preverse = 1;
+            }
+        }
+        break;
+    case L_BF_CODE39:
+        start = !strncmp(barstr, Code39[C39_START], 9);
+        len = strlen(barstr);
+        if (len < 30)
+            return ERROR_INT("barstr too short for CODE39", __func__, 1);
+        stop = !strncmp(&barstr[len - 9], Code39[C39_STOP], 9);
+        if (start && stop) {
+            *pvalid = 1;
+        } else {
+            revbarstr = stringReverse(barstr);
+            start = !strncmp(revbarstr, Code39[C39_START], 9);
+            stop = !strncmp(&revbarstr[len - 9], Code39[C39_STOP], 9);
+            LEPT_FREE(revbarstr);
+            if (start && stop) {
+                *pvalid = 1;
+                if (preverse) *preverse = 1;
+            }
+        }
+        break;
+    case L_BF_CODABAR:
+        start = stop = 0;
+        len = strlen(barstr);
+        if (len < 26)
+            return ERROR_INT("barstr too short for CODABAR", __func__, 1);
+        for (i = 16; i <= 19; i++)  /* any of these will do */
+            start += !strncmp(barstr, Codabar[i], 7);
+        for (i = 16; i <= 19; i++)  /* ditto */
+            stop += !strncmp(&barstr[len - 7], Codabar[i], 7);
+        if (start && stop) {
+            *pvalid = 1;
+        } else {
+            start = stop = 0;
+            revbarstr = stringReverse(barstr);
+            for (i = 16; i <= 19; i++)
+                start += !strncmp(revbarstr, Codabar[i], 7);
+            for (i = 16; i <= 19; i++)
+                stop += !strncmp(&revbarstr[len - 7], Codabar[i], 7);
+            LEPT_FREE(revbarstr);
+            if (start && stop) {
+                *pvalid = 1;
+                if (preverse) *preverse = 1;
+            }
+        }
+        break;
+    case L_BF_UPCA:
+    case L_BF_EAN13:
+        len = strlen(barstr);
+        if (len != 59)
+            return ERROR_INT("invalid length for UPCA or EAN13", __func__, 1);
+        start = !strncmp(barstr, Upca[UPCA_START], 3);
+        mid = !strncmp(&barstr[27], Upca[UPCA_MID], 5);
+        stop = !strncmp(&barstr[len - 3], Upca[UPCA_STOP], 3);
+        if (start && mid && stop)
+            *pvalid = 1;
+        break;
+    default:
+        return ERROR_INT("format not supported", __func__, 1);
+    }
+
+    return 0;
+}
+
+
+/*------------------------------------------------------------------------*
+ *                             Code 2 of 5                                *
+ *------------------------------------------------------------------------*/
+/*!
+ * \brief   barcodeDecode2of5()
+ *
+ * \param[in]    barstr     of widths, in set {1, 2}
+ * \param[in]    debugflag
+ * \return  data string of digits, or NULL if none found or on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) Ref: http://en.wikipedia.org/wiki/Two-out-of-five_code (Note:
+ *                 the codes given here are wrong!)
+ *               http://morovia.com/education/symbology/code25.asp
+ *      (2) This is a very low density encoding for the 10 digits.
+ *          Each digit is encoded with 5 black bars, of which 2 are wide
+ *          and 3 are narrow.  No information is carried in the spaces
+ *          between the bars, which are all equal in width, represented by
+ *          a "1" in our encoding.
+ *      (3) The mapping from the sequence of five bar widths to the
+ *          digit is identical to the mapping used by the interleaved
+ *          2 of 5 code.  The start code is 21211, representing two
+ *          wide bars and a narrow bar, and the interleaved "1" spaces
+ *          are explicit.  The stop code is 21112.  For all codes
+ *          (including start and stop), the trailing space "1" is
+ *          implicit -- there is no reason to represent it in the
+ *          Code2of5[] array.
+ * </pre>
+ */
+static char *
+barcodeDecode2of5(char    *barstr,
+                  l_int32  debugflag)
+{
+char    *data, *vbarstr;
+char     code[10];
+l_int32  valid, reverse, i, j, len, error, ndigits, start, found;
+
+    if (!barstr)
+        return (char *)ERROR_PTR("barstr not defined", __func__, NULL);
+
+        /* Verify format; reverse if necessary */
+    barcodeVerifyFormat(barstr, L_BF_CODE2OF5, &valid, &reverse);
+    if (!valid)
+        return (char *)ERROR_PTR("barstr not in 2of5 format", __func__, NULL);
+    if (reverse)
+        vbarstr = stringReverse(barstr);
+    else
+        vbarstr = stringNew(barstr);
+
+        /* Verify size */
+    len = strlen(vbarstr);
+    if ((len - 11) % 10 != 0) {
+        LEPT_FREE(vbarstr);
+        return (char *)ERROR_PTR("size not divisible by 10: invalid 2of5 code",
+                                 __func__, NULL);
+    }
+
+    error = FALSE;
+    ndigits = (len - 11) / 10;
+    data = (char *)LEPT_CALLOC(ndigits + 1, sizeof(char));
+    memset(code, 0, 10);
+    for (i = 0; i < ndigits; i++) {
+        start = 6 + 10 * i;
+        for (j = 0; j < 9; j++)
+            code[j] = vbarstr[start + j];
+
+        if (debugflag)
+            lept_stderr("code: %s\n", code);
+
+        found = FALSE;
+        for (j = 0; j < 10; j++) {
+            if (!strcmp(code, Code2of5[j])) {
+                data[i] = 0x30 + j;
+                found = TRUE;
+                break;
+            }
+        }
+        if (!found) error = TRUE;
+    }
+    LEPT_FREE(vbarstr);
+
+    if (error) {
+        LEPT_FREE(data);
+        return (char *)ERROR_PTR("error in decoding", __func__, NULL);
+    }
+
+    return data;
+}
+
+
+/*------------------------------------------------------------------------*
+ *                       Interleaved Code 2 of 5                          *
+ *------------------------------------------------------------------------*/
+/*!
+ * \brief   barcodeDecodeI2of5()
+ *
+ * \param[in]    barstr     of widths, in set {1, 2}
+ * \param[in]    debugflag
+ * \return  data string of digits, or NULL if none found or on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) Ref: http://en.wikipedia.org/wiki/Interleaved_2_of_5
+ *      (2) This always encodes an even number of digits.
+ *          The start code is 1111; the stop code is 211.
+ * </pre>
+ */
+static char *
+barcodeDecodeI2of5(char    *barstr,
+                   l_int32  debugflag)
+{
+char    *data, *vbarstr;
+char     code1[6], code2[6];
+l_int32  valid, reverse, i, j, len, error, npairs, start, found;
+
+    if (!barstr)
+        return (char *)ERROR_PTR("barstr not defined", __func__, NULL);
+
+        /* Verify format; reverse if necessary */
+    barcodeVerifyFormat(barstr, L_BF_CODEI2OF5, &valid, &reverse);
+    if (!valid)
+        return (char *)ERROR_PTR("barstr not in i2of5 format", __func__, NULL);
+    if (reverse)
+        vbarstr = stringReverse(barstr);
+    else
+        vbarstr = stringNew(barstr);
+
+        /* Verify size */
+    len = strlen(vbarstr);
+    if ((len - 7) % 10 != 0) {
+        LEPT_FREE(vbarstr);
+        return (char *)ERROR_PTR("size not divisible by 10: invalid I2of5 code",
+                                 __func__, NULL);
+    }
+
+    error = FALSE;
+    npairs = (len - 7) / 10;
+    data = (char *)LEPT_CALLOC(2 * npairs + 1, sizeof(char));
+    memset(code1, 0, 6);
+    memset(code2, 0, 6);
+    for (i = 0; i < npairs; i++) {
+        start = 4 + 10 * i;
+        for (j = 0; j < 5; j++) {
+            code1[j] = vbarstr[start + 2 * j];
+            code2[j] = vbarstr[start + 2 * j + 1];
+        }
+
+        if (debugflag)
+            lept_stderr("code1: %s, code2: %s\n", code1, code2);
+
+        found = FALSE;
+        for (j = 0; j < 10; j++) {
+            if (!strcmp(code1, CodeI2of5[j])) {
+                data[2 * i] = 0x30 + j;
+                found = TRUE;
+                break;
+            }
+        }
+        if (!found) error = TRUE;
+        found = FALSE;
+        for (j = 0; j < 10; j++) {
+            if (!strcmp(code2, CodeI2of5[j])) {
+                data[2 * i + 1] = 0x30 + j;
+                found = TRUE;
+                break;
+            }
+        }
+        if (!found) error = TRUE;
+    }
+    LEPT_FREE(vbarstr);
+
+    if (error) {
+        LEPT_FREE(data);
+        return (char *)ERROR_PTR("error in decoding", __func__, NULL);
+    }
+
+    return data;
+}
+
+
+/*------------------------------------------------------------------------*
+ *                                 Code 93                                *
+ *------------------------------------------------------------------------*/
+/*!
+ * \brief   barcodeDecode93()
+ *
+ * \param[in]    barstr     of widths, in set {1, 2, 3, 4}
+ * \param[in]    debugflag
+ * \return  data string of digits, or NULL if none found or on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) Ref:  http://en.wikipedia.org/wiki/Code93
+ *                http://morovia.com/education/symbology/code93.asp
+ *      (2) Each symbol has 3 black and 3 white bars.
+ *          The start and stop codes are 111141; the stop code then is
+ *          terminated with a final (1) bar.
+ *      (3) The last two codes are check codes.  We are checking them
+ *          for correctness, and issuing a warning on failure.  Should
+ *          probably not return any data on failure.
+ * </pre>
+ */
+static char *
+barcodeDecode93(char    *barstr,
+                l_int32  debugflag)
+{
+const char  *checkc, *checkk;
+char        *data, *vbarstr;
+char         code[7];
+l_int32      valid, reverse, i, j, len, error, nsymb, start, found, sum;
+l_int32     *index;
+
+    if (!barstr)
+        return (char *)ERROR_PTR("barstr not defined", __func__, NULL);
+
+        /* Verify format; reverse if necessary */
+    barcodeVerifyFormat(barstr, L_BF_CODE93, &valid, &reverse);
+    if (!valid)
+        return (char *)ERROR_PTR("barstr not in code93 format", __func__, NULL);
+    if (reverse)
+        vbarstr = stringReverse(barstr);
+    else
+        vbarstr = stringNew(barstr);
+
+        /* Verify size; skip the first 6 and last 7 bars. */
+    len = strlen(vbarstr);
+    if ((len - 13) % 6 != 0) {
+        LEPT_FREE(vbarstr);
+        return (char *)ERROR_PTR("size not divisible by 6: invalid code 93",
+                                 __func__, NULL);
+    }
+
+        /* Decode the symbols */
+    nsymb = (len - 13) / 6;
+    data = (char *)LEPT_CALLOC(nsymb + 1, sizeof(char));
+    index = (l_int32 *)LEPT_CALLOC(nsymb, sizeof(l_int32));
+    memset(code, 0, 7);
+    error = FALSE;
+    for (i = 0; i < nsymb; i++) {
+        start = 6 + 6 * i;
+        for (j = 0; j < 6; j++)
+            code[j] = vbarstr[start + j];
+
+        if (debugflag)
+            lept_stderr("code: %s\n", code);
+
+        found = FALSE;
+        for (j = 0; j < C93_START; j++) {
+            if (!strcmp(code, Code93[j])) {
+                data[i] = Code93Val[j];
+                index[i] = j;
+                found = TRUE;
+                break;
+            }
+        }
+        if (!found) error = TRUE;
+    }
+    LEPT_FREE(vbarstr);
+
+    if (error) {
+        LEPT_FREE(index);
+        LEPT_FREE(data);
+        return (char *)ERROR_PTR("error in decoding", __func__, NULL);
+    }
+
+        /* Do check sums.  For character "C", use only the
+         * actual data in computing the sum.  For character "K",
+         * use the actual data plus the check character "C". */
+    sum = 0;
+    for (i = 0; i < nsymb - 2; i++)  /* skip the "C" and "K" */
+        sum += ((i % 20) + 1) * index[nsymb - 3 - i];
+    if (data[nsymb - 2] != Code93Val[sum % 47])
+        L_WARNING("Error for check C\n", __func__);
+
+    if (debugflag) {
+        checkc = Code93[sum % 47];
+        lept_stderr("checkc = %s\n", checkc);
+    }
+
+    sum = 0;
+    for (i = 0; i < nsymb - 1; i++)  /* skip the "K" */
+        sum += ((i % 15) + 1) * index[nsymb - 2 - i];
+    if (data[nsymb - 1] != Code93Val[sum % 47])
+        L_WARNING("Error for check K\n", __func__);
+
+    if (debugflag) {
+        checkk = Code93[sum % 47];
+        lept_stderr("checkk = %s\n", checkk);
+    }
+
+        /* Remove the two check codes from the output */
+    data[nsymb - 2] = '\0';
+
+    LEPT_FREE(index);
+    return data;
+}
+
+
+/*------------------------------------------------------------------------*
+ *                                 Code 39                                *
+ *------------------------------------------------------------------------*/
+/*!
+ * \brief   barcodeDecode39()
+ *
+ * \param[in]    barstr     of widths, in set {1, 2}
+ * \param[in]    debugflag
+ * \return  data string of digits, or NULL if none found or on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) Ref:  http://en.wikipedia.org/wiki/Code39
+ *                http://morovia.com/education/symbology/code39.asp
+ *      (2) Each symbol has 5 black and 4 white bars.
+ *          The start and stop codes are 121121211 (the asterisk)
+ *      (3) This decoder was contributed by Roger Hyde.
+ * </pre>
+ */
+static char *
+barcodeDecode39(char    *barstr,
+                l_int32  debugflag)
+{
+char     *data, *vbarstr;
+char      code[10];
+l_int32   valid, reverse, i, j, len, error, nsymb, start, found;
+
+    if (!barstr)
+        return (char *)ERROR_PTR("barstr not defined", __func__, NULL);
+
+        /* Verify format; reverse if necessary */
+    barcodeVerifyFormat(barstr, L_BF_CODE39, &valid, &reverse);
+    if (!valid)
+        return (char *)ERROR_PTR("barstr not in code39 format", __func__, NULL);
+    if (reverse)
+        vbarstr = stringReverse(barstr);
+    else
+        vbarstr = stringNew(barstr);
+
+        /* Verify size */
+    len = strlen(vbarstr);
+    if ((len + 1) % 10 != 0) {
+        LEPT_FREE(vbarstr);
+        return (char *)ERROR_PTR("size+1 not divisible by 10: invalid code 39",
+                                 __func__, NULL);
+    }
+
+        /* Decode the symbols */
+    nsymb = (len - 19) / 10;
+    data = (char *)LEPT_CALLOC(nsymb + 1, sizeof(char));
+    memset(code, 0, 10);
+    error = FALSE;
+    for (i = 0; i < nsymb; i++) {
+        start = 10 + 10 * i;
+        for (j = 0; j < 9; j++)
+            code[j] = vbarstr[start + j];
+
+        if (debugflag)
+            lept_stderr("code: %s\n", code);
+
+        found = FALSE;
+        for (j = 0; j < C39_START; j++) {
+            if (!strcmp(code, Code39[j])) {
+                data[i] = Code39Val[j];
+                found = TRUE;
+                break;
+            }
+        }
+        if (!found) error = TRUE;
+    }
+    LEPT_FREE(vbarstr);
+
+    if (error) {
+        LEPT_FREE(data);
+        return (char *)ERROR_PTR("error in decoding", __func__, NULL);
+    }
+
+    return data;
+}
+
+
+/*------------------------------------------------------------------------*
+ *                                 Codabar                                *
+ *------------------------------------------------------------------------*/
+/*!
+ * \brief   barcodeDecodeCodabar()
+ *
+ * \param[in]    barstr     of widths, in set {1, 2}
+ * \param[in]    debugflag
+ * \return  data string of digits, or NULL if none found or on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) Ref:  http://en.wikipedia.org/wiki/Codabar
+ *                http://morovia.com/education/symbology/codabar.asp
+ *      (2) Each symbol has 4 black and 3 white bars.  They represent the
+ *          10 digits, and optionally 6 other characters.  The start and
+ *          stop codes can be any of four (typically denoted A,B,C,D).
+ * </pre>
+ */
+static char *
+barcodeDecodeCodabar(char    *barstr,
+                     l_int32  debugflag)
+{
+char     *data, *vbarstr;
+char      code[8];
+l_int32   valid, reverse, i, j, len, error, nsymb, start, found;
+
+    if (!barstr)
+        return (char *)ERROR_PTR("barstr not defined", __func__, NULL);
+
+        /* Verify format; reverse if necessary */
+    barcodeVerifyFormat(barstr, L_BF_CODABAR, &valid, &reverse);
+    if (!valid)
+        return (char *)ERROR_PTR("barstr not in codabar format",
+                                 __func__, NULL);
+    if (reverse)
+        vbarstr = stringReverse(barstr);
+    else
+        vbarstr = stringNew(barstr);
+
+        /* Verify size */
+    len = strlen(vbarstr);
+    if ((len + 1) % 8 != 0) {
+        LEPT_FREE(vbarstr);
+        return (char *)ERROR_PTR("size+1 not divisible by 8: invalid codabar",
+                                 __func__, NULL);
+    }
+
+        /* Decode the symbols */
+    nsymb = (len - 15) / 8;
+    data = (char *)LEPT_CALLOC(nsymb + 1, sizeof(char));
+    memset(code, 0, 8);
+    error = FALSE;
+    for (i = 0; i < nsymb; i++) {
+        start = 8 + 8 * i;
+        for (j = 0; j < 7; j++)
+            code[j] = vbarstr[start + j];
+
+        if (debugflag)
+            lept_stderr("code: %s\n", code);
+
+        found = FALSE;
+        for (j = 0; j < 16; j++) {
+            if (!strcmp(code, Codabar[j])) {
+                data[i] = CodabarVal[j];
+                found = TRUE;
+                break;
+            }
+        }
+        if (!found) error = TRUE;
+    }
+    LEPT_FREE(vbarstr);
+
+    if (error) {
+        LEPT_FREE(data);
+        return (char *)ERROR_PTR("error in decoding", __func__, NULL);
+    }
+
+    return data;
+}
+
+
+/*------------------------------------------------------------------------*
+ *                               Code UPC-A                               *
+ *------------------------------------------------------------------------*/
+/*!
+ * \brief   barcodeDecodeUpca()
+ *
+ * \param[in]    barstr     of widths, in set {1, 2, 3, 4}
+ * \param[in]    debugflag
+ * \return  data string of digits, or NULL if none found or on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) Ref:  http://en.wikipedia.org/wiki/UniversalProductCode
+ *                http://morovia.com/education/symbology/upc-a.asp
+ *      (2) Each symbol has 2 black and 2 white bars, and encodes a digit.
+ *          The start and stop codes are 111 and 111.  There are a total of
+ *          30 black bars, encoding 12 digits in two sets of 6, with
+ *          2 black bars separating the sets.
+ *      (3) The last digit is a check digit.  We check for correctness, and
+ *          issue a warning on failure.  Should probably not return any
+ *          data on failure.
+ * </pre>
+ */
+static char *
+barcodeDecodeUpca(char    *barstr,
+                  l_int32  debugflag)
+{
+char     *data, *vbarstr;
+char      code[5];
+l_int32   valid, i, j, len, error, start, found, sum, checkdigit;
+
+    if (!barstr)
+        return (char *)ERROR_PTR("barstr not defined", __func__, NULL);
+
+        /* Verify format; reverse has no meaning here -- we must test both */
+    barcodeVerifyFormat(barstr, L_BF_UPCA, &valid, NULL);
+    if (!valid)
+        return (char *)ERROR_PTR("barstr not in UPC-A format", __func__, NULL);
+
+        /* Verify size */
+    len = strlen(barstr);
+    if (len != 59)
+        return (char *)ERROR_PTR("size not 59; invalid UPC-A barcode",
+                                 __func__, NULL);
+
+        /* Check the first digit.  If invalid, reverse the string. */
+    memset(code, 0, 5);
+    for (i = 0; i < 4; i++)
+        code[i] = barstr[i + 3];
+    found = FALSE;
+    for (i = 0; i < 10; i++) {
+        if (!strcmp(code, Upca[i])) {
+            found = TRUE;
+            break;
+        }
+    }
+    if (found == FALSE)
+        vbarstr = stringReverse(barstr);
+    else
+        vbarstr = stringNew(barstr);
+
+        /* Decode the 12 symbols */
+    data = (char *)LEPT_CALLOC(13, sizeof(char));
+    memset(code, 0, 5);
+    error = FALSE;
+    for (i = 0; i < 12; i++) {
+        if (i < 6)
+            start = 3 + 4 * i;
+        else
+            start = 32 + 4 * (i - 6);
+        for (j = 0; j < 4; j++)
+            code[j] = vbarstr[start + j];
+
+        if (debugflag)
+            lept_stderr("code: %s\n", code);
+
+        found = FALSE;
+        for (j = 0; j < 10; j++) {
+            if (!strcmp(code, Upca[j])) {
+                data[i] = 0x30 + j;
+                found = TRUE;
+                break;
+            }
+        }
+        if (!found) error = TRUE;
+    }
+    LEPT_FREE(vbarstr);
+
+    if (error) {
+        LEPT_FREE(data);
+        return (char *)ERROR_PTR("error in decoding", __func__, NULL);
+    }
+
+        /* Calculate the check digit (data[11]). */
+    sum = 0;
+    for (i = 0; i < 12; i += 2)  /* "even" digits */
+        sum += 3 * (data[i] - 0x30);
+    for (i = 1; i < 11; i += 2)  /* "odd" digits */
+        sum += (data[i] - 0x30);
+    checkdigit = sum % 10;
+    if (checkdigit)  /* not 0 */
+        checkdigit = 10 - checkdigit;
+    if (checkdigit + 0x30 != data[11])
+        L_WARNING("Error for UPC-A check character\n", __func__);
+
+    return data;
+}
+
+
+/*------------------------------------------------------------------------*
+ *                               Code EAN-13                              *
+ *------------------------------------------------------------------------*/
+/*!
+ * \brief   barcodeDecodeEan13()
+ *
+ * \param[in]    barstr     of widths, in set {1, 2, 3, 4}
+ * \param[in]    first      first digit: 0 - 9
+ * \param[in]    debugflag
+ * \return  data string of digits, or NULL if none found or on error
+ *
+ * <pre>
+ * Notes:
+ *      (1) Ref:  http://en.wikipedia.org/wiki/UniversalProductCode
+ *                http://morovia.com/education/symbology/ean-13.asp
+ *      (2) The encoding is essentially the same as UPC-A, except
+ *          there are 13 digits in total, of which 12 are encoded
+ *          by bars (as with UPC-A) and the 13th is a leading digit
+ *          that determines the encoding of the next 6 digits,
+ *          selecting each digit from one of two tables.
+ *          encoded in the bars (as with UPC-A).  If the first digit
+ *          is 0, the encoding is identical to UPC-A.
+ *      (3) As with UPC-A, the last digit is a check digit.
+ *      (4) For now, we assume the first digit is input to this function.
+ *          Eventually, we will read it by pattern matching.
+ *
+ *    TODO: fix this for multiple tables, depending on the value of %first
+ * </pre>
+ */
+static char *
+barcodeDecodeEan13(char    *barstr,
+                   l_int32  first,
+                   l_int32  debugflag)
+{
+char     *data, *vbarstr;
+char      code[5];
+l_int32   valid, i, j, len, error, start, found, sum, checkdigit;
+
+    if (!barstr)
+        return (char *)ERROR_PTR("barstr not defined", __func__, NULL);
+
+        /* Verify format.  You can't tell the orientation by the start
+         * and stop codes, but you can by the location of the digits.
+         * Use the UPCA verifier for EAN 13 -- it is identical. */
+    barcodeVerifyFormat(barstr, L_BF_UPCA, &valid, NULL);
+    if (!valid)
+        return (char *)ERROR_PTR("barstr not in EAN 13 format", __func__, NULL);
+
+        /* Verify size */
+    len = strlen(barstr);
+    if (len != 59)
+        return (char *)ERROR_PTR("size not 59; invalid EAN 13 barcode",
+                                 __func__, NULL);
+
+        /* Check the first digit.  If invalid, reverse the string. */
+    memset(code, 0, 5);
+    for (i = 0; i < 4; i++)
+        code[i] = barstr[i + 3];
+    found = FALSE;
+    for (i = 0; i < 10; i++) {
+        if (!strcmp(code, Upca[i])) {
+            found = TRUE;
+            break;
+        }
+    }
+    if (found == FALSE)
+        vbarstr = stringReverse(barstr);
+    else
+        vbarstr = stringNew(barstr);
+
+        /* Decode the 12 symbols */
+    data = (char *)LEPT_CALLOC(13, sizeof(char));
+    memset(code, 0, 5);
+    error = FALSE;
+    for (i = 0; i < 12; i++) {
+        if (i < 6)
+            start = 3 + 4 * i;
+        else
+            start = 32 + 4 * (i - 6);
+        for (j = 0; j < 4; j++)
+            code[j] = vbarstr[start + j];
+
+        if (debugflag)
+            lept_stderr("code: %s\n", code);
+
+        found = FALSE;
+        for (j = 0; j < 10; j++) {
+            if (!strcmp(code, Upca[j])) {
+                data[i] = 0x30 + j;
+                found = TRUE;
+                break;
+            }
+        }
+        if (!found) error = TRUE;
+    }
+    LEPT_FREE(vbarstr);
+
+    if (error) {
+        LEPT_FREE(data);
+        return (char *)ERROR_PTR("error in decoding", __func__, NULL);
+    }
+
+        /* Calculate the check digit (data[11]). */
+    sum = 0;
+    for (i = 0; i < 12; i += 2)  /* "even" digits */
+        sum += 3 * (data[i] - 0x30);
+    for (i = 1; i < 12; i += 2)  /* "odd" digits */
+        sum += (data[i] - 0x30);
+    checkdigit = sum % 10;
+    if (checkdigit)  /* not 0 */
+        checkdigit = 10 - checkdigit;
+    if (checkdigit + 0x30 != data[11])
+        L_WARNING("Error for EAN-13 check character\n", __func__);
+
+    return data;
+}