diff options
-rw-r--r-- | Makefile | 23 | ||||
-rw-r--r-- | inc/num.h | 160 | ||||
-rw-r--r-- | inc/tga.h | 1 | ||||
-rw-r--r-- | inc/upc.h | 2 | ||||
-rw-r--r-- | obj/.gitignore | 2 | ||||
-rw-r--r-- | readme | 11 | ||||
-rw-r--r-- | src/main.c | 137 | ||||
-rw-r--r-- | src/tga.c | 55 | ||||
-rw-r--r-- | src/upc.c | 113 |
9 files changed, 504 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c859442 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +.RECIPEPREFIX = > + +CC = gcc + +CFLAGS = -I$(INCDIR) + +INCDIR = inc +OBJDIR = obj +SRCDIR = src + +DEP = $(INCDIR)/tga.h $(INCDIR)/upc.h $(INCDIR)/num.h +OBJ = $(OBJDIR)/main.o $(OBJDIR)/tga.o $(OBJDIR)/upc.o # $(OBJDIR)/cli.o + +$(OBJDIR)/%.o: $(SRCDIR)/%.c $(DEPS) +> $(CC) -c -o $@ $< $(CFLAGS) + +upc: $(OBJ) +> $(CC) -o $@ $^ $(CFLAGS) + +.PHONY: clean + +clean: +> rm -f $(OBJDIR)/*.o ./*.tga upc diff --git a/inc/num.h b/inc/num.h new file mode 100644 index 0000000..7cd93d8 --- /dev/null +++ b/inc/num.h @@ -0,0 +1,160 @@ +const char NUMBER[10][16][8] = { 0, 0, 0, 0, 0, 0, 0, 0, // NUMBER 0 + 0, 0, 1, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 1, 1, 0, + 0, 1, 1, 0, 1, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 1, 0, 1, 1, 0, + 0, 1, 1, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, // NUMBER 1 + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 1, 1, 1, 1, 0, 0, 0, + 0, 1, 1, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, // NUMBER 2 + 0, 0, 1, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, // NUMBER 3 + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 1, 1, 1, 0, 0, + 0, 0, 1, 1, 1, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, // NUMBER 4 + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, // NUMBER 5 + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, // NUMBER 6 + 0, 0, 1, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, // NUMBER 7 + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 1, 1, 1, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, // NUMBER 8 + 0, 0, 1, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 0, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, // NUMBER 9 + 0, 0, 1, 1, 1, 1, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 1, 1 ,0, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; diff --git a/inc/tga.h b/inc/tga.h new file mode 100644 index 0000000..4b449f4 --- /dev/null +++ b/inc/tga.h @@ -0,0 +1 @@ +void CreateTGA( char *filename, short width, short height, void *image ); diff --git a/inc/upc.h b/inc/upc.h new file mode 100644 index 0000000..fd0cf01 --- /dev/null +++ b/inc/upc.h @@ -0,0 +1,2 @@ +const char* create_upc_a( char *input ); +char upc_a_calculate_check_digit( char *digits ); diff --git a/obj/.gitignore b/obj/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/obj/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore @@ -0,0 +1,11 @@ + UPC-A Barcode Image Generator Program "upc", by Tyler Delgadillo + + This program converts an 11-digit number into a TGA image file representing a UPC-A barcode. + + Invoke at the command line. This program expects an argument in the form of an 11-digit number. +You may include spaces to delimit the number system digit, manufacturer code, and product code. +Additionally, the check digit is calculated automatically. + + Command Line Options: + -o : specify output filename, defaults to "img.tga" + -n : include human-readable numeric digits in the image diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..b5e115e --- /dev/null +++ b/src/main.c @@ -0,0 +1,137 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "./num.h" +#include "./tga.h" +#include "./upc.h" + +#define UPC_A_WIDTH 113 +#define UPC_A_HEIGHT 79 + +typedef struct { + int eflag; + int nflag; + char *ovalue; +} opts; + +char *getInput( int index, char *argv[] ); +opts parseOpts( int argc, char *argv[] ); + +void DrawBarcode( char *dst, const char *src ); +void DrawNumbers( char *dst, const char *src ); + +int main(int argc, char *argv[]) { + + // Parse CLI options + opts options = parseOpts( argc, argv ); + + // Parse CLI input + char *input = getInput( optind, argv ); + + // Set final element of input array to the appropriate check digit + input[11] = upc_a_calculate_check_digit( input ); + + // Create array of chars to represent 1D barcode + const char *barcode = create_upc_a( input ); + + // Allocate memory to hold image data + char *image = malloc( UPC_A_WIDTH * UPC_A_HEIGHT ); + + // Draw barcode to image data + DrawBarcode( image, barcode ); + + // Draw human-readable interpretation if '-e' is used + if( options.nflag ) { + DrawNumbers( image, input ); + } + + free( input ); + + // Create a .tga image file using image data + CreateTGA( options.ovalue, UPC_A_WIDTH, UPC_A_HEIGHT, image ); + + free( image ); + + return 0; +} + + +char *getInput( int index, char *argv[] ) { + + char *result = malloc( 12 ); + + int k = 0; + + for( int i = index; argv[i] != NULL; ++i ) { + for( int j = 0; j < strlen(argv[i]); ++j ) { + if( argv[i][j] < 58 && argv[i][j] > 47 ) { + result[k] = argv[i][j]; + result[k] -= 48; + ++k; + } + } + } + if( k != 11 ) { + printf( "Inappropriate input detected!\nPlease provide an 11-digit number.\n" ); + } + + return result; +} + +opts parseOpts( int argc, char *argv[] ) { + + opts options = { 0, 0, NULL }; + int c; + while( ( c = getopt(argc, argv, "eno:")) != -1 ) { + switch( c ) { + case 'e': + options.eflag = 1; + break; + case 'n': + options.nflag = 1; + break; + case 'o': + options.ovalue = optarg; + break; + } + } + return options; +} + +void DrawBarcode( char *dst, const char *src ) { + + const char UPC_A_QUIET_ZONE[9] = {0}; + + for(int i = 0; i < UPC_A_HEIGHT; ++i) { + memcpy( dst + (i * UPC_A_WIDTH), &UPC_A_QUIET_ZONE, sizeof(UPC_A_QUIET_ZONE) ); + memcpy( dst + 9 + (i * UPC_A_WIDTH), src, 95 ); + memcpy( dst + 104 + (i * UPC_A_WIDTH), &UPC_A_QUIET_ZONE, sizeof(UPC_A_QUIET_ZONE) ); + } + +} + +void DrawNumbers( char *dst, const char *src ) { + const int POSITION_0 = UPC_A_WIDTH * (UPC_A_HEIGHT - 16) + 1; + const int POSITION_1 = UPC_A_WIDTH * (UPC_A_HEIGHT - 16) + 14; + const int POSITION_2 = UPC_A_WIDTH * (UPC_A_HEIGHT - 16) + 59; + const int POSITION_3 = UPC_A_WIDTH * (UPC_A_HEIGHT - 16) + 104; + + for(int i = 0; i < 16; ++i) { + memcpy( dst + POSITION_0 + (i * UPC_A_WIDTH), NUMBER[src[0]][i], sizeof(NUMBER[0][i]) ); + } + for( int i = 0; i < 5; ++i ) { + for(int j = 0; j < 16; ++j) { + memcpy( dst + POSITION_1 + (j * UPC_A_WIDTH) + (i * 8), NUMBER[src[i + 1]][j], sizeof(NUMBER[0][j]) ); + } + } + for( int i = 0; i < 5; ++i ) { + for(int j = 0; j < 16; ++j) { + memcpy( dst + POSITION_2 + (j * UPC_A_WIDTH) + (i * 8), NUMBER[src[i + 6]][j], sizeof(NUMBER[0][j]) ); + } + } + for(int i = 0; i < 16; ++i) { + memcpy( dst + POSITION_3 + (i * UPC_A_WIDTH), NUMBER[src[11]][i], sizeof(NUMBER[0][i]) ); + } +} diff --git a/src/tga.c b/src/tga.c new file mode 100644 index 0000000..95a82d6 --- /dev/null +++ b/src/tga.c @@ -0,0 +1,55 @@ +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void CreateTGA( char *filename, short width, short height, void *image ) { + + // create file pointer to write to + FILE *tga = NULL; + + // write filename from func arg to buffer + char buffer[256]; + if( filename ) { + strncpy( buffer, filename, sizeof(buffer) ); + } + // if no filename is provided, set default + else { + strcpy( buffer, "img.tga" ); + } + + // open the named file for binary writing + tga = fopen( buffer, "wb" ); + + // set 'id length' field of tga header + fputc( 0, tga ); + + + // set 'color map type' field of tga header + fputc( 1, tga ); // color map present + + // set 'image type' field of tga header + fputc( 1, tga ); // uncompressed color mapped image + + // set 'color map specification' field of tga header + fputc( 0, tga ); fputc( 0, tga ); // 2 byte 'first entry index' subfield, index 0 + fputc( 2, tga ); fputc( 0, tga ); // 2 byte 'color map length' subfield, 2 entries + fputc( 24, tga ); // 'color map entry size' subfield, 24-bit color + + // set 'image specification' field of tga header + fputc( 0, tga ); fputc( 0, tga ); // 2 byte 'x-origin' subfield, origin 0 + fputc( 0, tga ); fputc( 0, tga ); // 2 byte 'y-origin' subfield, origin 0 + fputc( width & 0xff, tga ); fputc( width >> 8, tga ); // 2 byte 'width' subfield, set by function argument 'w' + fputc( height & 0xff, tga ); fputc( height >> 8, tga ); // 2 byte 'height' subfield, set by function argument 'h' + fputc( 8, tga ); // 'pixel depth' subfield, 8 bits per pixel + fputc( 0x20, tga ); // 'image descriptor' subfield, bit 5 set, indicating top-to-bottom, left-to-right order + + // set 'image and color map data' field + fputc( 0xff, tga ); fputc( 0xff, tga ); fputc( 0xff, tga ); // set first color map entry, 0xffffff + fputc( 0x00, tga ); fputc( 0x00, tga ); fputc( 0x00, tga ); // set second color map entry, 0x000000 + + // write image data + fwrite( image, 1, (width * height), tga ); + + fclose( tga ); +} diff --git a/src/upc.c b/src/upc.c new file mode 100644 index 0000000..a81979b --- /dev/null +++ b/src/upc.c @@ -0,0 +1,113 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define UPC_A_WIDTH 95 +#define UPC_A_NUMDIGITS 12 + +const char UPC_A_GUARD_PATTERN_SE[3] = { 1, 0, 1 }; +const char UPC_A_GUARD_PATTERN_M[5] = { 0, 1, 0, 1, 0 }; + +const char UPC_DIGIT[10][7] = { { 0, 0, 0, 1, 1, 0, 1 }, + { 0, 0, 1, 1, 0, 0, 1 }, + { 0, 0, 1, 0, 0, 1, 1 }, + { 0, 1, 1, 1, 1, 0, 1 }, + { 0, 1, 0, 0, 0, 1, 1 }, + { 0, 1, 1, 0, 0, 0, 1 }, + { 0, 1, 0, 1, 1, 1, 1 }, + { 0, 1, 1, 1, 0, 1, 1 }, + { 0, 1, 1, 0, 1, 1, 1 }, + { 0, 0, 0, 1, 0, 1, 1 } }; + +static char upc_calculate_check_digit( char *digits ); +static void upc_draw_pattern( char buffer[], int *index, const char *pattern, int pattern_size ); +static void upc_draw_digits( char *buffer, int *index, char *digits, char isLeft ); + +const char* create_upc_a( char *input ) { + + // allocate 95 bytes of memory for resultant data + char *result = malloc(UPC_A_WIDTH); + // create a char array to store the GTIN-12 number + char digits[UPC_A_NUMDIGITS]; + + int index = 0; + + // copy digits from function param to local array + memcpy( digits, input, UPC_A_NUMDIGITS ); + + // set the final digit to the appropriate check digit + // digits[11] = upc_calculate_check_digit( digits ); + + // draw start guard pattern + upc_draw_pattern( result, &index, UPC_A_GUARD_PATTERN_SE, sizeof(UPC_A_GUARD_PATTERN_SE) ); + + // draw LLLLLL digits + upc_draw_digits( result, &index, digits, 1 ); + + // draw middle guard pattern + upc_draw_pattern( result, &index, UPC_A_GUARD_PATTERN_M, sizeof(UPC_A_GUARD_PATTERN_M) ); + + // draw RRRRRR digits + upc_draw_digits( result, &index, digits, 0 ); + + // draw end guard pattern + upc_draw_pattern( result, &index, UPC_A_GUARD_PATTERN_SE, sizeof(UPC_A_GUARD_PATTERN_SE) ); + + return result; +} + +char upc_a_calculate_check_digit( char *digits ) { + + char result = 0; + int M; + + // 1. Sum digits at odd-numbered positions + for( int i = 0; i < UPC_A_NUMDIGITS; ++i ) { + if( i % 2 == 0 ) { result += digits[i]; } + } + // 2. Multiply result by 3 + result *= 3; + // 3. Add digits at even-numbered positions to the result + for( int i = 0; i < UPC_A_NUMDIGITS; ++i ) { + if( i % 2 != 0 ) { result += digits[i]; } + } + // 4. Find the result mod 10 and call it "M" + M = result % 10; + // 5. If M is zero, the check digit is 0, + if( M == 0 ) { result = M; } + // otherwise the check digit is 10 - M + else { + result = 10 - M; + } + + return result; +} + +static void upc_draw_pattern( char buffer[], int *index, const char *pattern, int pattern_size ) { + + for( int i = 0; i < pattern_size; ++i ) { + buffer[*index] = pattern[i]; + ++*index; + } +} + +static void upc_draw_digits( char *buffer, int *index, char *digits, char isLeft ) { + + char temp[6][7]; + if ( isLeft ) { + for( int i = 0; i < 6; ++i ) { + memcpy(temp[i], UPC_DIGIT[digits[i]], sizeof( *UPC_DIGIT ) ); + upc_draw_pattern( buffer, index, temp[i], sizeof( *UPC_DIGIT ) ); + } + } + else { + for( int i = 0; i < 6; ++i ) { + memcpy(temp[i], UPC_DIGIT[digits[i + 6]], sizeof( *UPC_DIGIT ) ); + //invert values of RRRRRR digits + for( int j = 0; j < sizeof( *UPC_DIGIT ); ++j ) { + temp[i][j] = !temp[i][j]; + } + upc_draw_pattern( buffer, index, temp[i], sizeof( *UPC_DIGIT ) ); + } + } +} |