2008-07-14 01:15:55 +04:00
/* maxicode.c - Handles Maxicode */
/*
libzint - the open source barcode library
2021-03-28 23:31:45 +03:00
Copyright ( C ) 2010 - 2021 Robin Stuart < rstuart114 @ gmail . com >
2008-07-14 01:15:55 +04:00
2013-05-16 21:26:38 +04:00
Redistribution and use in source and binary forms , with or without
modification , are permitted provided that the following conditions
are met :
2016-02-20 13:50:15 +03:00
1. Redistributions of source code must retain the above copyright
notice , this list of conditions and the following disclaimer .
2013-05-16 21:26:38 +04:00
2. Redistributions in binary form must reproduce the above copyright
notice , this list of conditions and the following disclaimer in the
2016-02-20 13:50:15 +03:00
documentation and / or other materials provided with the distribution .
2013-05-16 21:26:38 +04:00
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
2016-02-20 13:50:15 +03:00
without specific prior written permission .
2013-05-16 21:26:38 +04:00
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
2016-02-20 13:50:15 +03:00
OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
2013-05-16 21:26:38 +04:00
SUCH DAMAGE .
2016-02-20 13:50:15 +03:00
*/
2019-12-19 03:37:55 +03:00
/* vim: set ts=4 sw=4 et : */
2008-07-14 01:15:55 +04:00
2010-05-18 12:33:03 +04:00
/* Includes corrections thanks to Monica Swanson @ Source Technologies */
2020-12-19 20:13:35 +03:00
# include <stdio.h>
# ifdef _MSC_VER
# include <malloc.h>
# endif
2008-07-14 01:15:55 +04:00
# include "common.h"
# include "maxicode.h"
# include "reedsol.h"
2016-02-20 13:50:15 +03:00
/* Handles error correction of primary message */
2020-12-19 20:13:35 +03:00
static void maxi_do_primary_check ( unsigned char maxi_codeword [ 144 ] ) {
2016-02-20 13:50:15 +03:00
unsigned char results [ 15 ] ;
int j ;
int datalen = 10 ;
int ecclen = 10 ;
2020-11-27 15:54:44 +03:00
rs_t rs ;
2010-05-18 12:33:03 +04:00
2020-11-27 15:54:44 +03:00
rs_init_gf ( & rs , 0x43 ) ;
rs_init_code ( & rs , ecclen , 1 ) ;
2010-05-18 12:33:03 +04:00
2020-12-19 20:13:35 +03:00
rs_encode ( & rs , datalen , maxi_codeword , results ) ;
2008-07-14 01:15:55 +04:00
2016-02-20 13:50:15 +03:00
for ( j = 0 ; j < ecclen ; j + = 1 )
maxi_codeword [ datalen + j ] = results [ ecclen - 1 - j ] ;
2008-07-14 01:15:55 +04:00
}
2016-02-20 13:50:15 +03:00
/* Handles error correction of odd characters in secondary */
2020-12-19 20:13:35 +03:00
static void maxi_do_secondary_chk_odd ( unsigned char maxi_codeword [ 144 ] , const int ecclen ) {
2016-02-20 13:50:15 +03:00
unsigned char data [ 100 ] ;
unsigned char results [ 30 ] ;
int j ;
int datalen = 68 ;
2020-11-27 15:54:44 +03:00
rs_t rs ;
2010-05-18 12:33:03 +04:00
2020-11-27 15:54:44 +03:00
rs_init_gf ( & rs , 0x43 ) ;
rs_init_code ( & rs , ecclen , 1 ) ;
2008-07-14 01:15:55 +04:00
2016-02-20 13:50:15 +03:00
if ( ecclen = = 20 )
datalen = 84 ;
2008-07-14 01:15:55 +04:00
2020-12-19 20:13:35 +03:00
for ( j = 1 ; j < datalen ; j + = 2 )
data [ ( j - 1 ) / 2 ] = maxi_codeword [ j + 20 ] ;
2008-07-14 01:15:55 +04:00
2020-11-27 15:54:44 +03:00
rs_encode ( & rs , datalen / 2 , data , results ) ;
2008-07-14 01:15:55 +04:00
2016-02-20 13:50:15 +03:00
for ( j = 0 ; j < ( ecclen ) ; j + = 1 )
maxi_codeword [ datalen + ( 2 * j ) + 1 + 20 ] = results [ ecclen - 1 - j ] ;
2008-07-14 01:15:55 +04:00
}
2016-02-20 13:50:15 +03:00
/* Handles error correction of even characters in secondary */
2020-12-19 20:13:35 +03:00
static void maxi_do_secondary_chk_even ( unsigned char maxi_codeword [ 144 ] , const int ecclen ) {
2016-02-20 13:50:15 +03:00
unsigned char data [ 100 ] ;
unsigned char results [ 30 ] ;
int j ;
int datalen = 68 ;
2020-11-27 15:54:44 +03:00
rs_t rs ;
2008-07-14 01:15:55 +04:00
2016-02-20 13:50:15 +03:00
if ( ecclen = = 20 )
datalen = 84 ;
2008-07-14 01:15:55 +04:00
2020-11-27 15:54:44 +03:00
rs_init_gf ( & rs , 0x43 ) ;
rs_init_code ( & rs , ecclen , 1 ) ;
2010-05-18 12:33:03 +04:00
2020-12-19 20:13:35 +03:00
for ( j = 0 ; j < datalen + 1 ; j + = 2 )
data [ j / 2 ] = maxi_codeword [ j + 20 ] ;
2008-07-14 01:15:55 +04:00
2020-11-27 15:54:44 +03:00
rs_encode ( & rs , datalen / 2 , data , results ) ;
2008-07-14 01:15:55 +04:00
2016-02-20 13:50:15 +03:00
for ( j = 0 ; j < ( ecclen ) ; j + = 1 )
maxi_codeword [ datalen + ( 2 * j ) + 20 ] = results [ ecclen - 1 - j ] ;
2008-07-14 01:15:55 +04:00
}
2016-02-20 13:50:15 +03:00
/* Moves everything up so that a shift or latch can be inserted */
2021-03-28 23:31:45 +03:00
static void maxi_bump ( unsigned char set [ ] , unsigned char character [ ] , const int bump_posn , int * p_length ) {
2010-05-18 12:33:03 +04:00
2021-03-28 23:31:45 +03:00
if ( bump_posn < 143 ) {
memmove ( set + bump_posn + 1 , set + bump_posn , 143 - bump_posn ) ;
memmove ( character + bump_posn + 1 , character + bump_posn , 143 - bump_posn ) ;
}
( * p_length ) + + ; /* Increment length regardless to make sure too long always triggered */
2008-07-14 01:15:55 +04:00
}
2019-08-30 17:40:44 +03:00
/* If the value is present in array, return the value, else return badvalue */
2020-12-19 20:13:35 +03:00
static int value_in_array ( const unsigned char val , const unsigned char arr [ ] , const int badvalue , const int arrLength ) {
2019-08-30 17:40:44 +03:00
int i ;
2020-12-19 20:13:35 +03:00
for ( i = 0 ; i < arrLength ; i + + ) {
if ( arr [ i ] = = val ) return val ;
2019-08-30 17:40:44 +03:00
}
return badvalue ;
}
2020-12-19 20:13:35 +03:00
/* Choose the best set from previous and next set in the range of the setval array, if no value can be found we
* return setval [ 0 ] */
static int bestSurroundingSet ( const int index , const int length , const unsigned char set [ ] , const unsigned char setval [ ] ,
const int setLength ) {
2019-08-30 17:40:44 +03:00
int badValue = - 1 ;
2019-09-01 19:14:21 +03:00
int option1 = value_in_array ( set [ index - 1 ] , setval , badValue , setLength ) ;
2019-08-30 17:40:44 +03:00
if ( index + 1 < length ) {
// we have two options to check (previous & next)
2019-09-01 19:14:21 +03:00
int option2 = value_in_array ( set [ index + 1 ] , setval , badValue , setLength ) ;
2019-08-30 17:40:44 +03:00
if ( option2 ! = badValue & & option1 > option2 ) {
2020-12-19 20:13:35 +03:00
return option2 ;
2019-08-30 17:40:44 +03:00
}
}
2020-12-19 20:13:35 +03:00
2019-08-30 17:40:44 +03:00
if ( option1 ! = badValue ) {
2020-12-19 20:13:35 +03:00
return option1 ;
2019-08-30 17:40:44 +03:00
}
return setval [ 0 ] ;
}
2016-02-20 13:50:15 +03:00
/* Format text according to Appendix A */
2020-12-19 20:13:35 +03:00
static int maxi_text_process ( unsigned char maxi_codeword [ 144 ] , const int mode , const unsigned char in_source [ ] , int length ,
2021-03-28 23:31:45 +03:00
const int eci , const int scm_vv , const int debug_print ) {
2020-12-19 20:13:35 +03:00
unsigned char set [ 144 ] , character [ 144 ] = { 0 } ;
int i , count , current_set , padding_set ;
2016-02-20 13:50:15 +03:00
2020-12-19 20:13:35 +03:00
static const unsigned char set15 [ 2 ] = { 1 , 5 } ;
static const unsigned char set12 [ 2 ] = { 1 , 2 } ;
static const unsigned char set12345 [ 5 ] = { 1 , 2 , 3 , 4 , 5 } ;
2016-02-20 13:50:15 +03:00
2020-12-19 20:13:35 +03:00
const unsigned char * source = in_source ;
# ifndef _MSC_VER
unsigned char source_buf [ length + 9 ] ; /* For prefixing 9-character SCM sequence */
# else
unsigned char * source_buf = ( unsigned char * ) _alloca ( length + 9 ) ;
# endif
2019-08-30 17:40:44 +03:00
2020-12-19 20:13:35 +03:00
if ( length > 144 ) {
2016-02-20 13:50:15 +03:00
return ZINT_ERROR_TOO_LONG ;
}
2020-12-19 20:13:35 +03:00
if ( scm_vv ! = - 1 ) { /* Add SCM prefix */
if ( length > 135 ) {
return ZINT_ERROR_TOO_LONG ;
}
sprintf ( ( char * ) source_buf , " [)> \036 01 \035 %02d " , scm_vv ) ; /* [)>\R01\Gvv */
memcpy ( source_buf + 9 , in_source , length ) ;
source = source_buf ;
length + = 9 ;
2016-02-20 13:50:15 +03:00
}
2020-12-19 20:13:35 +03:00
memset ( set , 255 , 144 ) ;
2016-02-20 13:50:15 +03:00
for ( i = 0 ; i < length ; i + + ) {
/* Look up characters in table from Appendix A - this gives
value and code set for most characters */
set [ i ] = maxiCodeSet [ source [ i ] ] ;
character [ i ] = maxiSymbolChar [ source [ i ] ] ;
}
/* If a character can be represented in more than one code set,
pick which version to use */
if ( set [ 0 ] = = 0 ) {
if ( character [ 0 ] = = 13 ) {
character [ 0 ] = 0 ;
}
set [ 0 ] = 1 ;
}
for ( i = 1 ; i < length ; i + + ) {
if ( set [ i ] = = 0 ) {
/* Special character */
if ( character [ i ] = = 13 ) {
/* Carriage Return */
2019-09-01 19:14:21 +03:00
set [ i ] = bestSurroundingSet ( i , length , set , set15 , 2 ) ;
2019-08-30 17:40:44 +03:00
if ( set [ i ] = = 5 ) {
2016-02-20 13:50:15 +03:00
character [ i ] = 13 ;
} else {
2019-08-30 17:40:44 +03:00
character [ i ] = 0 ;
2016-02-20 13:50:15 +03:00
}
2020-12-19 20:13:35 +03:00
} else if ( character [ i ] = = 28 ) {
2016-02-20 13:50:15 +03:00
/* FS */
2019-09-01 19:14:21 +03:00
set [ i ] = bestSurroundingSet ( i , length , set , set12345 , 5 ) ;
2019-08-30 17:40:44 +03:00
if ( set [ i ] = = 5 ) {
2021-03-28 23:31:45 +03:00
character [ i ] = 32 ;
2016-02-20 13:50:15 +03:00
}
2020-12-19 20:13:35 +03:00
} else if ( character [ i ] = = 29 ) {
2016-02-20 13:50:15 +03:00
/* GS */
2019-09-01 19:14:21 +03:00
set [ i ] = bestSurroundingSet ( i , length , set , set12345 , 5 ) ;
2019-08-30 17:40:44 +03:00
if ( set [ i ] = = 5 ) {
2016-02-20 13:50:15 +03:00
character [ i ] = 33 ;
2021-03-28 23:31:45 +03:00
}
2016-02-20 13:50:15 +03:00
2020-12-19 20:13:35 +03:00
} else if ( character [ i ] = = 30 ) {
2016-02-20 13:50:15 +03:00
/* RS */
2021-03-28 23:31:45 +03:00
set [ i ] = bestSurroundingSet ( i , length , set , set12345 , 5 ) ;
2019-08-30 17:40:44 +03:00
if ( set [ i ] = = 5 ) {
2016-02-20 13:50:15 +03:00
character [ i ] = 34 ;
}
2020-12-19 20:13:35 +03:00
} else if ( character [ i ] = = 32 ) {
2016-02-20 13:50:15 +03:00
/* Space */
2019-09-01 19:14:21 +03:00
set [ i ] = bestSurroundingSet ( i , length , set , set12345 , 5 ) ;
2019-08-30 17:40:44 +03:00
if ( set [ i ] = = 1 ) {
2016-02-20 13:50:15 +03:00
character [ i ] = 32 ;
2019-08-30 17:40:44 +03:00
} else if ( set [ i ] = = 2 ) {
2016-02-20 13:50:15 +03:00
character [ i ] = 47 ;
2019-08-30 17:40:44 +03:00
} else {
character [ i ] = 59 ;
2016-02-20 13:50:15 +03:00
}
2020-12-19 20:13:35 +03:00
} else if ( character [ i ] = = 44 ) {
2016-02-20 13:50:15 +03:00
/* Comma */
2019-09-01 19:14:21 +03:00
set [ i ] = bestSurroundingSet ( i , length , set , set12 , 2 ) ;
2019-08-30 17:40:44 +03:00
if ( set [ i ] = = 2 ) {
2016-02-20 13:50:15 +03:00
character [ i ] = 48 ;
}
2020-12-19 20:13:35 +03:00
} else if ( character [ i ] = = 46 ) {
2016-02-20 13:50:15 +03:00
/* Full Stop */
2019-09-01 19:14:21 +03:00
set [ i ] = bestSurroundingSet ( i , length , set , set12 , 2 ) ;
2019-08-30 17:40:44 +03:00
if ( set [ i ] = = 2 ) {
2016-02-20 13:50:15 +03:00
character [ i ] = 49 ;
2021-03-28 23:31:45 +03:00
}
2016-02-20 13:50:15 +03:00
2020-12-19 20:13:35 +03:00
} else if ( character [ i ] = = 47 ) {
2016-02-20 13:50:15 +03:00
/* Slash */
2019-09-01 19:14:21 +03:00
set [ i ] = bestSurroundingSet ( i , length , set , set12 , 2 ) ;
2019-08-30 17:40:44 +03:00
if ( set [ i ] = = 2 ) {
2016-02-20 13:50:15 +03:00
character [ i ] = 50 ;
}
2020-12-19 20:13:35 +03:00
} else if ( character [ i ] = = 58 ) {
2016-02-20 13:50:15 +03:00
/* Colon */
2019-09-01 19:14:21 +03:00
set [ i ] = bestSurroundingSet ( i , length , set , set12 , 2 ) ;
2019-08-30 17:40:44 +03:00
if ( set [ i ] = = 2 ) {
2016-02-20 13:50:15 +03:00
character [ i ] = 51 ;
}
}
}
}
2020-12-19 20:13:35 +03:00
padding_set = set [ length - 1 ] = = 2 ? 2 : 1 ;
2016-02-20 13:50:15 +03:00
for ( i = length ; i < 144 ; i + + ) {
/* Add the padding */
2020-12-19 20:13:35 +03:00
set [ i ] = padding_set ;
2016-02-20 13:50:15 +03:00
character [ i ] = 33 ;
}
/* Find candidates for number compression */
2020-12-19 20:13:35 +03:00
/* Note the prohibition on number compression in the primary message in ISO/IEC 16023:2000 B.1 (1)
applies to modes 2 & 3 only */
2016-02-20 13:50:15 +03:00
count = 0 ;
2020-12-19 20:13:35 +03:00
for ( i = 0 ; i < 144 ; i + + ) {
2016-02-20 13:50:15 +03:00
if ( ( set [ i ] = = 1 ) & & ( ( character [ i ] > = 48 ) & & ( character [ i ] < = 57 ) ) ) {
/* Character is a number */
count + + ;
2020-12-19 20:13:35 +03:00
if ( count = = 9 ) {
/* Nine digits in a row can be compressed */
2021-03-28 23:31:45 +03:00
memset ( set + i - 8 , 6 , 9 ) ; /* Set set of nine digits to 6 */
2020-12-19 20:13:35 +03:00
count = 0 ;
}
2016-02-20 13:50:15 +03:00
} else {
count = 0 ;
}
}
/* Add shift and latch characters */
current_set = 1 ;
i = 0 ;
do {
if ( ( set [ i ] ! = current_set ) & & ( set [ i ] ! = 6 ) ) {
switch ( set [ i ] ) {
case 1 :
2021-03-28 23:31:45 +03:00
if ( current_set = = 2 ) { /* Set B */
if ( i + 1 < 144 & & set [ i + 1 ] = = 1 ) {
if ( i + 2 < 144 & & set [ i + 2 ] = = 1 ) {
if ( i + 3 < 144 & & set [ i + 3 ] = = 1 ) {
/* Latch A */
maxi_bump ( set , character , i , & length ) ;
character [ i ] = 63 ; /* Set B Latch A */
current_set = 1 ;
i + = 3 ; /* Next 3 Set A so skip over */
if ( debug_print ) printf ( " LCHA " ) ;
} else {
/* 3 Shift A */
maxi_bump ( set , character , i , & length ) ;
character [ i ] = 57 ; /* Set B triple shift A */
i + = 2 ; /* Next 2 Set A so skip over */
if ( debug_print ) printf ( " 3SHA " ) ;
}
2016-02-20 13:50:15 +03:00
} else {
2021-03-28 23:31:45 +03:00
/* 2 Shift A */
maxi_bump ( set , character , i , & length ) ;
character [ i ] = 56 ; /* Set B double shift A */
i + + ; /* Next Set A so skip over */
if ( debug_print ) printf ( " 2SHA " ) ;
2016-02-20 13:50:15 +03:00
}
} else {
2021-03-28 23:31:45 +03:00
/* Shift A */
maxi_bump ( set , character , i , & length ) ;
character [ i ] = 59 ; /* Set A Shift B */
if ( debug_print ) printf ( " SHA " ) ;
2016-02-20 13:50:15 +03:00
}
2021-03-28 23:31:45 +03:00
} else { /* All sets other than B only have latch */
/* Latch A */
maxi_bump ( set , character , i , & length ) ;
character [ i ] = 58 ; /* Sets C,D,E Latch A */
current_set = 1 ;
if ( debug_print ) printf ( " LCHA " ) ;
2016-02-20 13:50:15 +03:00
}
break ;
2021-03-28 23:31:45 +03:00
case 2 : /* Set B */
if ( current_set ! = 1 | | ( i + 1 < 144 & & set [ i + 1 ] = = 2 ) ) { /* If not Set A or next Set B */
2016-02-20 13:50:15 +03:00
/* Latch B */
2021-03-28 23:31:45 +03:00
maxi_bump ( set , character , i , & length ) ;
character [ i ] = 63 ; /* Sets A,C,D,E Latch B */
2016-02-20 13:50:15 +03:00
current_set = 2 ;
2021-03-28 23:31:45 +03:00
if ( debug_print ) printf ( " LCHB " ) ;
} else { /* Only available from Set A */
2016-02-20 13:50:15 +03:00
/* Shift B */
2021-03-28 23:31:45 +03:00
maxi_bump ( set , character , i , & length ) ;
character [ i ] = 59 ; /* Set B Shift A */
if ( debug_print ) printf ( " SHB " ) ;
2019-08-30 17:40:44 +03:00
}
2016-02-20 13:50:15 +03:00
break ;
2021-03-28 23:31:45 +03:00
case 3 : /* Set C */
case 4 : /* Set D */
case 5 : /* Set E */
/* If first and next 3 same set, or not first and previous and next 2 same set */
if ( ( i = = 0 & & i + 3 < 144 & & set [ i + 1 ] = = set [ i ] & & set [ i + 2 ] = = set [ i ]
& & set [ i + 3 ] = = set [ i ] )
| | ( i > 0 & & set [ i - 1 ] = = set [ i ] & & i + 2 < 144 & & set [ i + 1 ] = = set [ i ]
& & set [ i + 2 ] = = set [ i ] ) ) {
/* Lock in C/D/E */
if ( i = = 0 ) {
maxi_bump ( set , character , i , & length ) ;
character [ i ] = 60 + set [ i ] - 3 ;
i + + ; /* Extra bump */
maxi_bump ( set , character , i , & length ) ;
character [ i ] = 60 + set [ i ] - 3 ;
i + = 3 ; /* Next 3 same set so skip over */
} else {
/* Add single Shift to previous Shift */
maxi_bump ( set , character , i - 1 , & length ) ;
character [ i - 1 ] = 60 + set [ i ] - 3 ;
i + = 2 ; /* Next 2 same set so skip over */
}
current_set = set [ i ] ;
if ( debug_print ) printf ( " LCK%c " , ' C ' + set [ i ] - 3 ) ;
2019-08-30 17:40:44 +03:00
} else {
2021-03-28 23:31:45 +03:00
/* Shift C/D/E */
maxi_bump ( set , character , i , & length ) ;
character [ i ] = 60 + set [ i ] - 3 ;
if ( debug_print ) printf ( " SH%c " , ' C ' + set [ i ] - 3 ) ;
2020-12-19 20:13:35 +03:00
}
2016-02-20 13:50:15 +03:00
break ;
}
2021-03-28 23:31:45 +03:00
i + + ; /* Allow for bump */
2016-02-20 13:50:15 +03:00
}
i + + ;
2017-01-25 22:06:33 +03:00
} while ( i < 144 ) ;
2016-02-20 13:50:15 +03:00
2021-03-28 23:31:45 +03:00
if ( debug_print ) printf ( " \n " ) ;
2016-02-20 13:50:15 +03:00
/* Number compression has not been forgotten! - It's handled below */
i = 0 ;
do {
if ( set [ i ] = = 6 ) {
/* Number compression */
2020-12-19 20:13:35 +03:00
int value = to_int ( character + i , 9 ) ;
2016-02-20 13:50:15 +03:00
character [ i ] = 31 ; /* NS */
character [ i + 1 ] = ( value & 0x3f000000 ) > > 24 ;
character [ i + 2 ] = ( value & 0xfc0000 ) > > 18 ;
character [ i + 3 ] = ( value & 0x3f000 ) > > 12 ;
character [ i + 4 ] = ( value & 0xfc0 ) > > 6 ;
character [ i + 5 ] = ( value & 0x3f ) ;
i + = 6 ;
2020-12-19 20:13:35 +03:00
memmove ( set + i , set + i + 3 , 141 - i ) ;
memmove ( character + i , character + i + 3 , 141 - i ) ;
2016-02-20 13:50:15 +03:00
length - = 3 ;
} else {
i + + ;
}
2020-03-28 05:50:15 +03:00
} while ( i < = 135 ) ; /* 144 - 9 */
2016-02-20 13:50:15 +03:00
2016-08-16 14:43:41 +03:00
/* Insert ECI at the beginning of message if needed */
2017-06-18 15:00:22 +03:00
/* Encode ECI assignment numbers according to table 3 */
2019-10-06 19:39:54 +03:00
if ( eci ! = 0 ) {
2021-03-28 23:31:45 +03:00
maxi_bump ( set , character , 0 , & length ) ;
2016-08-16 14:43:41 +03:00
character [ 0 ] = 27 ; // ECI
2017-06-18 15:00:22 +03:00
if ( eci < = 31 ) {
2021-03-28 23:31:45 +03:00
maxi_bump ( set , character , 1 , & length ) ;
2017-06-18 15:00:22 +03:00
character [ 1 ] = eci ;
2020-12-19 20:13:35 +03:00
} else if ( eci < = 1023 ) {
2021-03-28 23:31:45 +03:00
maxi_bump ( set , character , 1 , & length ) ;
maxi_bump ( set , character , 1 , & length ) ;
2021-03-18 19:21:22 +03:00
character [ 1 ] = 0x20 | ( ( eci > > 6 ) & 0x0F ) ;
2017-06-18 15:00:22 +03:00
character [ 2 ] = eci & 0x3F ;
2020-12-19 20:13:35 +03:00
} else if ( eci < = 32767 ) {
2021-03-28 23:31:45 +03:00
maxi_bump ( set , character , 1 , & length ) ;
maxi_bump ( set , character , 1 , & length ) ;
maxi_bump ( set , character , 1 , & length ) ;
2021-03-18 19:21:22 +03:00
character [ 1 ] = 0x30 | ( ( eci > > 12 ) & 0x07 ) ;
2017-06-18 15:00:22 +03:00
character [ 2 ] = ( eci > > 6 ) & 0x3F ;
character [ 3 ] = eci & 0x3F ;
2020-12-19 20:13:35 +03:00
} else {
2021-03-28 23:31:45 +03:00
maxi_bump ( set , character , 1 , & length ) ;
maxi_bump ( set , character , 1 , & length ) ;
maxi_bump ( set , character , 1 , & length ) ;
maxi_bump ( set , character , 1 , & length ) ;
2021-03-18 19:21:22 +03:00
character [ 1 ] = 0x38 | ( ( eci > > 18 ) & 0x03 ) ;
2017-06-18 15:00:22 +03:00
character [ 2 ] = ( eci > > 12 ) & 0x3F ;
character [ 3 ] = ( eci > > 6 ) & 0x3F ;
character [ 4 ] = eci & 0x3F ;
}
2016-08-16 14:43:41 +03:00
}
2017-10-23 22:37:52 +03:00
2021-03-28 23:31:45 +03:00
if ( debug_print ) printf ( " Length: %d \n " , length ) ;
2016-02-20 13:50:15 +03:00
if ( ( ( mode = = 2 ) | | ( mode = = 3 ) ) & & ( length > 84 ) ) {
return ZINT_ERROR_TOO_LONG ;
2020-12-19 20:13:35 +03:00
} else if ( ( ( mode = = 4 ) | | ( mode = = 6 ) ) & & ( length > 93 ) ) {
2016-02-20 13:50:15 +03:00
return ZINT_ERROR_TOO_LONG ;
2020-12-19 20:13:35 +03:00
} else if ( ( mode = = 5 ) & & ( length > 77 ) ) {
2016-02-20 13:50:15 +03:00
return ZINT_ERROR_TOO_LONG ;
}
/* Copy the encoded text into the codeword array */
if ( ( mode = = 2 ) | | ( mode = = 3 ) ) {
for ( i = 0 ; i < 84 ; i + + ) { /* secondary only */
maxi_codeword [ i + 20 ] = character [ i ] ;
}
2020-12-19 20:13:35 +03:00
} else if ( ( mode = = 4 ) | | ( mode = = 6 ) ) {
2016-02-20 13:50:15 +03:00
for ( i = 0 ; i < 9 ; i + + ) { /* primary */
maxi_codeword [ i + 1 ] = character [ i ] ;
}
for ( i = 0 ; i < 84 ; i + + ) { /* secondary */
maxi_codeword [ i + 20 ] = character [ i + 9 ] ;
}
2020-12-19 20:13:35 +03:00
} else { /* Mode 5 */
2016-02-20 13:50:15 +03:00
for ( i = 0 ; i < 9 ; i + + ) { /* primary */
maxi_codeword [ i + 1 ] = character [ i ] ;
}
for ( i = 0 ; i < 68 ; i + + ) { /* secondary */
maxi_codeword [ i + 20 ] = character [ i + 9 ] ;
}
}
return 0 ;
2008-07-14 01:15:55 +04:00
}
2016-02-20 13:50:15 +03:00
/* Format structured primary for Mode 2 */
2020-12-19 20:13:35 +03:00
static void maxi_do_primary_2 ( unsigned char maxi_codeword [ 144 ] , const unsigned char postcode [ ] , const int postcode_length ,
const int country , const int service ) {
int postcode_num ;
2016-02-20 13:50:15 +03:00
2020-12-19 20:13:35 +03:00
postcode_num = atoi ( ( const char * ) postcode ) ;
2016-02-20 13:50:15 +03:00
maxi_codeword [ 0 ] = ( ( postcode_num & 0x03 ) < < 4 ) | 2 ;
maxi_codeword [ 1 ] = ( ( postcode_num & 0xfc ) > > 2 ) ;
maxi_codeword [ 2 ] = ( ( postcode_num & 0x3f00 ) > > 8 ) ;
maxi_codeword [ 3 ] = ( ( postcode_num & 0xfc000 ) > > 14 ) ;
maxi_codeword [ 4 ] = ( ( postcode_num & 0x3f00000 ) > > 20 ) ;
maxi_codeword [ 5 ] = ( ( postcode_num & 0x3c000000 ) > > 26 ) | ( ( postcode_length & 0x3 ) < < 4 ) ;
maxi_codeword [ 6 ] = ( ( postcode_length & 0x3c ) > > 2 ) | ( ( country & 0x3 ) < < 4 ) ;
maxi_codeword [ 7 ] = ( country & 0xfc ) > > 2 ;
maxi_codeword [ 8 ] = ( ( country & 0x300 ) > > 8 ) | ( ( service & 0xf ) < < 2 ) ;
maxi_codeword [ 9 ] = ( ( service & 0x3f0 ) > > 4 ) ;
2008-07-14 01:15:55 +04:00
}
2016-02-20 13:50:15 +03:00
/* Format structured primary for Mode 3 */
2020-12-19 20:13:35 +03:00
static void maxi_do_primary_3 ( unsigned char maxi_codeword [ 144 ] , unsigned char postcode [ ] , const int country ,
const int service ) {
int i ;
/* Convert to Code Set A */
for ( i = 0 ; i < 6 ; i + + ) {
postcode [ i ] = maxiSymbolChar [ postcode [ i ] ] ;
2016-02-20 13:50:15 +03:00
}
maxi_codeword [ 0 ] = ( ( postcode [ 5 ] & 0x03 ) < < 4 ) | 3 ;
maxi_codeword [ 1 ] = ( ( postcode [ 4 ] & 0x03 ) < < 4 ) | ( ( postcode [ 5 ] & 0x3c ) > > 2 ) ;
maxi_codeword [ 2 ] = ( ( postcode [ 3 ] & 0x03 ) < < 4 ) | ( ( postcode [ 4 ] & 0x3c ) > > 2 ) ;
maxi_codeword [ 3 ] = ( ( postcode [ 2 ] & 0x03 ) < < 4 ) | ( ( postcode [ 3 ] & 0x3c ) > > 2 ) ;
maxi_codeword [ 4 ] = ( ( postcode [ 1 ] & 0x03 ) < < 4 ) | ( ( postcode [ 2 ] & 0x3c ) > > 2 ) ;
maxi_codeword [ 5 ] = ( ( postcode [ 0 ] & 0x03 ) < < 4 ) | ( ( postcode [ 1 ] & 0x3c ) > > 2 ) ;
maxi_codeword [ 6 ] = ( ( postcode [ 0 ] & 0x3c ) > > 2 ) | ( ( country & 0x3 ) < < 4 ) ;
maxi_codeword [ 7 ] = ( country & 0xfc ) > > 2 ;
maxi_codeword [ 8 ] = ( ( country & 0x300 ) > > 8 ) | ( ( service & 0xf ) < < 2 ) ;
maxi_codeword [ 9 ] = ( ( service & 0x3f0 ) > > 4 ) ;
2008-07-14 01:15:55 +04:00
}
2020-12-19 20:13:35 +03:00
INTERNAL int maxicode ( struct zint_symbol * symbol , unsigned char source [ ] , int length ) {
int i , j , block , shift , mode , lp = 0 ;
int error_number = 0 , eclen ;
unsigned char maxi_codeword [ 144 ] = { 0 } ;
int scm_vv = - 1 ;
2010-05-18 12:33:03 +04:00
2016-02-20 13:50:15 +03:00
mode = symbol - > option_1 ;
2020-12-19 20:13:35 +03:00
if ( mode < = 0 ) { /* If mode is unspecified (-1) or to be auto-determined (0) between 2 and 3 */
lp = ( int ) strlen ( symbol - > primary ) ;
2016-02-20 13:50:15 +03:00
if ( lp = = 0 ) {
2020-12-19 20:13:35 +03:00
if ( mode = = 0 ) { /* Require primary message to auto-determine between 2 and 3 */
strcpy ( symbol - > errtxt , " 554: Primary Message empty " ) ;
return ZINT_ERROR_INVALID_DATA ;
}
2016-02-20 13:50:15 +03:00
mode = 4 ;
} else {
mode = 2 ;
2020-12-19 20:13:35 +03:00
for ( i = 0 ; i < lp - 6 ; i + + ) {
if ( ( ( symbol - > primary [ i ] < ' 0 ' ) | | ( symbol - > primary [ i ] > ' 9 ' ) ) & & ( symbol - > primary [ i ] ! = ' ' ) ) {
2016-02-20 13:50:15 +03:00
mode = 3 ;
break ;
}
}
}
}
if ( ( mode < 2 ) | | ( mode > 6 ) ) { /* Only codes 2 to 6 supported */
2020-12-19 20:13:35 +03:00
strcpy ( symbol - > errtxt , " 550: Invalid MaxiCode Mode " ) ;
2016-02-20 13:50:15 +03:00
return ZINT_ERROR_INVALID_OPTION ;
}
if ( ( mode = = 2 ) | | ( mode = = 3 ) ) { /* Modes 2 and 3 need data in symbol->primary */
2020-12-19 20:13:35 +03:00
unsigned char postcode [ 10 ] ;
2017-10-23 22:34:31 +03:00
int countrycode ;
int service ;
2020-12-19 20:13:35 +03:00
int postcode_len ;
2016-02-20 13:50:15 +03:00
if ( lp = = 0 ) { /* Mode set manually means lp doesn't get set */
2020-12-19 20:13:35 +03:00
lp = ( int ) strlen ( symbol - > primary ) ;
2016-02-20 13:50:15 +03:00
}
2020-12-19 20:13:35 +03:00
if ( lp < 7 | | lp > 15 ) { /* 1 to 9 character postcode + 3 digit country code + 3 digit service class */
strcpy ( symbol - > errtxt , " 551: Invalid length for Primary Message " ) ;
2016-02-20 13:50:15 +03:00
return ZINT_ERROR_INVALID_DATA ;
}
2020-12-19 20:13:35 +03:00
postcode_len = lp - 6 ;
2016-02-20 13:50:15 +03:00
2020-12-19 20:13:35 +03:00
countrycode = to_int ( ( const unsigned char * ) ( symbol - > primary + postcode_len ) , 3 ) ;
service = to_int ( ( const unsigned char * ) ( symbol - > primary + postcode_len + 3 ) , 3 ) ;
if ( countrycode = = - 1 | | service = = - 1 ) { /* check that country code and service are numeric */
strcpy ( symbol - > errtxt , " 552: Non-numeric country code or service class in Primary Message " ) ;
return ZINT_ERROR_INVALID_DATA ;
2016-02-20 13:50:15 +03:00
}
2020-12-19 20:13:35 +03:00
memcpy ( postcode , symbol - > primary , postcode_len ) ;
postcode [ postcode_len ] = ' \0 ' ;
2016-02-20 13:50:15 +03:00
if ( mode = = 2 ) {
2020-12-19 20:13:35 +03:00
for ( i = 0 ; i < postcode_len ; i + + ) {
2016-02-20 13:50:15 +03:00
if ( postcode [ i ] = = ' ' ) {
postcode [ i ] = ' \0 ' ;
2020-12-19 20:13:35 +03:00
postcode_len = i ;
break ;
} else if ( postcode [ i ] < ' 0 ' | | postcode [ i ] > ' 9 ' ) {
strcpy ( symbol - > errtxt , " 555: Non-numeric postcode in Primary Message " ) ;
return ZINT_ERROR_INVALID_DATA ;
2016-02-20 13:50:15 +03:00
}
}
2020-12-19 20:13:35 +03:00
maxi_do_primary_2 ( maxi_codeword , postcode , postcode_len , countrycode , service ) ;
} else {
/* Just truncate and space-pad */
2016-02-20 13:50:15 +03:00
postcode [ 6 ] = ' \0 ' ;
2020-12-19 20:13:35 +03:00
for ( i = postcode_len ; i < 6 ; i + + ) {
postcode [ i ] = ' ' ;
}
/* Upper-case and check for Code Set A characters only */
to_upper ( postcode ) ;
for ( i = 0 ; i < 6 ; i + + ) {
/* Don't allow Code Set A control characters CR, RS, GS and RS */
if ( postcode [ i ] < ' ' | | maxiCodeSet [ postcode [ i ] ] > 1 ) {
strcpy ( symbol - > errtxt , " 556: Invalid characters in postcode in Primary Message " ) ;
return ZINT_ERROR_INVALID_DATA ;
}
}
maxi_do_primary_3 ( maxi_codeword , postcode , countrycode , service ) ;
2016-02-20 13:50:15 +03:00
}
2020-12-19 20:13:35 +03:00
if ( symbol - > option_2 ) { /* Check for option_2 = vv + 1, where vv is version of SCM prefix "[)>\R01\Gvv" */
if ( symbol - > option_2 < 0 | | symbol - > option_2 > 100 ) {
strcpy ( symbol - > errtxt , " 557: Invalid SCM prefix version " ) ;
return ZINT_ERROR_INVALID_OPTION ;
}
scm_vv = symbol - > option_2 - 1 ;
2016-02-20 13:50:15 +03:00
}
2020-12-19 20:13:35 +03:00
if ( symbol - > debug & ZINT_DEBUG_PRINT ) {
printf ( " Postcode: %s, Country Code: %d, Service Class: %d \n " , postcode , countrycode , service ) ;
2016-02-20 13:50:15 +03:00
}
} else {
maxi_codeword [ 0 ] = mode ;
}
2020-12-19 20:13:35 +03:00
if ( symbol - > debug & ZINT_DEBUG_PRINT ) {
printf ( " Mode: %d \n " , mode ) ;
}
2021-03-28 23:31:45 +03:00
i = maxi_text_process ( maxi_codeword , mode , source , length , symbol - > eci , scm_vv , symbol - > debug & ZINT_DEBUG_PRINT ) ;
2016-02-20 13:50:15 +03:00
if ( i = = ZINT_ERROR_TOO_LONG ) {
2017-07-27 18:01:53 +03:00
strcpy ( symbol - > errtxt , " 553: Input data too long " ) ;
2016-02-20 13:50:15 +03:00
return i ;
}
/* All the data is sorted - now do error correction */
2020-09-30 14:19:12 +03:00
maxi_do_primary_check ( maxi_codeword ) ; /* always EEC */
2016-02-20 13:50:15 +03:00
if ( mode = = 5 )
eclen = 56 ; // 68 data codewords , 56 error corrections
else
eclen = 40 ; // 84 data codewords, 40 error corrections
2020-09-30 14:19:12 +03:00
maxi_do_secondary_chk_even ( maxi_codeword , eclen / 2 ) ; // do error correction of even
maxi_do_secondary_chk_odd ( maxi_codeword , eclen / 2 ) ; // do error correction of odd
2016-02-20 13:50:15 +03:00
2020-12-19 20:13:35 +03:00
if ( symbol - > debug & ZINT_DEBUG_PRINT ) {
printf ( " Codewords: " ) ;
for ( i = 0 ; i < 144 ; i + + ) printf ( " %d " , maxi_codeword [ i ] ) ;
printf ( " \n " ) ;
}
# ifdef ZINT_TEST
if ( symbol - > debug & ZINT_DEBUG_TEST ) {
debug_test_codeword_dump ( symbol , maxi_codeword , 144 ) ;
}
# endif
2016-02-20 13:50:15 +03:00
/* Copy data into symbol grid */
for ( i = 0 ; i < 33 ; i + + ) {
for ( j = 0 ; j < 30 ; j + + ) {
block = ( MaxiGrid [ ( i * 30 ) + j ] + 5 ) / 6 ;
if ( block ! = 0 ) {
2020-12-19 20:13:35 +03:00
shift = 5 - ( ( MaxiGrid [ ( i * 30 ) + j ] + 5 ) % 6 ) ;
2016-02-20 13:50:15 +03:00
2020-12-19 20:13:35 +03:00
if ( ( maxi_codeword [ block - 1 ] > > shift ) & 0x1 ) {
2016-02-20 13:50:15 +03:00
set_module ( symbol , i , j ) ;
}
}
}
}
/* Add orientation markings */
set_module ( symbol , 0 , 28 ) ; // Top right filler
set_module ( symbol , 0 , 29 ) ;
set_module ( symbol , 9 , 10 ) ; // Top left marker
set_module ( symbol , 9 , 11 ) ;
set_module ( symbol , 10 , 11 ) ;
set_module ( symbol , 15 , 7 ) ; // Left hand marker
set_module ( symbol , 16 , 8 ) ;
set_module ( symbol , 16 , 20 ) ; // Right hand marker
set_module ( symbol , 17 , 20 ) ;
set_module ( symbol , 22 , 10 ) ; // Bottom left marker
set_module ( symbol , 23 , 10 ) ;
set_module ( symbol , 22 , 17 ) ; // Bottom right marker
set_module ( symbol , 23 , 17 ) ;
symbol - > width = 30 ;
symbol - > rows = 33 ;
2020-09-30 14:19:12 +03:00
return error_number ;
2008-07-14 01:15:55 +04:00
}