2019-10-30 01:54:18 +03:00
/* general_field.c - Handles general field compaction (GS1 DataBar and composites) */
/*
libzint - the open source barcode library
Add multiple segments support for AZTEC, CODEONE, DATAMATRIX, DOTCODE,
GRIDMATRIX, HANXIN, MAXICODE, MICROPDF417, PDF417, QRCODE, RMQR, ULTRA
RMQR: fix ECI encoding (wrong bit length for indicator)
MICROQR: check versions M1 and M2 for allowed characters so as to give
better error messages
DOTCODE: some small optimizations
common.c: add is_chr(), segs_length(), segs_cpy()
CODEONE/CODE128/DOTCODE/GRIDMATRIX/HANXIN/MAXICODE/QRCODE/ULTRA: add
namespace prefixes to static funcs/data
includes: use Z_ prefix, unuse double underscore prefixes (guard defines)
manual.txt: compress some tables using double/treble column sets
2022-05-09 21:50:50 +03:00
Copyright ( C ) 2019 - 2022 Robin Stuart < rstuart114 @ gmail . com >
2019-10-30 01:54:18 +03:00
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 .
*/
2022-06-24 16:38:48 +03:00
/* SPDX-License-Identifier: BSD-3-Clause */
2019-10-30 01:54:18 +03:00
# include "common.h"
# include "general_field.h"
2021-07-13 19:39:03 +03:00
static const char alphanum_puncs [ ] = " *,-./ " ;
static const char isoiec_puncs [ ] = " ! \" %&'()*+,-./:;<=>?_ " ; /* Note contains space, not in cset82 */
Add multiple segments support for AZTEC, CODEONE, DATAMATRIX, DOTCODE,
GRIDMATRIX, HANXIN, MAXICODE, MICROPDF417, PDF417, QRCODE, RMQR, ULTRA
RMQR: fix ECI encoding (wrong bit length for indicator)
MICROQR: check versions M1 and M2 for allowed characters so as to give
better error messages
DOTCODE: some small optimizations
common.c: add is_chr(), segs_length(), segs_cpy()
CODEONE/CODE128/DOTCODE/GRIDMATRIX/HANXIN/MAXICODE/QRCODE/ULTRA: add
namespace prefixes to static funcs/data
includes: use Z_ prefix, unuse double underscore prefixes (guard defines)
manual.txt: compress some tables using double/treble column sets
2022-05-09 21:50:50 +03:00
# define IS_ISOIEC_F (IS_LWR_F | IS_C82_F | IS_AST_F | IS_PLS_F | IS_MNS_F | IS_SPC_F)
2019-10-30 01:54:18 +03:00
/* Returns type of char at `i`. FNC1 counted as NUMERIC. Returns 0 if invalid char */
2020-12-21 22:30:07 +03:00
static int general_field_type ( const char * general_field , const int i ) {
2022-06-24 16:38:48 +03:00
if ( general_field [ i ] = = ' [ ' | | z_isdigit ( general_field [ i ] ) ) {
2019-10-30 01:54:18 +03:00
return NUMERIC ;
}
2022-06-24 16:38:48 +03:00
if ( z_isupper ( general_field [ i ] ) | | posn ( alphanum_puncs , general_field [ i ] ) ! = - 1 ) {
2020-07-10 21:39:32 +03:00
return ALPHANUMERIC ;
2019-10-30 01:54:18 +03:00
}
2021-10-21 01:05:30 +03:00
if ( is_sane ( IS_ISOIEC_F , ( const unsigned char * ) general_field + i , 1 ) ) {
2019-10-30 01:54:18 +03:00
return ISOIEC ;
}
return 0 ;
}
/* Returns true if next (including `i`) `num` chars of type `type`, or if given (non-zero), `type2` */
2020-12-21 22:30:07 +03:00
static int general_field_next ( const char * general_field , int i , const int general_field_len , int num , const int type ,
const int type2 ) {
2019-10-30 01:54:18 +03:00
if ( i + num > general_field_len ) {
return 0 ;
}
for ( ; i < general_field_len & & num ; i + + , num - - ) {
int type_i = general_field_type ( general_field , i ) ;
if ( ( type_i ! = type & & ! type2 ) | | ( type_i ! = type & & type_i ! = type2 ) ) {
return 0 ;
}
}
return num = = 0 ;
}
/* Returns true if next (including `i`) `num` up to `max_num` chars of type `type` and occur at end */
2020-12-21 22:30:07 +03:00
static int general_field_next_terminate ( const char * general_field , int i , const int general_field_len , int num ,
const int max_num , const int type ) {
2019-10-30 01:54:18 +03:00
if ( i + max_num < general_field_len ) {
return 0 ;
}
for ( ; i < general_field_len ; i + + , num - - ) {
if ( general_field_type ( general_field , i ) ! = type ) {
return 0 ;
}
}
return i = = general_field_len & & num < = 0 ;
}
/* Returns true if none of the next (including `i`) `num` chars (or end occurs) of type `type` */
2020-12-21 22:30:07 +03:00
static int general_field_next_none ( const char * general_field , int i , const int general_field_len , int num ,
const int type ) {
2019-10-30 01:54:18 +03:00
for ( ; i < general_field_len & & num ; i + + , num - - ) {
if ( general_field_type ( general_field , i ) = = type ) {
return 0 ;
}
}
return num = = 0 | | i = = general_field_len ;
}
/* Attempts to apply encoding rules from sections 7.2.5.5.1 to 7.2.5.5.3
2019-10-31 05:01:42 +03:00
* of ISO / IEC 24724 : 2011 ( same as sections 5.4 .1 to 5.4 .3 of ISO / IEC 24723 : 2010 ) */
2020-12-21 22:30:07 +03:00
INTERNAL int general_field_encode ( const char * general_field , const int general_field_len , int * p_mode ,
char * p_last_digit , char binary_string [ ] , int * p_bp ) {
2019-10-30 01:54:18 +03:00
int i , d1 , d2 ;
int mode = * p_mode ;
2020-12-21 22:30:07 +03:00
char last_digit = ' \0 ' ; /* Set to odd remaining digit at end if any */
int bp = * p_bp ;
2019-10-30 01:54:18 +03:00
for ( i = 0 ; i < general_field_len ; ) {
int type = general_field_type ( general_field , i ) ;
if ( ! type ) {
return 0 ;
}
switch ( mode ) {
case NUMERIC :
if ( i < general_field_len - 1 ) { /* If at least 2 characters remain */
2020-12-21 22:30:07 +03:00
if ( type ! = NUMERIC | | general_field_type ( general_field , i + 1 ) ! = NUMERIC ) {
/* 7.2.5.5.1/5.4.1 a) */
bp = bin_append_posn ( 0 , 4 , binary_string , bp ) ; /* Alphanumeric latch "0000" */
2020-07-10 21:39:32 +03:00
mode = ALPHANUMERIC ;
2019-10-30 01:54:18 +03:00
} else {
d1 = general_field [ i ] = = ' [ ' ? 10 : ctoi ( general_field [ i ] ) ;
d2 = general_field [ i + 1 ] = = ' [ ' ? 10 : ctoi ( general_field [ i + 1 ] ) ;
2020-12-21 22:30:07 +03:00
bp = bin_append_posn ( ( 11 * d1 ) + d2 + 8 , 7 , binary_string , bp ) ;
2019-10-30 01:54:18 +03:00
i + = 2 ;
}
} else { /* If 1 character remains */
2020-12-21 22:30:07 +03:00
if ( type ! = NUMERIC ) {
/* 7.2.5.5.1/5.4.1 b) */
bp = bin_append_posn ( 0 , 4 , binary_string , bp ) ; /* Alphanumeric latch "0000" */
2020-07-10 21:39:32 +03:00
mode = ALPHANUMERIC ;
2019-10-30 01:54:18 +03:00
} else {
2020-12-21 22:30:07 +03:00
/* Ending with single digit.
* 7.2 .5 .5 .1 c ) and 5.4 .1 c ) dealt with separately outside this procedure */
last_digit = general_field [ i ] ;
2019-10-30 01:54:18 +03:00
i + + ;
}
}
break ;
2020-07-10 21:39:32 +03:00
case ALPHANUMERIC :
2020-12-21 22:30:07 +03:00
if ( general_field [ i ] = = ' [ ' ) {
/* 7.2.5.5.2/5.4.2 a) */
bp = bin_append_posn ( 15 , 5 , binary_string , bp ) ; /* "01111" */
2019-10-30 01:54:18 +03:00
mode = NUMERIC ;
i + + ;
2020-12-21 22:30:07 +03:00
} else if ( type = = ISOIEC ) {
/* 7.2.5.5.2/5.4.2 b) */
bp = bin_append_posn ( 4 , 5 , binary_string , bp ) ; /* ISO/IEC 646 latch "00100" */
2019-10-30 01:54:18 +03:00
mode = ISOIEC ;
2020-12-21 22:30:07 +03:00
} else if ( general_field_next ( general_field , i , general_field_len , 6 , NUMERIC , 0 ) ) {
/* 7.2.5.5.2/5.4.2 c) */
bp = bin_append_posn ( 0 , 3 , binary_string , bp ) ; /* Numeric latch "000" */
2019-10-30 01:54:18 +03:00
mode = NUMERIC ;
2020-12-21 22:30:07 +03:00
} else if ( general_field_next_terminate ( general_field , i , general_field_len , 4 ,
5 /*Can limit to 5 max due to above*/ , NUMERIC ) ) {
/* 7.2.5.5.2/5.4.2 d) */
bp = bin_append_posn ( 0 , 3 , binary_string , bp ) ; /* Numeric latch "000" */
2019-10-30 01:54:18 +03:00
mode = NUMERIC ;
2022-06-24 16:38:48 +03:00
} else if ( z_isdigit ( general_field [ i ] ) ) {
2020-12-21 22:30:07 +03:00
bp = bin_append_posn ( general_field [ i ] - 43 , 5 , binary_string , bp ) ;
2019-10-30 01:54:18 +03:00
i + + ;
2022-06-24 16:38:48 +03:00
} else if ( z_isupper ( general_field [ i ] ) ) {
2020-12-21 22:30:07 +03:00
bp = bin_append_posn ( general_field [ i ] - 33 , 6 , binary_string , bp ) ;
2019-10-30 01:54:18 +03:00
i + + ;
} else {
2020-12-21 22:30:07 +03:00
bp = bin_append_posn ( posn ( alphanum_puncs , general_field [ i ] ) + 58 , 6 , binary_string , bp ) ;
2019-10-30 01:54:18 +03:00
i + + ;
}
break ;
case ISOIEC :
2020-12-21 22:30:07 +03:00
if ( general_field [ i ] = = ' [ ' ) {
/* 7.2.5.5.3/5.4.3 a) */
bp = bin_append_posn ( 15 , 5 , binary_string , bp ) ; /* "01111" */
2019-10-30 01:54:18 +03:00
mode = NUMERIC ;
i + + ;
} else {
int next_10_not_isoiec = general_field_next_none ( general_field , i , general_field_len , 10 , ISOIEC ) ;
2020-12-21 22:30:07 +03:00
if ( next_10_not_isoiec & & general_field_next ( general_field , i , general_field_len , 4 ,
NUMERIC , 0 ) ) {
/* 7.2.5.5.3/5.4.3 b) */
bp = bin_append_posn ( 0 , 3 , binary_string , bp ) ; /* Numeric latch "000" */
2019-10-30 01:54:18 +03:00
mode = NUMERIC ;
2020-12-21 22:30:07 +03:00
} else if ( next_10_not_isoiec & & general_field_next ( general_field , i , general_field_len , 5 ,
ALPHANUMERIC , NUMERIC ) ) {
/* 7.2.5.5.3/5.4.3 c) */
2019-10-30 01:54:18 +03:00
/* Note this rule can produce longer bitstreams if most of the alphanumerics are numeric */
2020-12-21 22:30:07 +03:00
bp = bin_append_posn ( 4 , 5 , binary_string , bp ) ; /* Alphanumeric latch "00100" */
2020-07-10 21:39:32 +03:00
mode = ALPHANUMERIC ;
2022-06-24 16:38:48 +03:00
} else if ( z_isdigit ( general_field [ i ] ) ) {
2020-12-21 22:30:07 +03:00
bp = bin_append_posn ( general_field [ i ] - 43 , 5 , binary_string , bp ) ;
2019-10-30 01:54:18 +03:00
i + + ;
2022-06-24 16:38:48 +03:00
} else if ( z_isupper ( general_field [ i ] ) ) {
2020-12-21 22:30:07 +03:00
bp = bin_append_posn ( general_field [ i ] - 1 , 7 , binary_string , bp ) ;
2019-10-30 01:54:18 +03:00
i + + ;
2022-06-24 16:38:48 +03:00
} else if ( z_islower ( general_field [ i ] ) ) {
2020-12-21 22:30:07 +03:00
bp = bin_append_posn ( general_field [ i ] - 7 , 7 , binary_string , bp ) ;
2019-10-30 01:54:18 +03:00
i + + ;
} else {
2020-12-21 22:30:07 +03:00
bp = bin_append_posn ( posn ( isoiec_puncs , general_field [ i ] ) + 232 , 8 , binary_string , bp ) ;
2019-10-30 01:54:18 +03:00
i + + ;
}
}
break ;
}
}
* p_mode = mode ;
* p_last_digit = last_digit ;
2020-12-21 22:30:07 +03:00
* p_bp = bp ;
2019-10-30 01:54:18 +03:00
return 1 ;
}
Add multiple segments support for AZTEC, CODEONE, DATAMATRIX, DOTCODE,
GRIDMATRIX, HANXIN, MAXICODE, MICROPDF417, PDF417, QRCODE, RMQR, ULTRA
RMQR: fix ECI encoding (wrong bit length for indicator)
MICROQR: check versions M1 and M2 for allowed characters so as to give
better error messages
DOTCODE: some small optimizations
common.c: add is_chr(), segs_length(), segs_cpy()
CODEONE/CODE128/DOTCODE/GRIDMATRIX/HANXIN/MAXICODE/QRCODE/ULTRA: add
namespace prefixes to static funcs/data
includes: use Z_ prefix, unuse double underscore prefixes (guard defines)
manual.txt: compress some tables using double/treble column sets
2022-05-09 21:50:50 +03:00
/* vim: set ts=4 sw=4 et : */