diff --git a/backend/codablock.c b/backend/codablock.c index 8b9fd77f..416123c3 100644 --- a/backend/codablock.c +++ b/backend/codablock.c @@ -31,15 +31,918 @@ */ #include +#include +#include +#include +#ifdef _MSC_VER +#include +#endif #include "common.h" +#include "gs1.h" + +#define uchar unsigned char +/* FTab Flagzeichen - Addierbar */ +#define CodeA 1 +#define CodeB 2 +#define CodeC 4 +#define CEnd 8 +#define CShift 16 +#define CFill 32 +#define CodeFNC1 64 +#define ZTNum (CodeA+CodeB+CodeC) +#define ZTFNC1 (CodeA+CodeB+CodeC+CodeFNC1) + +/* ASCII-Extension for Codablock-F */ +#define aFNC1 (uchar)(128) +#define aFNC2 (uchar)(129) +#define aFNC3 (uchar)(130) +#define aFNC4 (uchar)(131) +#define aCodeA (uchar)(132) +#define aCodeB (uchar)(133) +#define aCodeC (uchar)(134) +#define aShift (uchar)(135) + +/* Code F Analysing-Chart */ +typedef struct sCharacterSetTable +{ + int CharacterSet; /* Still possible character sets for actual*/ + int AFollowing; /* Still following Characters in Charset A */ + int BFollowing; /* Still following Characters in Charset B */ + int CFollowing; /* Still following Characters in Charset C */ +} CharacterSetTable; + +/* Find the possible Code-128 Character sets for a character + * The result is an or of CodeA,CodeB,CodeC,CodeFNC1 in dependency of the + * possible Code 128 character sets. + */ +int GetPossibleCharacterSet(unsigned char C) +{ + if (C<='\x19') /* Dec:31 */ + return CodeA; + if (C>='0' && C<='9') + return ZTNum; /* ZTNum=CodeA+CodeB+CodeC */ + if (C==aFNC1) + return ZTFNC1; /* ZTFNC1=CodeA+CodeB+CodeC+CodeFNC1 */ + if (C>='\x60' && C<='\x7f') /* 60 to 127 */ + return CodeB; + return CodeA+CodeB; +} + +/* Create a Table with the following information for each Data character: + * int CharacterSet is an or of CodeA,CodeB,CodeC,CodeFNC1, in + * dependency which character set is applicable. + * (Result of GetPossibleCharacterSet) + * int AFollowing,BFollowing The number of source characters you still may encode + * in this character set. + * int CFollowing The number of characters encodable in CodeC if we + * start here. + */ +void CreateCharacterSetTable(CharacterSetTable T[], unsigned char *data, int dataLength) +{ + int charCur; + int runChar; + + /* Treat the Data backwards */ + charCur=dataLength-1; + T[charCur].CharacterSet=GetPossibleCharacterSet(data[charCur]); + T[charCur].AFollowing=((T[charCur].CharacterSet & CodeA)==0)?0:1; + T[charCur].BFollowing=((T[charCur].CharacterSet & CodeB)==0)?0:1; + T[charCur].CFollowing=0; + + for (charCur--;charCur>=0;charCur--) + { + T[charCur].CharacterSet=GetPossibleCharacterSet(data[charCur]); + T[charCur].AFollowing= + ((T[charCur].CharacterSet & CodeA)==0)?0:T[charCur+1].AFollowing+1; + T[charCur].BFollowing= + ((T[charCur].CharacterSet & CodeB)==0)?0:T[charCur+1].BFollowing+1; + T[charCur].CFollowing=0; + + } + /* Find the CodeC-chains */ + for (charCur=0;charCur=dataLength) + break; + /* Only a Number may follow */ + if (T[runChar].CharacterSet==ZTNum) + T[charCur].CFollowing+=2; + else + break; + } + ++runChar; + } while (runChar0 && runChar44) are requested the columns is extended. + * A oneLigner may be shorter. Error is set to eCorrectWidthInOneLigner. + * Parameters : + * Pcolumns Pointer on the Characters who fits in the Line + * If a different count is calculated it is corrected + * in the callers workspace. + * pFillings Output of filling characters + * pSet Output of the character sets used, allocated by me. + * Data The Data string to encode, exceptionnally not an out + * Return value The lines requested. + */ + +int Columns2Rows(CharacterSetTable *T, unsigned char *data, int dataLength, + int * pRows, int * pUseColumns, int * pSet, int * pFillings) +{ + int useColumns; /* Usable Characters per line */ + int fillings; /* Number of filling characters */ + int rowsCur; + int charCur; + int runChar; + int emptyColumns; /* Number of codes still empty in line. */ + int emptyColumns2; /* Alternative emptyColumns to compare */ + int fOneLiner; /* Flag if One Liner */ + int CPaires; /* Number of digit pairs which may fit in the line */ + int characterSetCur; /* Current Character Set */ + + useColumns=*pUseColumns; + if (useColumns<3) + useColumns=3; + + /* >>> Loop until rowsCur<44 */ + do { + memset(pSet,0,dataLength*sizeof(int)); + charCur=0; + rowsCur=0; + fOneLiner=1; /* First try one-Liner */ + + /* >>> Line and OneLiner-try Loop */ + do{ + /* >> Start Character */ + emptyColumns=useColumns; /* Remained place in Line */ + if (fOneLiner) + emptyColumns+=2; + + /* >>Choose in Set A or B */ + /* (C is changed as an option later on) */ + + pSet[charCur]=characterSetCur= + (T[charCur].AFollowing > T[charCur].BFollowing) + ? CodeA : CodeB; + + /* >> Test on Numeric Mode C */ + CPaires=RemainingDigits(T,charCur, emptyColumns); + if (CPaires>=4) + { + /* 4 Digits in Numeric compression ->OK */ + /* > May an odd start find more ? */ + /* Skip leading 's */ + /* Typical structure : 12... */ + /* Test if numeric after one isn't better.*/ + runChar=charCur; + emptyColumns2=emptyColumns; + while (T[runChar].CharacterSet==ZTFNC1) + { + ++runChar; + --emptyColumns2; + } + if (CPaires>=RemainingDigits(T,runChar+1,emptyColumns2-1)) + { + /* Start odd is not better */ + /* We start in C */ + pSet[charCur]=characterSetCur=CodeC; + /* Inkrement charCur */ + if (T[charCur].CharacterSet!=ZTFNC1) + ++charCur; /* 2 Num.Digits */ + } + } + ++charCur; + --emptyColumns; + + /* >> Following characters */ + while(emptyColumns>0 && charCur> Check switching to CodeC */ + /* Switch if : + * - Character not FNC1 + * - 4 real Digits will fit in line + * - an odd Start will not be better + */ + if (T[charCur].CharacterSet==ZTNum + && (CPaires=RemainingDigits(T,charCur, emptyColumns-1))>=4 + && CPaires > RemainingDigits(T,charCur+1,emptyColumns-2)) + { + /* > Change to C */ + pSet[charCur]=characterSetCur=CodeC; + charCur+=2; /* 2 Digit */ + emptyColumns-=2; /* 12 */ + } else if (characterSetCur==CodeA) + { + if(T[charCur].AFollowing==0) + { + /* Must change to B */ + if (emptyColumns==1) + { + /* Can't switch: */ + pSet[charCur-1]|=CEnd+CFill; + emptyColumns=0; + }else{ + /* or ? */ + pSet[charCur]|=(T[charCur].BFollowing==1)?CShift:CodeB; + emptyColumns-=2; + ++charCur; + } + }else{ + --emptyColumns; + ++charCur; + } + } else { /* Last possibility : CodeB */ + if(T[charCur].BFollowing==0) + { + /* Must change to A */ + if (emptyColumns==1) + { + /* Can't switch: */ + pSet[charCur-1]|=CEnd+CFill; + emptyColumns=0; + } else { + /* or ? */ + pSet[charCur]|=(T[charCur].AFollowing==1)?CShift:CodeA; + emptyColumns-=2; + ++charCur; + } + }else{ + --emptyColumns; + ++charCur; + } + } + break; + case CodeC: + if(T[charCur].CFollowing>0) + { + charCur+=(T[charCur].CharacterSet==ZTFNC1)?1:2; + emptyColumns--; + }else{ + /* Must change to A or B */ + if (emptyColumns==1) + { + /* Can't switch: */ + pSet[charCur-1]|=CEnd+CFill; + emptyColumns=0; + }else{ + /* or ?*/ + characterSetCur=pSet[charCur]= + (T[charCur].AFollowing > T[charCur].BFollowing) + ?CodeA:CodeB; + emptyColumns-=2; + ++charCur; + } + } + break; + } /* switch */ + } /* while */ + + /* > End of Codeline */ + pSet[charCur-1]|=CEnd; + ++rowsCur; + if ( fOneLiner) + { + if (charCur44) { + ++useColumns; + if (useColumns > 62) { + return ZINT_ERROR_TOO_LONG; + } + } + } while(rowsCur>44); + #ifdef _DEBUG + printf(" -> out: rowsCur <%i>, useColumns <%i>, fillings <%i>\n",rowsCur,useColumns,fillings); + #endif + *pUseColumns=useColumns; + *pRows=rowsCur; + *pFillings=fillings; + return 0; +} +/* Find columns if rows are given. + * Error may be : + * eLessLines : The exakt number of lines may only be constructed + * with a filing line. + * eMoreLines : Only Maddin knows why... + * eCorrectWidthInOneLigner : A one-Ligner is shorter than demanded. + * eExtendWidth : The Code is wider because otherwise the data amount. + * would not fit in 50 lines. + */ +int Rows2Columns(CharacterSetTable *T, unsigned char *data, int dataLength, + int * pRows, int * pUseColumns, int * pSet, int * pFillings) +{ + int errorCur; + int rowsCur; + int rowsRequested; /* Number of Lines Requested */ + int backupRows; + int fillings; + int backupFillings; + int useColumns; + int testColumns; /* To enter into Width2Lines */ + int backupColumns; + int fBackupOk; /* The memorysed set is o.k. */ + int testListSize = 0; + int pTestList[62]; +#ifndef _MSC_VER + int *pBackupSet[dataLength]; +#else + int *pBackupSet = (int *)_alloca(dataLength*sizeof(int)); +#endif + + rowsRequested=*pRows; + + #ifdef _DEBUG + fprintf(stderr,"Optimizer : Searching Lines <%i>\n",rowsRequested); + #endif + + fBackupOk=0; + if (rowsRequested==1) + /* OneLiners are self-calibrating */ + testColumns=32767; + else { + /* First guess */ + testColumns=dataLength/rowsRequested; + if (testColumns > 62) + testColumns = 62; + else if (testColumns < 1) + testColumns = 1; + } + + for (;;) { + pTestList[testListSize] = testColumns; + testListSize++; + useColumns=testColumns; /* Make a copy because it may be modified */ + errorCur = Columns2Rows(T, data, dataLength, &rowsCur, &useColumns, pSet, &fillings); + if (errorCur != 0) + return errorCur; + if (rowsCur<=rowsRequested) { + /* Less or exactly line number found */ + /* check if column count below already tested or Count = 1*/ + int fInTestList = (rowsCur == 1); + int posCur; + for (posCur = 0; posCur < testListSize && ! fInTestList; posCur++) { + if ( pTestList[posCur] == testColumns-1 ) + fInTestList = 1; + } + if (fInTestList) { + /* >> Smaller Width already tested + * if rowsCur=rowsRequested->Exit + * if rowsCur0 + * -> New search for rowsRequested:=rowsCur + */ + if (rowsCur==rowsRequested||fillings==0) { + /* Exit with actual */ + *pFillings=fillings; + *pRows=rowsCur; + *pUseColumns = useColumns; + return 0; + } + /* Search again for smaller Line number */ + rowsRequested=rowsCur; + pTestList[0] = testColumns; + testListSize = 1; + } + /* > Test more Lines (shorter CDB) */ + fBackupOk=(rowsCur==rowsRequested); + memcpy(pBackupSet,pSet,dataLength*sizeof(int)); + backupFillings=fillings; + backupColumns=useColumns; + backupRows=rowsCur; + --testColumns; + } else { + /* > To many lines */ + int fInTestList = fBackupOk; + int posCur; + for (posCur = 0; posCur < testListSize && ! fInTestList; posCur++) { + if ( pTestList[posCur] == testColumns+1 ) + fInTestList = 1; + } + if (fInTestList) { + /* The next less-lines (larger) code was + * already tested. So give the larger + * back. + */ + memcpy(pSet,pBackupSet,dataLength*sizeof(int)); + *pFillings=backupFillings; + *pRows=backupRows; + return 0; + } + /* > Test less lines (longer code) */ + backupRows=rowsCur; + memcpy(pBackupSet,pSet,dataLength*sizeof(int)); + backupFillings=fillings; + backupColumns=useColumns; + fBackupOk=0; + ++testColumns; + } + } +} + +/* Print a character in character set A + */ +void A2C128_A(uchar **ppOutPos,uchar c) +{ + uchar * pOutPos = *ppOutPos; + switch(c){ + case aCodeB: *pOutPos=100; break; + case aFNC4: *pOutPos=101; break; + case aFNC1: *pOutPos=102; break; + case aFNC2: *pOutPos=97; break; + case aFNC3: *pOutPos=96; break; + case aCodeC: *pOutPos=99; break; + case aShift: *pOutPos=98; break; + default: + /* +++ HaO 13.11.98 c>' ' && c < '\x1F' corrected */ + if(c>=' ' && c<='_') + *pOutPos=(uchar)(c-' '); + else + *pOutPos=(uchar)(c+64); + break; + } + (*ppOutPos)++; +} +/* Output c in Set B + */ +void A2C128_B(uchar **ppOutPos,uchar c) +{ + uchar * pOutPos = *ppOutPos; + switch(c){ + case aFNC1: *pOutPos=102; break; + case aFNC2: *pOutPos=97; break; + case aFNC3: *pOutPos=96; break; + case aFNC4: *pOutPos=100; break; + case aCodeA: *pOutPos=101; break; + case aCodeC: *pOutPos=99; break; + case aShift: *pOutPos=98; break; + default: *pOutPos=(uchar)(c-' '); break; + } + ++(*ppOutPos); +} +/* Output c1, c2 in Set C + */ +void A2C128_C(uchar **ppOutPos,uchar c1,uchar c2) +{ + uchar * pOutPos = *ppOutPos; + switch(c1){ + case aFNC1: *pOutPos=102; break; + case aCodeB: *pOutPos=100; break; + case aCodeA: *pOutPos=101; break; + default: *pOutPos=(char)(10 * (c1- '0') + (c2 - '0'));break; + } + (*ppOutPos)++; +} +/* Output a character in Characterset + */ +void ASCIIZ128(uchar **ppOutPos, int CharacterSet,uchar c1, uchar c2) +{ + if (CharacterSet==CodeA) + A2C128_A(ppOutPos,c1); + else if(CharacterSet==CodeB) + A2C128_B(ppOutPos,c1); + else + A2C128_C(ppOutPos,c1,c2); +} +/* XLate Table A of Codablock-F Specification and call output + */ +void SummeASCII(uchar **ppOutPos, int Sum, int CharacterSet) +{ + switch (CharacterSet){ + case CodeA: + A2C128_A(ppOutPos, (uchar)Sum); + break; + case CodeB: + if (Sum<=31) + A2C128_B(ppOutPos, (uchar)(Sum+96)); + else if(Sum<=47) + A2C128_B(ppOutPos, (uchar)Sum); + else + A2C128_B(ppOutPos, (uchar)(Sum+10)); + break; + case CodeC: + A2C128_C(ppOutPos + ,(char)(Sum/10+'0') ,(uchar)(Sum%10+'0')); + break; + } +} + int codablock(struct zint_symbol *symbol, unsigned char source[], int length) { - + int charCur; + int dataLength; + int Error; + int rows, columns, useColumns; + int fillings; + int Sum1,Sum2; + uchar * pOutPos; + int rowCur; + int characterSetCur; + int emptyColumns; +#ifdef _MSC_VER + CharacterSetTable *T; + unsigned char *data; + int *pSet; + uchar * pOutput; +#endif + + /* Parameter check */ + /* option1: rows 0: automatic, 1..44 */ + rows = symbol->option_1; + if (rows > 44) { + strcpy(symbol->errtxt, "Row parameter not in 0..44"); + return ZINT_ERROR_INVALID_OPTION; + } + /* option_2: (usable data) columns: 0: automatic, 6..66 */ + columns = symbol->option_2; + if ( ! (columns <= 0 || (columns >= 6 && columns <=66)) ) { + strcpy(symbol->errtxt, "Columns parameter not in 0,6..66"); + return ZINT_ERROR_INVALID_OPTION; + } + /* There are 5 Codewords for Organisation Start(2),row(1),CheckSum,Stop */ + useColumns = columns - 5; + /* GS1 not implemented */ if (symbol->input_mode == GS1_MODE) { - printf("Encode GS1 data (Codablock-E?)\n"); - } else { - printf("Encode in Codablock-F\n"); - } - - return ZINT_ERROR_INVALID_OPTION; -} \ No newline at end of file + strcpy(symbol->errtxt, "GS1 mode not supported"); + return ZINT_ERROR_INVALID_OPTION; + } +#ifndef _MSC_VER + unsigned char data[length*2+1]; +#else + data = (unsigned char *) _alloca(length * 2+1); +#endif + + dataLength = 0; + if (symbol->output_options & READER_INIT) { + data[dataLength] = aFNC3; + dataLength++; + } + /* Replace all Codes>127 with Code-128 */ + for (charCur=0;charCur127) + { + data[dataLength] = aFNC4; + dataLength++; + data[dataLength] = (unsigned char)(source[charCur]&127); + } else + data[dataLength] = source[charCur]; + dataLength++; + } + + /* Build character set table */ +#ifndef _MSC_VER + CharacterSetTable T[dataLength]; + int pSet[dataLength]; +#else + T=(CharacterSetTable *)_alloca(dataLength*sizeof(CharacterSetTable)); + pSet = (int *)_alloca(dataLength*sizeof(int)); +#endif + CreateCharacterSetTable(T,data,dataLength); + + /* Find final row and column count */ + /* nor row nor column count given */ + if ( rows <= 0 && useColumns <= 0 ) { + /* Use Code128 until reasonable size */ + if (dataLength < 64) { + rows = 1; + } else { + /* use 3/1 aspect/ratio Codablock */ + rows = ((int)floor(sqrt(dataLength)))/3; + if (rows < 1) + rows = 1; + else if (rows > 44) + rows = 44; + } + } + if ( rows > 0 ) { + /* row count given */ + Error=Rows2Columns(T,data,dataLength,&rows,&useColumns,pSet,&fillings); + } else { + /* column count given */ + Error=Columns2Rows(T,data,dataLength,&rows,&useColumns,pSet,&fillings); + } + if (Error != 0) { + strcpy(symbol->errtxt, "data string to long"); + return Error; + } + /* Checksum */ + Sum1=Sum2=0; + if (rows>1) + { + int charCur; + for (charCur=0 ; charCur>> Build C128 code numbers */ + /* The C128 column count contains Start (2CW), Row ID, Checksum, Stop */ +#ifndef _MSC_VER + uchar pOutput[columns * rows]; +#else + pOutput = (char *)_alloca(columns * rows * sizeof(char)); +#endif + pOutPos = pOutput; + charCur=0; + /* >> Loop over Lines */ + for (rowCur=0 ; rowCur=dataLength) + { + /* >> Empty line with StartCCodeBCodeC */ + characterSetCur=CodeC; + /* CDB Start C*/ + pOutPos+=sprintf(pOutPos,"\x67\x63"); + SummeASCII(&pOutPos,rowCur+42,CodeC); + emptyColumns=useColumns-2; + while (emptyColumns>0) + { + if(characterSetCur==CodeC) + { + A2C128_C(&pOutPos,aCodeB,'\0'); + characterSetCur=CodeB; + }else{ + A2C128_B(&pOutPos,aCodeC); + characterSetCur=CodeC; + } + --emptyColumns; + } + }else{ + /* >> Normal Line */ + /* > Startcode */ + switch (pSet[charCur] & (CodeA+CodeB+CodeC)){ + case CodeA: + if (rows==1) + pOutPos+=sprintf((char *)pOutPos,"\x67"); + else + pOutPos+=sprintf((char *)pOutPos,"\x67\x62"); + characterSetCur=CodeA; + break; + case CodeB: + if (rows==1) + pOutPos+=sprintf((char *)pOutPos,"\x68"); + else + pOutPos+=sprintf((char *)pOutPos,"\x67\x64"); + characterSetCur=CodeB; + break; + case CodeC: + if (rows==1) + pOutPos+=sprintf((char *)pOutPos,"\x69"); + else + pOutPos+=sprintf((char *)pOutPos,"\x67\x63"); + characterSetCur=CodeC; + break; + } + if (rows>1) + { + /* > Set F1 */ + /* In first line : # of Lines */ + /* In Case of CodeA we shifted to CodeB */ + SummeASCII(&pOutPos + ,(rowCur==0)?rows-2:rowCur+42 + ,(characterSetCur==CodeA)?CodeB:characterSetCur + ); + } + /* >>> Data */ + emptyColumns=useColumns; + /* +++ One liner don't have start/stop code */ + if (rows == 1) + emptyColumns +=2; + /* >> Character loop */ + while (emptyColumns>0) + { + /* ? Change character set */ + /* not at first possition (It was then the start set) */ + /* +++ special case for one-ligner */ + if (emptyColumns> Shift it and put out the shiftet character */ + ASCIIZ128(&pOutPos,characterSetCur,aShift,'\0'); + emptyColumns-=2; + characterSetCur=(characterSetCur==CodeB)?CodeA:CodeB; + ASCIIZ128(&pOutPos,characterSetCur,data[charCur],'\0'); + characterSetCur=(characterSetCur==CodeB)?CodeA:CodeB; + }else{ + /* Normal Character */ + if (characterSetCur==CodeC) + { + if (data[charCur]==aFNC1) + A2C128_C(&pOutPos,aFNC1,'\0'); + else + { + A2C128_C(&pOutPos,data[charCur],data[charCur+1]); + ++charCur; + /* We need this here to get the good index */ + /* for the termination flags in Set. */ + } + }else + ASCIIZ128(&pOutPos,characterSetCur,data[charCur],'\0'); + --emptyColumns; + } + /* >> End Criteria */ + if ((pSet[charCur] & CFill)!=0) + { + /* Fill Line but leave space for checks in last line */ + if(rowCur==rows-1 && emptyColumns>=2) + emptyColumns-=2; + while(emptyColumns>0) + { + switch(characterSetCur){ + case CodeC: + A2C128_C(&pOutPos,aCodeB,'\0'); + characterSetCur=CodeB; + break; + case CodeB: + A2C128_B(&pOutPos,aCodeC); + characterSetCur=CodeC; + break; + case CodeA: + A2C128_A(&pOutPos,aCodeC); + characterSetCur=CodeC; + break; + } + --emptyColumns; + } + } + if ((pSet[charCur] & CEnd)!=0) + emptyColumns=0; + ++charCur; + } /* Loop over characters */ + } /* if filling-Line / normal */ + + /* Add checksum in last line */ + if(rows>1 && rowCur==rows-1) + { + SummeASCII(&pOutPos,Sum1,characterSetCur); + SummeASCII(&pOutPos,Sum2,characterSetCur); + } + /* Add Code 128 checksum */ + { + int Sum=0; + int Pos=0; + for ( ; Pos < useColumns+2 ; Pos++) + { + Sum = (Sum + + ((Pos==0?1:Pos) * pOutput[columns*rowCur+Pos]) % 103 + ) % 103; + } + *pOutPos=(uchar)Sum; + pOutPos++; + } + /* Add end character */ + *pOutPos=106; + pOutPos++; + } /* End Lineloop */ + + #ifdef _DEBUG + /* Dump the output to the screen + */ + printf("\nCode 128 Code Numbers:\n"); + { /* start a new level of local variables */ + int DPos, DPos2; + for (DPos=0 ; DPos< rows ; DPos++) + { + for (DPos2=0 ; DPos2 < columns ; DPos2++) + { + printf("% 3i ",(int)(pOutput[DPos*columns+DPos2])); + } + printf("\n"); + } + } + printf("rows=%i columns=%i fillings=%i\n", rows, columns, fillings); + #endif + return ZINT_ERROR_INVALID_OPTION; +} diff --git a/frontend/main.c b/frontend/main.c index 3a06a156..e081f49e 100644 --- a/frontend/main.c +++ b/frontend/main.c @@ -95,7 +95,8 @@ void usage(void) { " --directsvg Send SVG output to stdout\n" " --dump Dump hexadecimal representation to stdout\n" " --rotate=NUMBER Rotate symbol (PNG output only).\n" - " --cols=NUMBER (PDF417) Number of columns.\n" + " --cols=NUMBER (PDF417, Codablock F) Number of columns.\n" + " --rows=NUMBER (Codablock F) Number of rows.\n" " --vers=NUMBER (QR Code or Han Xin) Version\n" " --secure=NUMBER (PDF417 and QR Code) Error correction level.\n" " --primary=STRING (Maxicode and Composite) Structured primary message.\n" @@ -483,6 +484,7 @@ int main(int argc, char **argv) { {"fg", 1, 0, 0}, {"bg", 1, 0, 0}, {"cols", 1, 0, 0}, + {"rows", 1, 0, 0}, {"vers", 1, 0, 0}, {"rotate", 1, 0, 0}, {"secure", 1, 0, 0}, @@ -617,13 +619,21 @@ int main(int argc, char **argv) { } if (!strcmp(long_options[option_index].name, "cols")) { - if ((atoi(optarg) >= 1) && (atoi(optarg) <= 30)) { + if ((atoi(optarg) >= 6) && (atoi(optarg) <= 66)) { my_symbol->option_2 = atoi(optarg); } else { fprintf(stderr, "Number of columns out of range\n"); fflush(stderr); } } + if (!strcmp(long_options[option_index].name, "rows")) { + if ((atoi(optarg) >= 1) && (atoi(optarg) <= 44)) { + my_symbol->option_1 = atoi(optarg); + } else { + fprintf(stderr, "Number of columns out of range\n"); + fflush(stderr); + } + } if (!strcmp(long_options[option_index].name, "vers")) { if ((atoi(optarg) >= 1) && (atoi(optarg) <= 84)) { my_symbol->option_2 = atoi(optarg);