diff --git a/backend/gif.c b/backend/gif.c index f341f188..5cfbe891 100644 --- a/backend/gif.c +++ b/backend/gif.c @@ -41,7 +41,8 @@ #include #endif -#define SSET "0123456789ABCDEF" +/* Limit initial ZLW buffer size to this in expectation that compressed data will fit for typical scalings */ +#define GIF_ZLW_PAGE_SIZE 0x100000 /* Megabyte */ typedef struct s_statestruct { unsigned char *pOut; @@ -95,8 +96,15 @@ static int BufferNextByte(statestruct *pState) { pState->OutByteCountPos = pState->OutPosCur; (pState->OutPosCur)++; } - if (pState->OutPosCur >= pState->OutLength) - return 1; + if (pState->OutPosCur >= pState->OutLength) { + unsigned char *pOut; + pState->OutLength += GIF_ZLW_PAGE_SIZE; + /* Note pState->pOut not free()d by realloc() on failure */ + if (!(pOut = (unsigned char *) realloc(pState->pOut, pState->OutLength))) { + return 1; + } + pState->pOut = pOut; + } (pState->pOut)[pState->OutPosCur] = 0x00; return 0; @@ -301,15 +309,9 @@ INTERNAL int gif_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf) /* Allow for overhead of 4 == code size + byte count + overflow byte + zero terminator */ unsigned int lzoutbufSize = bitmapSize + 4; -#ifdef _MSC_VER - char *lzwoutbuf; -#endif - -#ifndef _MSC_VER - char lzwoutbuf[lzoutbufSize]; -#else - lzwoutbuf = (char *) _alloca(lzoutbufSize); -#endif /* _MSC_VER */ + if (lzoutbufSize > GIF_ZLW_PAGE_SIZE) { + lzoutbufSize = GIF_ZLW_PAGE_SIZE; + } /* * Build a table of the used palette items. @@ -582,19 +584,27 @@ INTERNAL int gif_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf) /* prepare state array */ State.pIn = pixelbuf; State.InLen = bitmapSize; - State.pOut = (unsigned char *) lzwoutbuf; + if (!(State.pOut = (unsigned char *) malloc(lzoutbufSize))) { + if (!output_to_stdout) { + fclose(gif_file); + } + strcpy(symbol->errtxt, "614: Insufficient memory for LZW buffer"); + return ZINT_ERROR_MEMORY; + } State.OutLength = lzoutbufSize; /* call lzw encoding */ byte_out = gif_lzw(&State, paletteBitSize); if (byte_out <= 0) { + free(State.pOut); if (!output_to_stdout) { fclose(gif_file); } strcpy(symbol->errtxt, "613: Insufficient memory for LZW buffer"); return ZINT_ERROR_MEMORY; } - fwrite(lzwoutbuf, byte_out, 1, gif_file); + fwrite((const char *) State.pOut, byte_out, 1, gif_file); + free(State.pOut); /* GIF terminator */ fputc('\x3b', gif_file); diff --git a/backend/tests/data/gif/itf14_height61.8_bind4_wsp24_3.gif b/backend/tests/data/gif/itf14_height61.8_bind4_wsp24_3.gif new file mode 100644 index 00000000..f8bfb1ee Binary files /dev/null and b/backend/tests/data/gif/itf14_height61.8_bind4_wsp24_3.gif differ diff --git a/backend/tests/test_gif.c b/backend/tests/test_gif.c index 763cc69e..6e84e33a 100644 --- a/backend/tests/test_gif.c +++ b/backend/tests/test_gif.c @@ -129,37 +129,40 @@ static void test_print(int index, int generate, int debug) { int whitespace_height; int option_1; int option_2; + float height; float scale; float dot_size; char *fgcolour; char *bgcolour; char *data; char *expected_file; + char *comment; }; struct item data[] = { - /* 0*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 0, "", "", "12", "dotcode_1.0.gif" }, - /* 1*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 0.1, "", "", "12", "dotcode_1.0_ds0.1.gif" }, - /* 2*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 1.1, "", "", "12", "dotcode_1.0_ds1.1.gif" }, - /* 3*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 1.5, 0, "", "", "12", "dotcode_1.5.gif" }, - /* 4*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 1.5, 0.4, "", "", "12", "dotcode_1.5_ds0.4.gif" }, - /* 5*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 1.5, 1.1, "", "", "12", "dotcode_1.5_ds1.1.gif" }, - /* 6*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 1.5, 2.1, "", "", "12", "dotcode_1.5_ds2.1.gif" }, - /* 7*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 2, 0, "", "", "12", "dotcode_2.0.gif" }, - /* 8*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 2, 0.9, "", "", "12", "dotcode_2.0_ds0.9.gif" }, - /* 9*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 2, 1.1, "", "", "12", "dotcode_2.0_ds1.1.gif" }, - /* 10*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 3, 0, "", "", "12", "dotcode_3.0.gif" }, - /* 11*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 3, 0.4, "", "", "12", "dotcode_3.0_ds0.4.gif" }, - /* 12*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 3, 1.1, "", "", "12", "dotcode_3.0_ds1.1.gif" }, - /* 13*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 3.5, 0, "", "", "12", "dotcode_3.5.gif" }, - /* 14*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 3.5, 0.4, "", "", "12", "dotcode_3.5_ds0.4.gif" }, - /* 15*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 3.5, 1.1, "", "", "12", "dotcode_3.5_ds1.1.gif" }, - /* 16*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 5, 0, "", "", "12", "dotcode_5.0.gif" }, - /* 17*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 5, 0.2, "", "", "12", "dotcode_5.0_ds0.2.gif" }, - /* 18*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 5, 1.1, "", "", "12", "dotcode_5.0_ds1.1.gif" }, - /* 19*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 5, 1.7, "", "", "12", "dotcode_5.0_ds1.7.gif" }, - /* 20*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 0, "2674C344", "FDFFC2CC", "12", "dotcode_bgfgalpha.gif" }, - /* 21*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 0, "00000000", "FFFFFF00", "12", "dotcode_bgfgtrans.gif" }, - /* 22*/ { BARCODE_ULTRA, 1, BARCODE_BOX, 1, 1, -1, -1, 0, 0, "0000FF", "FF0000", "12", "ultra_fgbg_hvwsp1_box1.gif" }, + /* 0*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 0, 0, "", "", "12", "dotcode_1.0.gif", "" }, + /* 1*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 0, 0.1, "", "", "12", "dotcode_1.0_ds0.1.gif", "" }, + /* 2*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 0, 1.1, "", "", "12", "dotcode_1.0_ds1.1.gif", "" }, + /* 3*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 1.5, 0, "", "", "12", "dotcode_1.5.gif", "" }, + /* 4*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 1.5, 0.4, "", "", "12", "dotcode_1.5_ds0.4.gif", "" }, + /* 5*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 1.5, 1.1, "", "", "12", "dotcode_1.5_ds1.1.gif", "" }, + /* 6*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 1.5, 2.1, "", "", "12", "dotcode_1.5_ds2.1.gif", "" }, + /* 7*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 2, 0, "", "", "12", "dotcode_2.0.gif", "" }, + /* 8*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 2, 0.9, "", "", "12", "dotcode_2.0_ds0.9.gif", "" }, + /* 9*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 2, 1.1, "", "", "12", "dotcode_2.0_ds1.1.gif", "" }, + /* 10*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 3, 0, "", "", "12", "dotcode_3.0.gif", "" }, + /* 11*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 3, 0.4, "", "", "12", "dotcode_3.0_ds0.4.gif", "" }, + /* 12*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 3, 1.1, "", "", "12", "dotcode_3.0_ds1.1.gif", "" }, + /* 13*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 3.5, 0, "", "", "12", "dotcode_3.5.gif", "" }, + /* 14*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 3.5, 0.4, "", "", "12", "dotcode_3.5_ds0.4.gif", "" }, + /* 15*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 3.5, 1.1, "", "", "12", "dotcode_3.5_ds1.1.gif", "" }, + /* 16*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 5, 0, "", "", "12", "dotcode_5.0.gif", "" }, + /* 17*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 5, 0.2, "", "", "12", "dotcode_5.0_ds0.2.gif", "" }, + /* 18*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 5, 1.1, "", "", "12", "dotcode_5.0_ds1.1.gif", "" }, + /* 19*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 5, 1.7, "", "", "12", "dotcode_5.0_ds1.7.gif", "" }, + /* 20*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 0, 0, "2674C344", "FDFFC2CC", "12", "dotcode_bgfgalpha.gif", "" }, + /* 21*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 0, 0, "00000000", "FFFFFF00", "12", "dotcode_bgfgtrans.gif", "" }, + /* 22*/ { BARCODE_ULTRA, 1, BARCODE_BOX, 1, 1, -1, -1, 0, 0, 0, "0000FF", "FF0000", "12", "ultra_fgbg_hvwsp1_box1.gif", "" }, + /* 23*/ { BARCODE_ITF14, 4, BARCODE_BIND, 24, -1, -1, -1, 61.8, 3, 0, "", "", "0501054800395", "itf14_height61.8_bind4_wsp24_3.gif", "#204 ARM-Cortex crash" }, }; int data_size = ARRAY_SIZE(data); int i, length, ret; @@ -201,6 +204,9 @@ static void test_print(int index, int generate, int debug) { if (data[i].whitespace_height != -1) { symbol->whitespace_height = data[i].whitespace_height; } + if (data[i].height) { + symbol->height = data[i].height; + } if (data[i].scale) { symbol->scale = data[i].scale; } @@ -224,11 +230,11 @@ static void test_print(int index, int generate, int debug) { assert_nonzero(testUtilDataPath(expected_file, sizeof(expected_file), data_dir, data[i].expected_file), "i:%d testUtilDataPath == 0\n", i); if (generate) { - printf(" /*%3d*/ { %s, %d, %s, %d, %d, %d, %d, %.5g, %.5g, \"%s\", \"%s\", \"%s\", \"%s\" },\n", + printf(" /*%3d*/ { %s, %d, %s, %d, %d, %d, %d, %.5g, %.5g, %.5g, \"%s\", \"%s\", \"%s\", \"%s\", \"%s\" },\n", i, testUtilBarcodeName(data[i].symbology), data[i].border_width, testUtilOutputOptionsName(data[i].output_options), data[i].whitespace_width, data[i].whitespace_height, - data[i].option_1, data[i].option_2, data[i].scale, data[i].dot_size, data[i].fgcolour, data[i].bgcolour, - testUtilEscape(data[i].data, length, escaped, escaped_size), data[i].expected_file); + data[i].option_1, data[i].option_2, data[i].height, data[i].scale, data[i].dot_size, data[i].fgcolour, data[i].bgcolour, + testUtilEscape(data[i].data, length, escaped, escaped_size), data[i].expected_file, data[i].comment); ret = testUtilRename(symbol->outfile, expected_file); assert_zero(ret, "i:%d testUtilRename(%s, %s) ret %d != 0\n", i, symbol->outfile, expected_file, ret); if (have_identify) { @@ -275,12 +281,43 @@ static void test_outfile(void) { testFinish(); } +static void test_large_scale(int debug) { + int length, ret; + struct zint_symbol symbol = {0}; + char data[] = "1"; + + testStart("test_large_scale"); + + length = (int) strlen(data); + + symbol.symbology = BARCODE_ITF14; + strcpy(symbol.fgcolour, "000000"); + strcpy(symbol.bgcolour, "ffffff"); + strcpy(symbol.outfile, "out.gif"); + // X-dimension 0.27mm * 95 = 25.65 ~ 25 pixels so 12.5 gives 95 dpmm (2400 dpi) + symbol.scale = 12.5f; // 70.0f would cause re-alloc as LZW > 1MB but very slow + symbol.dot_size = 4.0f / 5.0f; + + ret = ZBarcode_Encode_and_Print(&symbol, (unsigned char *) data, length, 0 /*rotate_angle*/); + assert_zero(ret, "%s ZBarcode_Encode_and_Print ret %d != 0 %s\n", testUtilBarcodeName(symbol.symbology), ret, symbol.errtxt); + + if (!(debug & ZINT_DEBUG_TEST_KEEP_OUTFILE)) { /* -d 64 */ + // 129.1 kB file manually inspected and checked (1.1 MB file for scale 70.0f also checked) + assert_zero(remove(symbol.outfile), "remove(%s) != 0\n", symbol.outfile); + } + + ZBarcode_Clear(&symbol); + + testFinish(); +} + int main(int argc, char *argv[]) { testFunction funcs[] = { /* name, func, has_index, has_generate, has_debug */ { "test_pixel_plot", test_pixel_plot, 1, 0, 1 }, { "test_print", test_print, 1, 1, 1 }, { "test_outfile", test_outfile, 0, 0, 0 }, + { "test_large_scale", test_large_scale, 0, 0, 1 }, }; testRun(argc, argv, funcs, ARRAY_SIZE(funcs));