diff mupdf-source/thirdparty/zint/backend/pdf417.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/zint/backend/pdf417.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,1805 @@
+/* pdf417.c - Handles PDF417 stacked symbology */
+/*
+    libzint - the open source barcode library
+    Copyright (C) 2008-2024 Robin Stuart <rstuart114@gmail.com>
+    Portions Copyright (C) 2004 Grandzebu
+    Bug Fixes thanks to KL Chin <klchin@users.sourceforge.net>
+
+    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.
+    3. Neither the name of the project nor the names of its contributors
+       may be used to endorse or promote products derived from this software
+       without specific prior written permission.
+
+    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 THE COPYRIGHT OWNER OR 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.
+ */
+/* SPDX-License-Identifier: BSD-3-Clause */
+
+/*  This code is adapted from "Code barre PDF 417 / PDF 417 barcode" v2.5.0
+    which is Copyright (C) 2004 (Grandzebu).
+    The original code (file pdf417.frm) can be downloaded from https://grandzebu.net/informatique/codbar/pdf417.zip */
+
+/* NOTE: symbol->option_1 is used to specify the security level (i.e. control the
+   number of check codewords)
+
+   symbol->option_2 is used to adjust the width of the resulting symbol (i.e. the
+   number of codeword columns not including row start and end data)
+
+   symbol->option_3 is used to adjust the rows of the resulting symbol */
+
+#include <assert.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include "common.h"
+#include "pdf417.h"
+#include "pdf417_tabs.h"
+
+/* Modes */
+#define PDF_ALP         1 /* Treating TEX sub-modes as pseudo-modes (minimal encode) */
+#define PDF_LOW         2
+#define PDF_MIX         3
+#define PDF_PNC         4
+#define PDF_TEX         4 /* Real modes */
+#define PDF_BYT         5
+#define PDF_NUM         6
+
+#define PDF_NUM_MODES   6
+
+/* Mode indicators including TEX pseudo-modes */
+static const char pdf_smodes[] = { '?', 'A', 'L', 'M', 'P', 'B', 'N' };
+
+/* Return (real) mode text */
+static const char *pdf_mode_str(const int mode) {
+    static const char modes[3][7] = { "Text", "Byte", "Number" };
+    return mode >= PDF_TEX && mode <= PDF_NUM ? modes[mode - PDF_TEX] : "ERROR";
+}
+
+#define PDF_REAL_MODE(m) ((m) <= PDF_TEX ? PDF_TEX : (m))
+
+/* TEX mode OR-able sub-modes (tables) */
+#define T_ALPHA   1
+#define T_LOWER   2
+#define T_MIXED   4
+#define T_PUNCT   8
+
+#define T_ALWMX   (T_ALPHA | T_LOWER | T_MIXED)
+#define T_MXPNC   (T_MIXED | T_PUNCT)
+
+#define PDF_TABLE_TO_MODE(t) (((t) >> 1) + !!((t) & 0x07)) /* Hack to map 1,2,4,8 to 1,2,3,4 */
+
+/*
+   Three figure numbers in comments give the location of command equivalents in the
+   original Visual Basic source code file pdf417.frm
+   this code retains some original (French) procedure and variable names to ease conversion */
+
+/* text mode processing tables */
+
+/* TEX sub-mode assignments */
+static const char pdf_asciix[256] = {
+          0,       0,       0,       0,       0,       0,       0,       0, /* 00-07 */
+          0, T_MXPNC, T_PUNCT,       0,       0, T_MXPNC,       0,       0, /* 08-0F .<HT><LF>..<CR>.. */
+          0,       0,       0,       0,       0,       0,       0,       0, /* 10-17 */
+          0,       0,       0,       0,       0,       0,       0,       0, /* 18-1F */
+    T_ALWMX, T_PUNCT, T_PUNCT, T_MIXED, T_MXPNC, T_MIXED, T_MIXED, T_PUNCT, /* 20-27 <SP>!"#$%&' */
+    T_PUNCT, T_PUNCT, T_MXPNC, T_MIXED, T_MXPNC, T_MXPNC, T_MXPNC, T_MXPNC, /* 28-2F ()*+,-./ */
+    T_MIXED, T_MIXED, T_MIXED, T_MIXED, T_MIXED, T_MIXED, T_MIXED, T_MIXED, /* 30-37 01234567 */
+    T_MIXED, T_MIXED, T_MXPNC, T_PUNCT, T_PUNCT, T_MIXED, T_PUNCT, T_PUNCT, /* 38-3F 89:;<=>? */
+    T_PUNCT, T_ALPHA, T_ALPHA, T_ALPHA, T_ALPHA, T_ALPHA, T_ALPHA, T_ALPHA, /* 40-47 @ABCDEFG */
+    T_ALPHA, T_ALPHA, T_ALPHA, T_ALPHA, T_ALPHA, T_ALPHA, T_ALPHA, T_ALPHA, /* 48-4F HIJKLMNO */
+    T_ALPHA, T_ALPHA, T_ALPHA, T_ALPHA, T_ALPHA, T_ALPHA, T_ALPHA, T_ALPHA, /* 50-57 PQRSTUVW */
+    T_ALPHA, T_ALPHA, T_ALPHA, T_PUNCT, T_PUNCT, T_PUNCT, T_MIXED, T_PUNCT, /* 58-5F XYZ[\]^_ */
+    T_PUNCT, T_LOWER, T_LOWER, T_LOWER, T_LOWER, T_LOWER, T_LOWER, T_LOWER, /* 60-67 `abcdefg */
+    T_LOWER, T_LOWER, T_LOWER, T_LOWER, T_LOWER, T_LOWER, T_LOWER, T_LOWER, /* 68-6F hijklmno */
+    T_LOWER, T_LOWER, T_LOWER, T_LOWER, T_LOWER, T_LOWER, T_LOWER, T_LOWER, /* 70-77 pqrstuvw */
+    T_LOWER, T_LOWER, T_LOWER, T_PUNCT, T_PUNCT, T_PUNCT, T_PUNCT,       0, /* 78-7E xyz{|}~D */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*80-9F*/
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*A0-BF*/
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*C0-DF*/
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*E0-FF*/
+};
+
+/* TEX sub-mode values */
+static const char pdf_asciiy[127] = {
+     0,  0,  0,  0,  0,  0,  0,  0, /* 00-07 */
+     0, 12, 15,  0,  0, 11,  0,  0, /* 08-0F */
+     0,  0,  0,  0,  0,  0,  0,  0, /* 10-17 */
+     0,  0,  0,  0,  0,  0,  0,  0, /* 18-1F */
+    26, 10, 20, 15, 18, 21, 10, 28, /* 20-27 */
+    23, 24, 22, 20, 13, 16, 17, 19, /* 28-2F */
+     0,  1,  2,  3,  4,  5,  6,  7, /* 30-37 */
+     8,  9, 14,  0,  1, 23,  2, 25, /* 38-3F */
+     3,  0,  1,  2,  3,  4,  5,  6, /* 40-47 */
+     7,  8,  9, 10, 11, 12, 13, 14, /* 48-4F */
+    15, 16, 17, 18, 19, 20, 21, 22, /* 50-57 */
+    23, 24, 25,  4,  5,  6, 24,  7, /* 58-5F */
+     8,  0,  1,  2,  3,  4,  5,  6, /* 60-67 */
+     7,  8,  9, 10, 11, 12, 13, 14, /* 68-6F */
+    15, 16, 17, 18, 19, 20, 21, 22, /* 70-77 */
+    23, 24, 25, 26, 21, 27,  9      /* 78-7F */
+};
+
+/* Automatic sizing table */
+
+static const char pdf_MicroAutosize[56] = {
+    4, 6, 7, 8, 10, 12, 13, 14, 16, 18, 19, 20, 24, 29, 30, 33, 34, 37, 39, 46, 54, 58, 70, 72, 82, 90, 108, 126,
+    1, 14, 2, 7, 3, 25, 8, 16, 5, 17, 9, 6, 10, 11, 28, 12, 19, 13, 29, 20, 30, 21, 22, 31, 23, 32, 33, 34
+};
+
+/* ISO/IEC 15438:2015 5.1.1 c) 3) Max possible number of characters at error correction level 0
+   (Numeric Compaction mode) */
+#define PDF_MAX_LEN         2710
+#define PDF_MAX_LEN_S       "2710" /* String version of above */
+#define PDF_MAX_STREAM_LEN  (PDF_MAX_LEN * 3) /* Allow for tripling up due to shifts/latches (ticket #300 (#7)) */
+
+/* ISO/IEC 24728:2006 5.1.1 c) 3) Max possible number of characters (Numeric Compaction mode) */
+#define MICRO_PDF_MAX_LEN   366
+#define MICRO_PDF_MAX_LEN_S "366" /* String version of above */
+
+/* 866 */
+/* Initial non-compressed categorization of input */
+static int pdf_quelmode(const unsigned char codeascii) {
+    if (z_isdigit(codeascii)) {
+        return PDF_NUM;
+    }
+    if (pdf_asciix[codeascii]) {
+        return PDF_TEX;
+    }
+    /* 876 */
+
+    return PDF_BYT;
+}
+
+/* Helper to switch TEX mode sub-mode */
+static int pdf_textprocess_switch(const int curtable, const int newtable, unsigned char chainet[PDF_MAX_STREAM_LEN],
+            int wnet) {
+    switch (curtable) {
+        case T_ALPHA:
+            switch (newtable) {
+                case T_LOWER: chainet[wnet++] = 27; /* LL */
+                    break;
+                case T_MIXED: chainet[wnet++] = 28; /* ML */
+                    break;
+                case T_PUNCT: chainet[wnet++] = 28; /* ML+PL */
+                    chainet[wnet++] = 25;
+                    break;
+            }
+            break;
+        case T_LOWER:
+            switch (newtable) {
+                case T_ALPHA: chainet[wnet++] = 28; /* ML+AL */
+                    chainet[wnet++] = 28;
+                    break;
+                case T_MIXED: chainet[wnet++] = 28; /* ML */
+                    break;
+                case T_PUNCT: chainet[wnet++] = 28; /* ML+PL */
+                    chainet[wnet++] = 25;
+                    break;
+            }
+            break;
+        case T_MIXED:
+            switch (newtable) {
+                case T_ALPHA: chainet[wnet++] = 28; /* AL */
+                    break;
+                case T_LOWER: chainet[wnet++] = 27; /* LL */
+                    break;
+                case T_PUNCT: chainet[wnet++] = 25; /* PL */
+                    break;
+            }
+            break;
+        case T_PUNCT:
+            switch (newtable) {
+                case T_ALPHA: chainet[wnet++] = 29; /* AL */
+                    break;
+                case T_LOWER: chainet[wnet++] = 29; /* AL+LL */
+                    chainet[wnet++] = 27;
+                    break;
+                case T_MIXED: chainet[wnet++] = 29; /* AL+ML */
+                    chainet[wnet++] = 28;
+                    break;
+            }
+            break;
+    }
+
+    return wnet;
+}
+
+/* Check consecutive segments for text/num and return the length */
+static int pdf_text_num_length(short liste[3][PDF_MAX_LEN], const int indexliste, const int start) {
+    int i, len = 0;
+    for (i = start; i < indexliste; i++) {
+        if (liste[1][i] == PDF_BYT)
+            break;
+
+        len += liste[0][i];
+        if (len >= 5) /* we don't care if it's longer than 5 */
+            break;
+    }
+
+    return len;
+}
+
+/* Calculate length of TEX allowing for sub-mode switches (no-output version of `pdf_textprocess()`) */
+static int pdf_text_submode_length(const unsigned char chaine[], const int start, const int length, int *p_curtable) {
+    int j, indexlistet, curtable = *p_curtable, listet[PDF_MAX_LEN], wnet = 0;
+    unsigned char chainet[PDF_MAX_STREAM_LEN];
+
+    for (indexlistet = 0; indexlistet < length; indexlistet++) {
+        assert(pdf_asciix[chaine[start + indexlistet]]); /* Should only be dealing with TEX */
+        listet[indexlistet] = pdf_asciix[chaine[start + indexlistet]];
+    }
+
+    for (j = 0; j < length; j++) {
+        if (listet[j] & curtable) {
+            /* The character is in the current table */
+            wnet++;
+        } else {
+            /* Obliged to change table */
+            int newtable;
+            if (j == (length - 1) || !(listet[j] & listet[j + 1])) {
+                /* we change only one character - look for temporary switch */
+                if ((listet[j] & T_ALPHA) && (curtable == T_LOWER)) {
+                    wnet += 2; /* AS+char */
+                    continue;
+                }
+                if (listet[j] & T_PUNCT) { /* (T_PUNCT and T_ALPHA not both possible) */
+                    wnet += 2; /* PS+char */
+                    continue;
+                }
+                /* No temporary switch available */
+                newtable = listet[j];
+            } else {
+                newtable = listet[j] & listet[j + 1];
+            }
+
+            /* 599 */
+
+            /* Maintain the first if several tables are possible */
+            if (newtable == T_ALWMX) { /* (T_ALPHA | T_LOWER | T_MIXED) */
+                newtable = T_ALPHA;
+            } else if (newtable == T_MXPNC) { /* (T_MIXED | T_PUNCT) */
+                newtable = T_MIXED;
+            }
+
+            /* 619 - select the switch */
+            wnet = pdf_textprocess_switch(curtable, newtable, chainet, wnet);
+            curtable = newtable;
+            /* 659 - at last we add the character */
+            wnet++;
+        }
+    }
+
+    *p_curtable = curtable;
+
+    return wnet;
+}
+
+/* Whether to stay in numeric mode or not */
+static int pdf_num_stay(const unsigned char *chaine, const int indexliste, short liste[3][PDF_MAX_LEN], const int i) {
+    int curtable, not_tex, last_len, last_ml, next_len, num_cws, tex_cws;
+
+    if (liste[0][i] >= 13 || (indexliste == 1 && liste[0][i] > 5)) {
+        return 1;
+    }
+    if (liste[0][i] < 11) {
+        return 0;
+    }
+
+    curtable = T_ALPHA;
+    not_tex = i == 0 || liste[1][i - 1] == PDF_BYT;
+    last_len = not_tex ? 0 : pdf_text_submode_length(chaine, liste[2][i - 1], liste[0][i - 1], &curtable);
+    last_ml = curtable == T_MIXED;
+
+    curtable = T_ALPHA; /* Next len if after NUM, sub-mode will be alpha */
+    not_tex = i == indexliste - 1 || liste[1][i + 1] == PDF_BYT;
+    next_len = not_tex ? 0 : pdf_text_submode_length(chaine, liste[2][i + 1], liste[0][i + 1], &curtable);
+    num_cws = ((last_len + 1) >> 1) + 1 + 4 + (liste[0][i] > 11) + 1 + ((next_len + 1) >> 1);
+
+    curtable = T_MIXED; /* Next len if stay TEX, sub-mode will be mixed */
+    next_len = not_tex ? 0 : pdf_text_submode_length(chaine, liste[2][i + 1], liste[0][i + 1], &curtable);
+    tex_cws = (last_len + !last_ml + liste[0][i] + next_len + 1) >> 1;
+
+    if (num_cws > tex_cws) {
+        return 0;
+    }
+    return 1;
+}
+
+/* Pack segments using the method described in Appendix D of the AIM specification (ISO/IEC 15438:2015 Annex N) */
+static void pdf_appendix_d_encode(const unsigned char *chaine, short liste[3][PDF_MAX_LEN], int *p_indexliste) {
+    const int indexliste = *p_indexliste;
+    int i = 0, next, last = 0, stayintext = 0;
+
+    while (i < indexliste) {
+
+        if ((liste[1][i] == PDF_NUM) && pdf_num_stay(chaine, indexliste, liste, i)) {
+            /* leave as numeric */
+            liste[0][last] = liste[0][i];
+            liste[1][last] = PDF_NUM;
+            liste[2][last] = liste[2][i];
+            stayintext = 0;
+            last++;
+        } else if (((liste[1][i] == PDF_TEX) || (liste[1][i] == PDF_NUM))
+                && (stayintext || i == indexliste - 1 || (liste[0][i] >= 5)
+                    || (pdf_text_num_length(liste, indexliste, i) >= 5))) {
+            /* set to text and combine additional text/short numeric segments */
+            liste[0][last] = liste[0][i];
+            liste[1][last] = PDF_TEX;
+            liste[2][last] = liste[2][i];
+            stayintext = 0;
+
+            next = i + 1;
+            while (next < indexliste) {
+                if ((liste[1][next] == PDF_NUM) && pdf_num_stay(chaine, indexliste, liste, next)) {
+                    break;
+                } else if (liste[1][next] == PDF_BYT) {
+                    break;
+                }
+
+                liste[0][last] += liste[0][next];
+                next++;
+            }
+
+            last++;
+            i = next;
+            continue;
+        } else {
+            /* build byte segment, including combining numeric/text segments that aren't long enough on their own */
+            liste[0][last] = liste[0][i];
+            liste[1][last] = PDF_BYT;
+            liste[2][last] = liste[2][i];
+            stayintext = 0;
+
+            next = i + 1;
+            while (next < indexliste) {
+                if (liste[1][next] != PDF_BYT) {
+                    /* check for case of a single byte shift from text mode */
+                    if ((liste[0][last] == 1) && (last > 0) && (liste[1][last - 1] == PDF_TEX)) {
+                        stayintext = 1;
+                        break;
+                    }
+
+                    if ((liste[0][next] >= 5) || (pdf_text_num_length(liste, indexliste, next) >= 5)) {
+                        break;
+                    }
+                }
+
+                liste[0][last] += liste[0][next];
+                next++;
+            }
+
+            last++;
+            i = next;
+            continue;
+        }
+
+        i++;
+    }
+
+    /* set the size of the list based on the last consolidated segment */
+    *p_indexliste = last;
+}
+
+/* Helper to pad TEX mode, allowing for whether last segment or not, writing out `chainet` */
+static void pdf_textprocess_end(short *chainemc, int *p_mclength, const int is_last_seg,
+            unsigned char chainet[PDF_MAX_STREAM_LEN], int wnet, int *p_curtable, int *p_tex_padded) {
+    int i;
+
+    *p_tex_padded = wnet & 1;
+    if (*p_tex_padded) {
+        if (is_last_seg) {
+            chainet[wnet++] = 29; /* PS or AL */
+        } else { /* Can't use PS as may carry over to following segment */
+            /* This is sub-optimal if curtable T_ALPHA and following seg lower, or if curtable T_MIXED
+               and following seg is lower or punct; TODO: need peek-ahead to table of following seg */
+            chainet[wnet++] = 28 + (*p_curtable == T_PUNCT); /* ML (T_ALPHA/T_LOWER) or AL (T_MIXED/T_PUNCT) */
+            *p_curtable = *p_curtable == T_ALPHA || *p_curtable == T_LOWER ? T_MIXED : T_ALPHA;
+        }
+    }
+
+    /* Now translate the string chainet into codewords */
+
+    for (i = 0; i < wnet; i += 2) {
+        chainemc[(*p_mclength)++] = (30 * chainet[i]) + chainet[i + 1];
+    }
+}
+
+/* 547 */
+/* Text compaction */
+static void pdf_textprocess(short *chainemc, int *p_mclength, const unsigned char chaine[], const int start,
+            const int length, const int lastmode, const int is_last_seg, int *p_curtable, int *p_tex_padded) {
+    const int real_lastmode = PDF_REAL_MODE(lastmode);
+    int j, indexlistet;
+    int curtable = real_lastmode == PDF_TEX ? *p_curtable : T_ALPHA; /* Set default table upper alpha */
+    int listet[2][PDF_MAX_LEN] = {{0}};
+    unsigned char chainet[PDF_MAX_STREAM_LEN];
+    int wnet = 0;
+
+    /* add mode indicator if needed */
+    if (real_lastmode != PDF_TEX) {
+        chainemc[(*p_mclength)++] = 900;
+    }
+
+    /* listet will contain the table numbers and the value of each characters */
+    for (indexlistet = 0; indexlistet < length; indexlistet++) {
+        const int codeascii = chaine[start + indexlistet];
+        listet[0][indexlistet] = pdf_asciix[codeascii];
+        listet[1][indexlistet] = pdf_asciiy[codeascii];
+    }
+
+    /* 570 */
+
+    for (j = 0; j < length; j++) {
+        if (listet[0][j] & curtable) {
+            /* The character is in the current table */
+            chainet[wnet++] = listet[1][j];
+        } else {
+            /* Obliged to change table */
+            int newtable;
+            if (j == (length - 1) || !(listet[0][j] & listet[0][j + 1])) {
+                /* we change only one character - look for temporary switch */
+                if ((listet[0][j] & T_ALPHA) && (curtable == T_LOWER)) {
+                    chainet[wnet++] = 27; /* AS */
+                    chainet[wnet++] = listet[1][j];
+                    continue;
+                }
+                if (listet[0][j] & T_PUNCT) { /* (T_PUNCT and T_ALPHA not both possible) */
+                    chainet[wnet++] = 29; /* PS */
+                    chainet[wnet++] = listet[1][j];
+                    continue;
+                }
+                /* No temporary switch available */
+                newtable = listet[0][j];
+            } else {
+                newtable = listet[0][j] & listet[0][j + 1];
+            }
+
+            /* 599 */
+
+            /* Maintain the first if several tables are possible */
+            if (newtable == T_ALWMX) { /* (T_ALPHA | T_LOWER | T_MIXED) */
+                newtable = T_ALPHA;
+            } else if (newtable == T_MXPNC) { /* (T_MIXED | T_PUNCT) */
+                newtable = T_MIXED;
+            }
+
+            /* 619 - select the switch */
+            wnet = pdf_textprocess_switch(curtable, newtable, chainet, wnet);
+            curtable = newtable;
+            /* 659 - at last we add the character */
+            chainet[wnet++] = listet[1][j];
+        }
+    }
+
+    /* 663 */
+    *p_curtable = curtable;
+    pdf_textprocess_end(chainemc, p_mclength, is_last_seg, chainet, wnet, p_curtable, p_tex_padded);
+}
+
+/* Minimal text compaction */
+static void pdf_textprocess_minimal(short *chainemc, int *p_mclength, const unsigned char chaine[],
+            short liste[3][PDF_MAX_LEN], const int indexliste, const int lastmode, const int is_last_seg,
+            int *p_curtable, int *p_tex_padded, int *p_i) {
+    const int real_lastmode = PDF_REAL_MODE(lastmode);
+    int i, j, k;
+    int curtable = real_lastmode == PDF_TEX ? *p_curtable : T_ALPHA; /* Set default table upper alpha */
+    unsigned char chainet[PDF_MAX_STREAM_LEN];
+    int wnet = 0;
+
+    /* add mode indicator if needed */
+    if (real_lastmode != PDF_TEX) {
+        chainemc[(*p_mclength)++] = 900;
+    }
+
+    for (i = *p_i; i < indexliste && PDF_REAL_MODE(liste[1][i]) == PDF_TEX; i++) {
+        static const unsigned char newtables[5] = { 0, T_ALPHA, T_LOWER, T_MIXED, T_PUNCT };
+        const int newtable = newtables[liste[1][i]];
+        const int from = liste[2][i];
+        for (j = 0; j < liste[0][i]; j++) {
+            const int c = chaine[from + j];
+            const int t_table = pdf_asciix[c];
+            if (!t_table) { /* BYT Shift? */
+                if (wnet & 1) {
+                    chainet[wnet++] = 29; /* PS or AL (T_PUNCT) */
+                    if (curtable == T_PUNCT) {
+                        curtable = T_ALPHA;
+                    }
+                }
+                for (k = 0; k < wnet; k += 2) {
+                    chainemc[(*p_mclength)++] = (30 * chainet[k]) + chainet[k + 1];
+                }
+                chainemc[(*p_mclength)++] = 913; /* BYT Shift */
+                chainemc[(*p_mclength)++] = c;
+                wnet = 0;
+                continue;
+            }
+
+            if (newtable != curtable) {
+                wnet = pdf_textprocess_switch(curtable, newtable, chainet, wnet);
+                curtable = newtable;
+            }
+            if (curtable == T_LOWER && t_table == T_ALPHA) {
+                chainet[wnet++] = 27; /* AS */
+            } else if (curtable != T_PUNCT && (t_table & T_PUNCT) && (curtable != T_MIXED || !(t_table & T_MIXED))) {
+                chainet[wnet++] = 29; /* PS */
+            }
+            /* At last we add the character */
+            chainet[wnet++] = pdf_asciiy[c];
+        }
+    }
+    *p_i = i ? i - 1 : 0;
+
+    *p_curtable = curtable;
+    pdf_textprocess_end(chainemc, p_mclength, is_last_seg, chainet, wnet, p_curtable, p_tex_padded);
+}
+
+/* 671 */
+/* Byte compaction */
+INTERNAL void pdf_byteprocess(short *chainemc, int *p_mclength, const unsigned char chaine[], int start,
+                const int length, const int lastmode) {
+    const int real_lastmode = PDF_REAL_MODE(lastmode);
+
+    if (length == 1) {
+        /* shift or latch depending on previous mode */
+        chainemc[(*p_mclength)++] = real_lastmode == PDF_TEX ? 913 : 901;
+        chainemc[(*p_mclength)++] = chaine[start];
+    } else {
+        int len;
+        /* select the switch for multiple of 6 bytes */
+        if (length % 6 == 0) {
+            chainemc[(*p_mclength)++] = 924;
+        } else {
+            /* Default mode for MICROPDF417 is Byte Compaction (ISO/IEC 24728:2006 5.4.3), but not emitting it
+             * depends on whether an ECI has been emitted previously (or not) it appears, so simpler and safer
+             * to always emit it. */
+            chainemc[(*p_mclength)++] = 901;
+        }
+
+        len = 0;
+
+        while (len < length) {
+            uint64_t total;
+            unsigned int chunkLen = length - len;
+            if (6 <= chunkLen) { /* Take groups of 6 */
+                chunkLen = 6;
+                len += chunkLen;
+                total = 0;
+
+                while (chunkLen--) {
+                    const uint64_t mantisa = chaine[start++];
+                    total |= mantisa << (chunkLen * 8);
+                }
+
+                chunkLen = 5;
+
+                while (chunkLen--) {
+                    chainemc[*p_mclength + chunkLen] = (int) (total % 900);
+                    total /= 900;
+                }
+                *p_mclength += 5;
+            } else { /* If there remains a group of less than 6 bytes */
+                len += chunkLen;
+                while (chunkLen--) {
+                    chainemc[(*p_mclength)++] = chaine[start++];
+                }
+            }
+        }
+    }
+}
+
+/* 712 */
+/* Numeric compaction */
+static void pdf_numbprocess(short *chainemc, int *p_mclength, const unsigned char chaine[], const int start,
+            const int length) {
+    int j;
+
+    chainemc[(*p_mclength)++] = 902;
+
+    j = 0;
+    while (j < length) {
+        int dumlength = 0;
+        int p, len, loop, nombre, dummy[50];
+        char chainemod[45];
+        int longueur = length - j;
+        if (longueur > 44) {
+            longueur = 44;
+        }
+        len = longueur + 1;
+        chainemod[0] = 1;
+        for (loop = 1; loop < len; loop++) {
+            chainemod[loop] = ctoi(chaine[start + loop + j - 1]);
+        }
+        do {
+            /* 877 - gosub Modulo */
+            p = 0;
+            nombre = 0;
+            for (loop = 0; loop < len; loop++) {
+                nombre *= 10;
+                nombre += chainemod[loop];
+                if (nombre < 900) {
+                    if (p) {
+                        chainemod[p++] = 0;
+                    }
+                } else {
+                    chainemod[p] = (nombre / 900);
+                    nombre -= chainemod[p++] * 900; /* nombre % 900 */
+                }
+            }
+            /* return to 723 */
+
+            dummy[dumlength++] = nombre;
+            len = p;
+        } while (p);
+        for (loop = dumlength - 1; loop >= 0; loop--) {
+            chainemc[(*p_mclength)++] = dummy[loop];
+        }
+        j += longueur;
+    }
+}
+
+#ifdef ZINT_TEST /* Wrapper for direct testing */
+INTERNAL void pdf_numbprocess_test(short *chainemc, int *p_mclength, const unsigned char chaine[], const int start,
+                const int length) {
+    pdf_numbprocess(chainemc, p_mclength, chaine, start, length);
+}
+#endif
+
+/* Minimal encoding */
+
+/* Return number of consecutive chars from `position` in table `t_table` */
+static int pdf_table_length(const unsigned char source[], const int length, const int position, const int t_table) {
+    int i;
+
+    for (i = position; i < length && (pdf_asciix[source[i]] & t_table); i++);
+
+    return i - position;
+}
+
+struct pdf_edge {
+    unsigned char mode;
+    unsigned short from; /* Position in input data, 0-based */
+    unsigned short len;
+    unsigned short units; /* Cumulative TEX/NUM/BYT units since entering TEX/NUM/BYT mode */
+    unsigned short unit_size; /* Number of codewords based on units since entering TEX/NUM/BYT mode */
+    unsigned short size; /* Cumulative number of codewords in previous TEX/NUM/BYT modes */
+    unsigned short previous; /* Index into edges array */
+};
+
+/* Note 1st row of edges not used so valid previous cannot point there, i.e. won't be zero */
+#define PDF_PREVIOUS(edges, edge) \
+    ((edge)->previous ? (edges) + (edge)->previous : NULL)
+
+#if 0
+#define PDF_TRACE
+#endif
+#include "pdf417_trace.h"
+
+/* Initialize a new edge */
+static int pdf_new_Edge(struct pdf_edge *edges, const int mode, const int from, const int len, const int t_table,
+            const int lastmode, struct pdf_edge *previous, struct pdf_edge *edge) {
+    const int real_mode = PDF_REAL_MODE(mode);
+    int previousMode, real_previousMode;
+    int units;
+    int unit_size = 0; /* Suppress clang-tidy clang-analyzer-core.uninitialized.Assign warning */
+    int dv, md;
+
+    edge->mode = mode;
+    edge->from = from;
+    edge->len = len;
+    if (previous) {
+        assert(previous->mode && previous->len && (previous->unit_size + previous->size));
+        previousMode = previous->mode;
+        real_previousMode = PDF_REAL_MODE(previousMode);
+        edge->previous = previous - edges;
+        if (real_mode != real_previousMode) {
+            edge->size = previous->size + previous->unit_size + 1; /* + TEX/NUM/BYT switch */
+            units = 0;
+        } else {
+            edge->size = previous->size;
+            units = previous->units;
+        }
+    } else {
+        previousMode = lastmode;
+        real_previousMode = PDF_REAL_MODE(previousMode);
+        edge->previous = 0;
+        edge->size = real_mode != real_previousMode || real_previousMode != PDF_TEX ? 1 : 0;
+        units = 0;
+    }
+
+    switch (mode) {
+        case PDF_ALP:
+            assert(!t_table || (t_table & (T_ALPHA | T_PUNCT)));
+            if (t_table) {
+                if (previousMode != mode && real_previousMode == PDF_TEX) {
+                    units += 1 + (previousMode == PDF_LOW); /* AL or ML+AL */
+                }
+                units += (1 + !(t_table & T_ALPHA)) * len; /* chars or PS + char */
+            } else { /* Binary shift */
+                assert(len == 1);
+                if (units & 1) {
+                    units++; /* PS or AL pad */
+                }
+                if (previousMode != mode && real_previousMode == PDF_TEX) {
+                    units += 1 + (previousMode == PDF_LOW); /* AL or ML+AL */
+                }
+                units += 4; /* BYT SHIFT 913 (2 units) + byte (2 units) */
+            }
+            unit_size = (units + 1) >> 1;
+            break;
+
+        case PDF_LOW:
+            assert(!t_table || (t_table & (T_LOWER | T_PUNCT | T_ALPHA)));
+            if (t_table) {
+                if (previousMode != mode) {
+                    units += 1 + (previousMode == PDF_PNC); /* LL or AL+LL */
+                }
+                units += (1 + !(t_table & T_LOWER)) * len; /* chars or PS/AS + char */
+            } else { /* Binary shift */
+                assert(len == 1);
+                if (units & 1) {
+                    units++; /* PS or AL pad */
+                }
+                if (previousMode != mode) {
+                    units += 1 + (previousMode == PDF_PNC); /* LL or AL+LL */
+                }
+                units += 4; /* BYT SHIFT 913 (2 units) + byte (2 units) */
+            }
+            unit_size = (units + 1) >> 1;
+            break;
+
+        case PDF_MIX:
+            assert(!t_table || (t_table & (T_MIXED | T_PUNCT)));
+            if (t_table) {
+                if (previousMode != mode) {
+                    units += 1 + (previousMode == PDF_PNC); /* ML or AL+ML */
+                }
+                units += (1 + !(t_table & T_MIXED)) * len; /* chars or PS + char */
+            } else { /* Binary shift */
+                assert(len == 1);
+                if (units & 1) {
+                    units++; /* PS pad */
+                }
+                if (previousMode != mode) {
+                    units += 1 + (previousMode == PDF_PNC); /* ML or AL+ML */
+                }
+                units += 4; /* BYT SHIFT 913 (2 units) + byte (2 units) */
+            }
+            unit_size = (units + 1) >> 1;
+            break;
+
+        case PDF_PNC:
+            assert(!t_table || (t_table & T_PUNCT));
+            if (t_table) {
+                if (previousMode != mode) {
+                    units += 1 + (previousMode != PDF_MIX); /* PL or ML+PL */
+                }
+                units += len; /* chars */
+            } else { /* Binary shift */
+                assert(len == 1);
+                if (units & 1) {
+                    units += 3; /* AL pad + (after BYT SHIFT) ML+PL to return to PNC */
+                } else if (previousMode != mode) {
+                    units += 1 + (previousMode != PDF_MIX); /* PL or ML+PL */
+                }
+                units += 4; /* BYT SHIFT 913 (2 units) + byte (2 units) */
+            }
+            unit_size = (units + 1) >> 1;
+            break;
+
+        case PDF_BYT:
+            units += len;
+            dv = units / 6;
+            unit_size = dv * 5 + (units - dv * 6);
+            break;
+
+        case PDF_NUM:
+            units += len;
+            dv = units / 44;
+            md = units - dv * 44;
+            unit_size = dv * 15 + (md ? md / 3 + 1 : 0);
+            break;
+    }
+    edge->units = units;
+    edge->unit_size = unit_size;
+
+    return edge->size + edge->unit_size;
+}
+
+/* Whether `new_units` likely to result in less (fractional) codewords than `existing_units`, allowing for `mode` */
+static int pdf_new_units_better(const int mode, const int existing_units, const int new_units) {
+    if (PDF_REAL_MODE(mode) == PDF_TEX) {
+        if ((new_units & 1) != (existing_units & 1)) {
+            return (existing_units & 1);
+        }
+    } else if (mode == PDF_BYT) {
+        const int existing_md = existing_units % 6;
+        if (new_units % 6 != existing_md) {
+            return existing_md;
+        }
+    }
+    return new_units < existing_units;
+}
+
+/* Add an edge for a mode at a vertex if no existing edge or if more optimal than existing edge */
+static void pdf_addEdge(const unsigned char *source, const int length, struct pdf_edge *edges, const int mode,
+            const int from, const int len, const int t_table, const int lastmode, struct pdf_edge *previous) {
+    struct pdf_edge edge;
+    const int new_size = pdf_new_Edge(edges, mode, from, len, t_table, lastmode, previous, &edge);
+    const int vertexIndex = from + len;
+    const int v_ij = vertexIndex * PDF_NUM_MODES + mode - 1;
+    const int v_size = edges[v_ij].size + edges[v_ij].unit_size;
+
+    if (edges[v_ij].mode == 0 || v_size > new_size
+            || (v_size == new_size && pdf_new_units_better(mode, edge.units, edges[v_ij].units))) {
+        PDF_TRACE_AddEdge(source, length, edges, previous, vertexIndex, t_table, &edge);
+        edges[v_ij] = edge;
+    } else {
+        PDF_TRACE_NotAddEdge(source, length, edges, previous, vertexIndex, t_table, &edge);
+    }
+}
+
+/* Add edges for the various modes at a vertex */
+static void pdf_addEdges(const unsigned char source[], const int length, const int lastmode, struct pdf_edge *edges,
+            const int from, struct pdf_edge *previous) {
+    const unsigned char c = source[from];
+    const int t_table = pdf_asciix[c];
+
+    if (t_table & T_ALPHA) {
+        const int len = pdf_table_length(source, length, from, T_ALPHA);
+        pdf_addEdge(source, length, edges, PDF_ALP, from, len, T_ALPHA, lastmode, previous);
+    }
+    if (!t_table || (t_table & T_PUNCT)) { /* Binary shift or PS */
+        pdf_addEdge(source, length, edges, PDF_ALP, from, 1 /*len*/, t_table & ~T_ALPHA, lastmode, previous);
+    }
+
+    if (t_table & T_LOWER) {
+        const int len = pdf_table_length(source, length, from, T_LOWER);
+        pdf_addEdge(source, length, edges, PDF_LOW, from, len, T_LOWER, lastmode, previous);
+    }
+    if (!t_table || (t_table & (T_PUNCT | T_ALPHA))) { /* Binary shift or PS/AS */
+        pdf_addEdge(source, length, edges, PDF_LOW, from, 1 /*len*/, t_table & ~T_LOWER, lastmode, previous);
+    }
+
+    if (t_table & T_MIXED) {
+        const int len = pdf_table_length(source, length, from, T_MIXED);
+        pdf_addEdge(source, length, edges, PDF_MIX, from, len, T_MIXED, lastmode, previous);
+        if (len > 1 && z_isdigit(source[from + 1])) { /* Add single-length edge before digit to compare to NUM */
+            pdf_addEdge(source, length, edges, PDF_MIX, from, 1 /*len*/, T_MIXED, lastmode, previous);
+        }
+    }
+    if (!t_table || (t_table & T_PUNCT)) { /* Binary shift or PS */
+        pdf_addEdge(source, length, edges, PDF_MIX, from, 1 /*len*/, t_table & ~T_MIXED, lastmode, previous);
+    }
+
+    if (t_table & T_PUNCT) {
+        const int len = pdf_table_length(source, length, from, T_PUNCT);
+        pdf_addEdge(source, length, edges, PDF_PNC, from, len, T_PUNCT, lastmode, previous);
+    }
+    if (!t_table) { /* Binary shift */
+        pdf_addEdge(source, length, edges, PDF_PNC, from, 1 /*len*/, t_table, lastmode, previous);
+    }
+
+    if (z_isdigit(c)) {
+        const int len = cnt_digits(source, length, from, -1 /*all*/);
+        pdf_addEdge(source, length, edges, PDF_NUM, from, len, 0 /*t_table*/, lastmode, previous);
+    }
+
+    pdf_addEdge(source, length, edges, PDF_BYT, from, 1 /*len*/, 0 /*t_table*/, lastmode, previous);
+}
+
+/* Calculate optimized encoding modes */
+static int pdf_define_mode(short liste[3][PDF_MAX_LEN], int *p_indexliste, const unsigned char source[],
+            const int length, const int lastmode, const int debug_print) {
+
+    int i, j, v_i;
+    int minimalJ, minimalSize;
+    struct pdf_edge *edge;
+    int mode_start, mode_len;
+
+    struct pdf_edge *edges = (struct pdf_edge *) calloc((length + 1) * PDF_NUM_MODES, sizeof(struct pdf_edge));
+    if (!edges) {
+        return 0;
+    }
+    pdf_addEdges(source, length, lastmode, edges, 0, NULL);
+
+    PDF_TRACE_Edges("DEBUG Initial situation\n", source, length, edges, 0);
+
+    for (i = 1; i < length; i++) {
+        v_i = i * PDF_NUM_MODES;
+        for (j = 0; j < PDF_NUM_MODES; j++) {
+            if (edges[v_i + j].mode) {
+                pdf_addEdges(source, length, lastmode, edges, i, edges + v_i + j);
+            }
+        }
+        PDF_TRACE_Edges("DEBUG situation after adding edges to vertices at position %d\n", source, length, edges, i);
+    }
+
+    PDF_TRACE_Edges("DEBUG Final situation\n", source, length, edges, length);
+
+    v_i = length * PDF_NUM_MODES;
+    minimalJ = -1;
+    minimalSize = INT_MAX;
+    for (j = 0; j < PDF_NUM_MODES; j++) {
+        edge = edges + v_i + j;
+        if (edge->mode) {
+            const int edge_size = edge->size + edge->unit_size;
+            if (debug_print) {
+                printf("edges[%d][%d][0] size %d(%d,%d)\n", length, j, edge_size, edge->unit_size, edge->size);
+            }
+            if (edge_size < minimalSize) {
+                minimalSize = edge_size;
+                minimalJ = j;
+                if (debug_print) printf(" set minimalJ %d\n", minimalJ);
+            }
+        } else {
+            if (debug_print) printf("edges[%d][%d][0] NULL\n", length, j);
+        }
+    }
+    assert(minimalJ >= 0);
+
+    edge = edges + v_i + minimalJ;
+    mode_len = 0;
+    mode_start = length;
+    while (edge) {
+        const int current_mode = edge->mode;
+        const int current_from = edge->from;
+        mode_len += edge->len;
+        edge = PDF_PREVIOUS(edges, edge);
+        if (!edge || edge->mode != current_mode) {
+            mode_start--;
+            liste[0][mode_start] = mode_len;
+            liste[1][mode_start] = current_mode;
+            liste[2][mode_start] = current_from;
+            mode_len = 0;
+        }
+    }
+    *p_indexliste = length - mode_start;
+    if (mode_start) {
+        memmove(liste[0], liste[0] + mode_start, sizeof(short) * (*p_indexliste));
+        memmove(liste[1], liste[1] + mode_start, sizeof(short) * (*p_indexliste));
+        memmove(liste[2], liste[2] + mode_start, sizeof(short) * (*p_indexliste));
+    }
+    if (debug_print) {
+        printf("modes (%d):", *p_indexliste);
+        for (i = 0; i < *p_indexliste; i++) printf(" %c(%d,%d)", pdf_smodes[liste[1][i]], liste[2][i], liste[0][i]);
+        fputc('\n', stdout);
+    }
+
+    free(edges);
+
+    return 1;
+}
+
+/* Initial processing of data, shared by `pdf417()` and `micropdf417()` */
+static int pdf_initial(struct zint_symbol *symbol, const unsigned char chaine[], const int length, const int eci,
+            const int is_micro, const int is_last_seg, int *p_lastmode, int *p_curtable, int *p_tex_padded,
+            short chainemc[PDF_MAX_STREAM_LEN], int *p_mclength) {
+    int i, indexchaine = 0, indexliste = 0;
+    short liste[3][PDF_MAX_LEN] = {{0}};
+    int mclength;
+    const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
+    const int fast_encode = symbol->input_mode & FAST_MODE;
+
+    /* 456 */
+
+    if (fast_encode) {
+        int mode = pdf_quelmode(chaine[0]);
+
+        /* 463 */
+        do {
+            liste[1][indexliste] = mode;
+            liste[2][indexliste] = indexchaine;
+            while ((liste[1][indexliste] == mode) && (indexchaine < length)) {
+                liste[0][indexliste]++;
+                indexchaine++;
+                mode = pdf_quelmode(chaine[indexchaine]);
+            }
+            indexliste++;
+        } while (indexchaine < length);
+
+        if (debug_print) {
+            fputs("\nInitial block pattern:\n", stdout);
+            for (i = 0; i < indexliste; i++) {
+                int j;
+                for (j = 0; j < liste[0][i]; j++) fputc(pdf_mode_str(liste[1][i])[0], stdout);
+            }
+            fputc('\n', stdout);
+        }
+
+        pdf_appendix_d_encode(chaine, liste, &indexliste);
+     } else {
+        if (!pdf_define_mode(liste, &indexliste, chaine, length, *p_lastmode, debug_print)) {
+            return errtxt(ZINT_ERROR_MEMORY, symbol, 749, "Insufficient memory for mode buffers");
+        }
+    }
+
+    if (debug_print) {
+        fputs("\nCompacted block pattern:\n", stdout);
+        for (i = 0; i < indexliste; i++) {
+            int j;
+            for (j = 0; j < liste[0][i]; j++) fputc(pdf_mode_str(PDF_REAL_MODE(liste[1][i]))[0], stdout);
+        }
+        fputc('\n', stdout);
+    }
+
+    /* 541 - now compress the data */
+    indexchaine = 0;
+    mclength = *p_mclength;
+    if (mclength == 0 && !is_micro) {
+        mclength++; /* Allow for length descriptor for full symbol */
+    }
+
+    if (*p_mclength == 0 && (symbol->output_options & READER_INIT)) {
+        chainemc[mclength++] = 921; /* Reader Initialisation */
+    }
+
+    if (eci != 0) {
+        if (eci > 811799) {
+            return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 472, "ECI code '%d' out of range (0 to 811799)",
+                            symbol->eci);
+        }
+        /* Encoding ECI assignment number, according to Table 8 */
+        if (eci <= 899) {
+            chainemc[mclength++] = 927; /* ECI */
+            chainemc[mclength++] = eci;
+        } else if (eci <= 810899) {
+            chainemc[mclength++] = 926; /* ECI */
+            chainemc[mclength++] = (eci / 900) - 1;
+            chainemc[mclength++] = eci % 900;
+        } else {
+            chainemc[mclength++] = 925; /* ECI */
+            chainemc[mclength++] = eci - 810900;
+        }
+    }
+
+    for (i = 0; i < indexliste; i++) {
+        const int real_mode = PDF_REAL_MODE(liste[1][i]);
+        switch (real_mode) {
+            case PDF_TEX: /* 547 - text mode */
+                if (fast_encode) {
+                    pdf_textprocess(chainemc, &mclength, chaine, indexchaine, liste[0][i], *p_lastmode, is_last_seg,
+                                    p_curtable, p_tex_padded);
+                    indexchaine += liste[0][i];
+                    *p_lastmode = PDF_ALP;
+                } else {
+                    pdf_textprocess_minimal(chainemc, &mclength, chaine, liste, indexliste, *p_lastmode, is_last_seg,
+                                            p_curtable, p_tex_padded, &i);
+                    indexchaine = i + 1 < indexliste ? liste[2][i + 1] : length;
+                    *p_lastmode = PDF_TABLE_TO_MODE(*p_curtable);
+                }
+                break;
+            case PDF_BYT: /* 670 - octet stream mode */
+                pdf_byteprocess(chainemc, &mclength, chaine, indexchaine, liste[0][i], *p_lastmode);
+                /* don't switch mode on single byte shift from text mode */
+                if (PDF_REAL_MODE(*p_lastmode) != PDF_TEX || liste[0][i] != 1) {
+                    *p_lastmode = PDF_BYT;
+                } else if (*p_curtable == T_PUNCT && *p_tex_padded) { /* If T_PUNCT and padded with AL */
+                    /* Then need to reset to alpha - ISO/IEC 15438:2015 5.4.2.4 b) 2) */
+                    *p_curtable = T_ALPHA;
+                }
+                indexchaine += liste[0][i];
+                break;
+            case PDF_NUM: /* 712 - numeric mode */
+                pdf_numbprocess(chainemc, &mclength, chaine, indexchaine, liste[0][i]);
+                *p_lastmode = PDF_NUM;
+                indexchaine += liste[0][i];
+                break;
+        }
+    }
+
+    *p_mclength = mclength;
+
+    return 0;
+}
+
+/* Call `pdf_initial()` for each segment, dealing with Structured Append beforehand */
+static int pdf_initial_segs(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count,
+            const int is_micro, short chainemc[PDF_MAX_STREAM_LEN], int *p_mclength, int structapp_cws[18],
+            int *p_structapp_cp) {
+    int i;
+    int error_number = 0;
+    int structapp_cp = 0;
+    int lastmode;
+    int curtable;
+    int tex_padded;
+
+    *p_mclength = 0;
+
+    if (symbol->structapp.count) {
+        int id_cnt = 0, ids[10];
+
+        if (symbol->structapp.count < 2 || symbol->structapp.count > 99999) {
+            return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 740,
+                            "Structured Append count '%d' out of range (2 to 99999)", symbol->structapp.count);
+        }
+        if (symbol->structapp.index < 1 || symbol->structapp.index > symbol->structapp.count) {
+            return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 741,
+                            "Structured Append index '%1$d' out of range (1 to count %2$d)",
+                            symbol->structapp.index, symbol->structapp.count);
+        }
+        if (symbol->structapp.id[0]) {
+            int id_len;
+
+            for (id_len = 1; id_len < 31 && symbol->structapp.id[id_len]; id_len++);
+
+            if (id_len > 30) { /* 10 triplets */
+                return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 742,
+                                "Structured Append ID length %d too long (30 digit maximum)", id_len);
+            }
+
+            for (i = 0; i < id_len; i += 3, id_cnt++) {
+                const int triplet_len = i + 3 < id_len ? 3 : id_len - i;
+                ids[id_cnt] = to_int((const unsigned char *) (symbol->structapp.id + i), triplet_len);
+                if (ids[id_cnt] == -1) {
+                    return errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 743,
+                                    "Invalid Structured Append ID (digits only)");
+                }
+                if (ids[id_cnt] > 899) {
+                    return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 744,
+                                    "Structured Append ID triplet %1$d value '%2$03d' out of range (000 to 899)",
+                                    id_cnt + 1, ids[id_cnt]);
+                }
+            }
+        }
+        structapp_cws[structapp_cp++] = 928; /* Macro marker */
+        structapp_cws[structapp_cp++] = (100000 + symbol->structapp.index - 1) / 900; /* Segment index 1 */
+        structapp_cws[structapp_cp++] = (100000 + symbol->structapp.index - 1) % 900; /* Segment index 2 */
+        for (i = 0; i < id_cnt; i++) {
+            structapp_cws[structapp_cp++] = ids[i];
+        }
+        structapp_cws[structapp_cp++] = 923; /* Optional field */
+        structapp_cws[structapp_cp++] = 1; /* Segment count tag */
+        structapp_cws[structapp_cp++] = (100000 + symbol->structapp.count) / 900; /* Segment count 1 */
+        structapp_cws[structapp_cp++] = (100000 + symbol->structapp.count) % 900; /* Segment count 2 */
+        if (symbol->structapp.index == symbol->structapp.count) {
+            structapp_cws[structapp_cp++] = 922; /* Special last segment terminator */
+        }
+    }
+    *p_structapp_cp = structapp_cp;
+
+    /* Default mode for PDF417 is Text Compaction Alpha (ISO/IEC 15438:2015 5.4.2.1), and for MICROPDF417 is Byte
+     * Compaction (ISO/IEC 24728:2006 5.4.3) */
+    lastmode = is_micro ? PDF_BYT : PDF_ALP;
+    /* Start in upper alpha - tracked across calls to `pdf_textprocess()` to allow for interleaving byte shifts */
+    curtable = T_ALPHA;
+
+    for (i = 0; i < seg_count; i++) {
+        error_number = pdf_initial(symbol, segs[i].source, segs[i].length, segs[i].eci, is_micro, i + 1 == seg_count,
+                        &lastmode, &curtable, &tex_padded, chainemc, p_mclength);
+        if (error_number) { /* Only errors >= ZINT_ERROR returned */
+            return error_number;
+        }
+    }
+
+    return error_number;
+}
+
+/* 366 */
+/* Encode PDF417 */
+static int pdf_enc(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count) {
+    int i, j, longueur, loop, mccorrection[520] = {0}, offset;
+    int total, mclength, c1, c2, c3, dummy[35];
+    short chainemc[PDF_MAX_STREAM_LEN];
+    int rows, cols, ecc, ecc_cws, padding;
+    char pattern[580];
+    int bp = 0;
+    int structapp_cws[18] = {0}; /* 3 (Index) + 10 (ID) + 4 (Count) + 1 (Last) */
+    int structapp_cp = 0;
+    int error_number;
+    const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
+    static const short ecc_num_cws[] = { 2, 4, 8, 16, 32, 64, 128, 256, 512 };
+
+    if ((i = segs_length(segs, seg_count)) > PDF_MAX_LEN) {
+        return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 463, "Input length %d too long (maximum " PDF_MAX_LEN_S ")", i);
+    }
+
+    error_number = pdf_initial_segs(symbol, segs, seg_count, 0 /*is_micro*/, chainemc, &mclength, structapp_cws,
+                    &structapp_cp);
+    if (error_number) { /* Only errors return >= ZINT_ERROR */
+        return error_number;
+    }
+
+    if (debug_print) {
+        printf("\nCompressed data stream (%d):\n", mclength - 1);
+        for (i = 1; i < mclength; i++) { /* Skip unset length descriptor */
+            printf("%d ", chainemc[i]);
+        }
+        fputs("\n\n", stdout);
+    }
+
+    /* 752 - Now take care of the number of CWs per row */
+
+    /* ECC */
+    ecc = symbol->option_1;
+    if (ecc < 0) { /* If not specified, set ECC depending on no. of codewords */
+        const int data_cws = mclength - 1 + structapp_cp; /* -1 for length descriptor */
+        /* ISO/IEC 15438:2015 Annex E Table E.1 Recommended minima */
+        if (data_cws <= 40) {
+            ecc = 2;
+        } else if (data_cws <= 160) {
+            ecc = 3;
+        } else if (data_cws <= 320) {
+            ecc = 4;
+        } else if (data_cws <= 863) {
+            ecc = 5;
+        } else {
+            ecc = 6; /* Not mentioned in Table E.1 */
+        }
+    }
+    ecc_cws = ecc_num_cws[ecc];
+
+    longueur = mclength + structapp_cp + ecc_cws;
+
+    if (debug_print) printf("Total No. of Codewords: %d, ECC %d, No. of ECC Codewords: %d\n", longueur, ecc, ecc_cws);
+
+    if (longueur > 928) {
+        /* Enforce maximum codeword limit */
+        return errtxt(ZINT_ERROR_TOO_LONG, symbol, 464, "Input too long, requires too many codewords (maximum 928)");
+    }
+
+    cols = symbol->option_2;
+    rows = symbol->option_3;
+    if (rows) { /* Rows given */
+        if (cols < 1) { /* Cols automatic */
+            cols = (longueur + rows - 1) / rows;
+            if (cols <= 1) {
+                cols = 1;
+            } else {
+                /* Increase rows if would need > 30 columns */
+                for (; cols > 30 && rows < 90; rows++, cols = (longueur + rows - 1) / rows);
+                assert(cols <= 30);
+                /* Increase rows if multiple too big */
+                for (; cols >= 1 && rows < 90 && rows * cols > 928; rows++, cols = (longueur + rows - 1) / rows);
+                if (rows * cols > 928) {
+                    return errtxt(ZINT_ERROR_TOO_LONG, symbol, 465,
+                                    "Input too long, requires too many codewords (maximum 928)");
+                }
+            }
+        } else { /* Cols given */
+            /* Increase rows if multiple too big */
+            for (; rows <= 90 && rows * cols < longueur; rows++);
+            if (rows > 90 || rows * cols > 928) {
+                return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 745, "Input too long for number of columns '%d'", cols);
+            }
+        }
+        if (rows != symbol->option_3) {
+            error_number = errtxtf(ZINT_WARN_INVALID_OPTION, symbol, 746,
+                                    "Number of rows increased from %1$d to %2$d", symbol->option_3, rows);
+        }
+    } else { /* Rows automatic, cols automatic or given */
+        if (cols < 1) { /* Cols automatic */
+            cols = (int) round(sqrt((longueur - 1) / 3.0)); /* -1 (length descriptor) for back-compatibility */
+        }
+        rows = (longueur + cols - 1) / cols;
+        if (rows <= 3) {
+            rows = 3;
+        } else {
+            /* Increase cols if would need > 90 rows - do this even if cols specified for better back-compatibility
+               (though previously only increased once) */
+            for (; rows > 90 && cols < 30; cols++, rows = (longueur + cols - 1) / cols);
+            assert(rows <= 90);
+            /* Increase cols if multiple too big */
+            for (; rows >= 3 && cols < 30 && rows * cols > 928; cols++, rows = (longueur + cols - 1) / cols);
+            if (rows * cols > 928) {
+                return errtxt(ZINT_ERROR_TOO_LONG, symbol, 747,
+                                "Input too long, requires too many codewords (maximum 928)");
+            }
+            if (symbol->option_2 && cols != symbol->option_2) { /* Note previously did not warn if cols auto-upped */
+                error_number = errtxtf(ZINT_WARN_INVALID_OPTION, symbol, 748,
+                                        "Number of columns increased from %1$d to %2$d", symbol->option_2, cols);
+            }
+        }
+    }
+    assert(rows * cols >= longueur);
+
+    /* 781 - Padding calculation */
+    padding = rows * cols - longueur;
+
+    /* We add the padding */
+    for (i = 0; i < padding; i++) {
+        chainemc[mclength++] = 900;
+    }
+    if (debug_print) printf("Padding: %d\n", padding);
+
+    /* We add the Structured Append Macro Control Block if any */
+    if (structapp_cp) {
+        for (i = 0; i < structapp_cp; i++) {
+            chainemc[mclength++] = structapp_cws[i];
+        }
+    }
+
+    /* Set the length descriptor */
+    chainemc[0] = mclength;
+
+    /* 796 - we now take care of the Reed Solomon codes */
+    switch (ecc) {
+        case 1: offset = 2;
+            break;
+        case 2: offset = 6;
+            break;
+        case 3: offset = 14;
+            break;
+        case 4: offset = 30;
+            break;
+        case 5: offset = 62;
+            break;
+        case 6: offset = 126;
+            break;
+        case 7: offset = 254;
+            break;
+        case 8: offset = 510;
+            break;
+        default: offset = 0;
+            break;
+    }
+
+    for (i = 0; i < mclength; i++) {
+        total = (chainemc[i] + mccorrection[ecc_cws - 1]) % 929;
+        for (j = ecc_cws - 1; j > 0; j--) {
+            mccorrection[j] = (mccorrection[j - 1] + 929 - (total * pdf_coefrs[offset + j]) % 929) % 929;
+        }
+        mccorrection[0] = (929 - (total * pdf_coefrs[offset]) % 929) % 929;
+    }
+
+    /* we add these codes to the string */
+    for (i = ecc_cws - 1; i >= 0; i--) {
+        chainemc[mclength++] = mccorrection[i] ? 929 - mccorrection[i] : 0;
+    }
+
+    if (debug_print) {
+        printf("Complete CW string (%d):\n", mclength);
+        for (i = 0; i < mclength; i++) {
+            printf("%d ", chainemc[i]);
+        }
+        fputc('\n', stdout);
+    }
+#ifdef ZINT_TEST
+    if (symbol->debug & ZINT_DEBUG_TEST) {
+        debug_test_codeword_dump_short(symbol, chainemc, mclength);
+    }
+#endif
+
+    if (debug_print) printf("\nSymbol size:\n%d columns x %d rows\n", cols, rows);
+
+    /* 818 - The CW string is finished */
+    c1 = (rows - 1) / 3;
+    c2 = ecc * 3 + (rows - 1) % 3;
+    c3 = cols - 1;
+
+    /* we now encode each row */
+    for (i = 0; i < rows; i++) {
+        const int k = (i / 3) * 30;
+        bp = 0;
+        for (j = 0; j < cols; j++) {
+            dummy[j + 1] = chainemc[i * cols + j];
+        }
+        switch (i % 3) {
+            case 0:
+                dummy[0] = k + c1;
+                dummy[cols + 1] = k + c3;
+                offset = 0; /* cluster(0) */
+                break;
+            case 1:
+                dummy[0] = k + c2;
+                dummy[cols + 1] = k + c1;
+                offset = 929; /* cluster(3) */
+                break;
+            case 2:
+                dummy[0] = k + c3;
+                dummy[cols + 1] = k + c2;
+                offset = 1858; /* cluster(6) */
+                break;
+        }
+        bp = bin_append_posn(0x1FEA8, 17, pattern, bp); /* Row start */
+
+        for (j = 0; j <= cols; j++) {
+            bp = bin_append_posn(pdf_bitpattern[offset + dummy[j]], 16, pattern, bp);
+            pattern[bp++] = '0';
+        }
+
+        if (symbol->symbology != BARCODE_PDF417COMP) {
+            bp = bin_append_posn(pdf_bitpattern[offset + dummy[j]], 16, pattern, bp);
+            pattern[bp++] = '0';
+            bp = bin_append_posn(0x3FA29, 18, pattern, bp); /* Row Stop */
+        } else {
+            pattern[bp++] = '1'; /* Compact PDF417 Stop pattern */
+        }
+
+        for (loop = 0; loop < bp; loop++) {
+            if (pattern[loop] == '1') {
+                set_module(symbol, i, loop);
+            }
+        }
+    }
+    symbol->width = bp;
+    symbol->rows = rows;
+
+    /* ISO/IEC 15438:2015 Section 5.8.2 3X minimum row height */
+    if (error_number) {
+        (void) set_height(symbol, 3.0f, 0.0f, 0.0f, 1 /*no_errtxt*/);
+    } else {
+        error_number = set_height(symbol, 3.0f, 0.0f, 0.0f, 0 /*no_errtxt*/);
+    }
+
+    /* 843 */
+    return error_number;
+}
+
+/* 345 */
+INTERNAL int pdf417(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count) {
+    int codeerr, error_number;
+
+    error_number = 0;
+
+    if ((symbol->option_1 < -1) || (symbol->option_1 > 8)) {
+        errtxtf(0, symbol, 460, "Error correction level '%d' out of range (0 to 8)", symbol->option_1);
+        if (symbol->warn_level == WARN_FAIL_ALL) {
+            return ZINT_ERROR_INVALID_OPTION;
+        }
+        error_number = errtxt_adj(ZINT_WARN_INVALID_OPTION, symbol, "%1$s%2$s", ", ignoring");
+        symbol->option_1 = -1;
+    }
+    if ((symbol->option_2 < 0) || (symbol->option_2 > 30)) {
+        errtxtf(0, symbol, 461, "Number of columns '%d' out of range (1 to 30)", symbol->option_2);
+        if (symbol->warn_level == WARN_FAIL_ALL) {
+            return ZINT_ERROR_INVALID_OPTION;
+        }
+        error_number = errtxt_adj(ZINT_WARN_INVALID_OPTION, symbol, "%1$s%2$s", ", ignoring");
+        symbol->option_2 = 0;
+    }
+    if (symbol->option_3 && (symbol->option_3 < 3 || symbol->option_3 > 90)) {
+        return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 466, "Number of rows '%d' out of range (3 to 90)",
+                        symbol->option_3);
+    }
+    if (symbol->option_2 && symbol->option_3 && symbol->option_2 * symbol->option_3 > 928) {
+        return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 475, "Columns x rows value '%d' out of range (1 to 928)",
+                        symbol->option_2 * symbol->option_3);
+    }
+
+    /* 349 */
+    codeerr = pdf_enc(symbol, segs, seg_count);
+
+    /* 352 */
+    if (codeerr != 0) {
+        error_number = codeerr;
+    }
+
+    /* 364 */
+    return error_number;
+}
+
+/* like PDF417 only much smaller! */
+INTERNAL int micropdf417(struct zint_symbol *symbol, struct zint_seg segs[], const int seg_count) {
+    int i, k, j, longueur, mccorrection[50] = {0}, offset;
+    int total, mclength, error_number = 0;
+    short chainemc[PDF_MAX_STREAM_LEN];
+    char pattern[580];
+    int bp = 0;
+    int structapp_cws[18] = {0}; /* 3 (Index) + 10 (ID) + 4 (Count) + 1 (Last) */
+    int structapp_cp = 0;
+    int variant;
+    int LeftRAP, CentreRAP, RightRAP, Cluster, loop;
+    const int debug_print = symbol->debug & ZINT_DEBUG_PRINT;
+    /* From ISO/IEC 24728:2006 Table 1 — MicroPDF417 version characteristics */
+    static char col_max_codewords[5] = { 0, 20, 37, 82, 126 };
+
+    if ((i = segs_length(segs, seg_count)) > MICRO_PDF_MAX_LEN) {
+        return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 474, "Input length %d too long (maximum " MICRO_PDF_MAX_LEN_S ")",
+                        i);
+    }
+    if (symbol->option_3) {
+        return errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 476, "Cannot specify rows for MicroPDF417");
+    }
+
+    /* Encoding starts out the same as PDF417, so use the same code */
+
+    error_number = pdf_initial_segs(symbol, segs, seg_count, 1 /*is_micro*/, chainemc, &mclength, structapp_cws,
+                    &structapp_cp);
+    if (error_number) { /* Only errors return >= ZINT_ERROR */
+        return error_number;
+    }
+
+    /* This is where it all changes! */
+
+    if (mclength + structapp_cp > 126) {
+        return errtxtf(ZINT_ERROR_TOO_LONG, symbol, 467, "Input too long, requires %d codewords (maximum 126)",
+                        mclength + structapp_cp);
+    }
+    if (symbol->option_2 > 4) {
+        if (symbol->warn_level == WARN_FAIL_ALL) {
+            return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 471, "Number of columns '%d' out of range (1 to 4)",
+                            symbol->option_2);
+        }
+        error_number = errtxtf(ZINT_WARN_INVALID_OPTION, symbol, 468,
+                                "Number of columns '%d' out of range (1 to 4), ignoring", symbol->option_2);
+        symbol->option_2 = 0;
+    }
+
+    if (debug_print) {
+        printf("\nEncoded Data Stream (%d):\n", mclength);
+        for (i = 0; i < mclength; i++) {
+            printf("%3d ", chainemc[i]);
+        }
+        fputc('\n', stdout);
+    }
+
+    /* Now figure out which variant of the symbol to use and load values accordingly */
+
+    variant = 0;
+
+    if (symbol->option_2 >= 1 && mclength + structapp_cp > col_max_codewords[symbol->option_2]) {
+        /* The user specified the column but the data doesn't fit - go to automatic */
+        if (symbol->warn_level == WARN_FAIL_ALL) {
+            return errtxtf(ZINT_ERROR_INVALID_OPTION, symbol, 469,
+                            "Input too long for number of columns '%1$d', requires %2$d codewords (maximum %3$d)",
+                            symbol->option_2, mclength + structapp_cp, col_max_codewords[symbol->option_2]);
+        }
+        error_number = errtxtf(ZINT_WARN_INVALID_OPTION, symbol, 470,
+                                "Input too long for number of columns '%d', ignoring", symbol->option_2);
+        symbol->option_2 = 0;
+    }
+
+    if (symbol->option_2 == 1) {
+        /* the user specified 1 column and the data does fit */
+        if (mclength + structapp_cp <= 4) {
+            variant = 1;
+        } else if (mclength + structapp_cp <= 7) {
+            variant = 2;
+        } else if (mclength + structapp_cp <= 10) {
+            variant = 3;
+        } else if (mclength + structapp_cp <= 12) {
+            variant = 4;
+        } else if (mclength + structapp_cp <= 16) {
+            variant = 5;
+        } else {
+            variant = 6;
+        }
+    } else if (symbol->option_2 == 2) {
+        /* the user specified 2 columns and the data does fit */
+        if (mclength + structapp_cp <= 8) {
+            variant = 7;
+        } else if (mclength + structapp_cp <= 13) {
+            variant = 8;
+        } else if (mclength + structapp_cp <= 19) {
+            variant = 9;
+        } else if (mclength + structapp_cp <= 24) {
+            variant = 10;
+        } else if (mclength + structapp_cp <= 29) {
+            variant = 11;
+        } else if (mclength + structapp_cp <= 33) {
+            variant = 12;
+        } else {
+            variant = 13;
+        }
+    } else if (symbol->option_2 == 3) {
+        /* the user specified 3 columns and the data does fit */
+        if (mclength + structapp_cp <= 6) {
+            variant = 14;
+        } else if (mclength + structapp_cp <= 10) {
+            variant = 15;
+        } else if (mclength + structapp_cp <= 14) {
+            variant = 16;
+        } else if (mclength + structapp_cp <= 18) {
+            variant = 17;
+        } else if (mclength + structapp_cp <= 24) {
+            variant = 18;
+        } else if (mclength + structapp_cp <= 34) {
+            variant = 19;
+        } else if (mclength + structapp_cp <= 46) {
+            variant = 20;
+        } else if (mclength + structapp_cp <= 58) {
+            variant = 21;
+        } else if (mclength + structapp_cp <= 70) {
+            variant = 22;
+        } else {
+            variant = 23;
+        }
+    } else if (symbol->option_2 == 4) {
+        /* the user specified 4 columns and the data does fit */
+        if (mclength + structapp_cp <= 8) {
+            variant = 24;
+        } else if (mclength + structapp_cp <= 12) {
+            variant = 25;
+        } else if (mclength + structapp_cp <= 18) {
+            variant = 26;
+        } else if (mclength + structapp_cp <= 24) {
+            variant = 27;
+        } else if (mclength + structapp_cp <= 30) {
+            variant = 28;
+        } else if (mclength + structapp_cp <= 39) {
+            variant = 29;
+        } else if (mclength + structapp_cp <= 54) {
+            variant = 30;
+        } else if (mclength + structapp_cp <= 72) {
+            variant = 31;
+        } else if (mclength + structapp_cp <= 90) {
+            variant = 32;
+        } else if (mclength + structapp_cp <= 108) {
+            variant = 33;
+        } else {
+            variant = 34;
+        }
+    } else {
+        /* Zint can choose automatically from all available variations */
+        for (i = 27; i >= 0; i--) {
+            /* Note mclength + structapp_cp <= 126 and pdf_MicroAutosize[27] == 126 so variant will be set */
+            if (pdf_MicroAutosize[i] >= mclength + structapp_cp) {
+                variant = pdf_MicroAutosize[i + 28];
+            } else {
+                break;
+            }
+        }
+    }
+    assert(variant > 0); /* Suppress clang-tidy clang-analyzer-core.uninitialized.Assign */
+
+    /* Now we have the variant we can load the data */
+    variant--;
+    symbol->option_2 = pdf_MicroVariants[variant]; /* columns */
+    symbol->rows = pdf_MicroVariants[variant + 34]; /* rows */
+    k = pdf_MicroVariants[variant + 68]; /* number of EC CWs */
+    longueur = (symbol->option_2 * symbol->rows) - k; /* number of non-EC CWs */
+    i = longueur - (mclength + structapp_cp); /* amount of padding required */
+    offset = pdf_MicroVariants[variant + 102]; /* coefficient offset */
+
+    if (debug_print) {
+        fputs("\nChoose symbol size:\n", stdout);
+        printf("%d columns x %d rows, variant %d\n", symbol->option_2, symbol->rows, variant + 1);
+        printf("%d data codewords (including %d pads), %d ecc codewords\n", longueur, i, k);
+        fputc('\n', stdout);
+    }
+
+    /* We add the padding */
+    while (i > 0) {
+        chainemc[mclength++] = 900;
+        i--;
+    }
+
+    /* We add the Structured Append Macro Control Block if any */
+    if (structapp_cp) {
+        for (i = 0; i < structapp_cp; i++) {
+            chainemc[mclength++] = structapp_cws[i];
+        }
+    }
+
+    /* Reed-Solomon error correction */
+    longueur = mclength;
+    for (i = 0; i < longueur; i++) {
+        total = (chainemc[i] + mccorrection[k - 1]) % 929;
+        for (j = k - 1; j >= 0; j--) {
+            if (j == 0) {
+                mccorrection[j] = (929 - (total * pdf_Microcoeffs[offset + j]) % 929) % 929;
+            } else {
+                mccorrection[j] = (mccorrection[j - 1] + 929 - (total * pdf_Microcoeffs[offset + j]) % 929) % 929;
+            }
+        }
+    }
+
+    for (j = 0; j < k; j++) {
+        if (mccorrection[j] != 0) {
+            mccorrection[j] = 929 - mccorrection[j];
+        }
+    }
+    /* we add these codes to the string */
+    for (i = k - 1; i >= 0; i--) {
+        chainemc[mclength++] = mccorrection[i];
+    }
+
+    if (debug_print) {
+        printf("Encoded Data Stream with ECC (%d):\n", mclength);
+        for (i = 0; i < mclength; i++) {
+            printf("%3d ", chainemc[i]);
+        }
+        fputc('\n', stdout);
+    }
+#ifdef ZINT_TEST
+    if (symbol->debug & ZINT_DEBUG_TEST) {
+        debug_test_codeword_dump_short(symbol, chainemc, mclength);
+    }
+#endif
+
+    /* Now get the RAP (Row Address Pattern) start values */
+    LeftRAP = pdf_RAPTable[variant];
+    CentreRAP = pdf_RAPTable[variant + 34];
+    RightRAP = pdf_RAPTable[variant + 68];
+    Cluster = pdf_RAPTable[variant + 102] / 3;
+
+    /* That's all values loaded, get on with the encoding */
+
+    /* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and Cluster(6) */
+
+    if (debug_print) fputs("\nInternal row representation:\n", stdout);
+    for (i = 0; i < symbol->rows; i++) {
+        if (debug_print) printf("row %d: ", i);
+        bp = 0;
+        offset = 929 * Cluster;
+        k = i * symbol->option_2;
+
+        /* Copy the data into codebarre */
+        bp = bin_append_posn(pdf_rap_side[LeftRAP - 1], 10, pattern, bp);
+        bp = bin_append_posn(pdf_bitpattern[offset + chainemc[k]], 16, pattern, bp);
+        pattern[bp++] = '0';
+        if (symbol->option_2 >= 2) {
+            if (symbol->option_2 == 3) {
+                bp = bin_append_posn(pdf_rap_centre[CentreRAP - 1], 10, pattern, bp);
+            }
+            bp = bin_append_posn(pdf_bitpattern[offset + chainemc[k + 1]], 16, pattern, bp);
+            pattern[bp++] = '0';
+            if (symbol->option_2 >= 3) {
+                if (symbol->option_2 == 4) {
+                    bp = bin_append_posn(pdf_rap_centre[CentreRAP - 1], 10, pattern, bp);
+                }
+                bp = bin_append_posn(pdf_bitpattern[offset + chainemc[k + 2]], 16, pattern, bp);
+                pattern[bp++] = '0';
+                if (symbol->option_2 == 4) {
+                    bp = bin_append_posn(pdf_bitpattern[offset + chainemc[k + 3]], 16, pattern, bp);
+                    pattern[bp++] = '0';
+                }
+            }
+        }
+        bp = bin_append_posn(pdf_rap_side[RightRAP - 1], 10, pattern, bp);
+        pattern[bp++] = '1'; /* stop */
+        if (debug_print) printf("%.*s\n", bp, pattern);
+
+        /* so now pattern[] holds the string of '1's and '0's. - copy this to the symbol */
+        for (loop = 0; loop < bp; loop++) {
+            if (pattern[loop] == '1') {
+                set_module(symbol, i, loop);
+            }
+        }
+
+        /* Set up RAPs and Cluster for next row */
+        LeftRAP++;
+        CentreRAP++;
+        RightRAP++;
+        Cluster++;
+
+        if (LeftRAP == 53) {
+            LeftRAP = 1;
+        }
+        if (CentreRAP == 53) {
+            CentreRAP = 1;
+        }
+        if (RightRAP == 53) {
+            RightRAP = 1;
+        }
+        if (Cluster == 3) {
+            Cluster = 0;
+        }
+    }
+    symbol->width = bp;
+
+    /* ISO/IEC 24728:2006 Section 5.8.2 2X minimum row height */
+    if (error_number) {
+        (void) set_height(symbol, 2.0f, 0.0f, 0.0f, 1 /*no_errtxt*/);
+    } else {
+        error_number = set_height(symbol, 2.0f, 0.0f, 0.0f, 0 /*no_errtxt*/);
+    }
+
+    return error_number;
+}
+
+#undef T_ALPHA
+#undef T_LOWER
+#undef T_MIXED
+#undef T_PUNCT
+#undef T_ALWMX
+#undef T_MXPNC
+
+/* vim: set ts=4 sw=4 et : */