diff --git a/ChangeLog b/ChangeLog index b01380d0..6ee80ec8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,23 @@ Version 2.13.0.9 (dev) not released yet ======================================= +**Incompatible changes** +------------------------ +None so far + +Changes +------- +- BMP: lessen heap memory usage by only `malloc()`ing a row +- GIF: lessen heap memory usage by paging; use standard colour char map + +Bugs +---- +- raster/BMP/GIF/PCX/TIF: fix dealing with very large data (use `size_t`) +- raster: add `raster_malloc()` to fail > 1GB (avoids very large output files; + also lessens to some degree chances of being victim of OOM killer on Linux) +- GUI: printing scale dialog: set maxima on X-dim and resolution to keep scale + <= 200 + Version 2.13.0 (2023-12-18) =========================== diff --git a/backend/bmp.c b/backend/bmp.c index 8f0093d1..4f0271e4 100644 --- a/backend/bmp.c +++ b/backend/bmp.c @@ -43,13 +43,10 @@ INTERNAL int bmp_pixel_plot(struct zint_symbol *symbol, const unsigned char *pixelbuf) { int i, row, column; - int row_size; int bits_per_pixel; int colour_count; int resolution; - unsigned int data_offset, data_size, file_size; - unsigned char *bitmap_file_start, *bmp_posn; - unsigned char *bitmap; + size_t row_size, data_offset, file_size; FILE *bmp_file; bitmap_file_header_t file_header; bitmap_info_header_t info_header; @@ -59,6 +56,7 @@ INTERNAL int bmp_pixel_plot(struct zint_symbol *symbol, const unsigned char *pix int ultra_fg_index = 9; unsigned char map[128]; const int output_to_stdout = symbol->output_options & BARCODE_STDOUT; /* Suppress gcc -fanalyzer warning */ + unsigned char *rowbuf; (void) out_colour_get_rgb(symbol->fgcolour, &fg.red, &fg.green, &fg.blue, NULL /*alpha*/); fg.reserved = 0x00; @@ -86,42 +84,26 @@ INTERNAL int bmp_pixel_plot(struct zint_symbol *symbol, const unsigned char *pix map['0'] = 0; map['1'] = 0x80; } - row_size = 4 * ((bits_per_pixel * symbol->bitmap_width + 31) / 32); - data_size = symbol->bitmap_height * row_size; + row_size = 4 * (((size_t) symbol->bitmap_width * bits_per_pixel + 31) / 32); data_offset = sizeof(bitmap_file_header_t) + sizeof(bitmap_info_header_t); - data_offset += colour_count * sizeof(color_ref_t); - file_size = data_offset + data_size; + data_offset += sizeof(color_ref_t) * colour_count; + file_size = data_offset + row_size * symbol->bitmap_height; - bitmap_file_start = (unsigned char *) malloc(file_size); - if (bitmap_file_start == NULL) { - strcpy(symbol->errtxt, "602: Insufficient memory for BMP file buffer"); + /* Must fit in `uint32_t` field in header */ + if (file_size != (uint32_t) file_size) { + strcpy(symbol->errtxt, "606: Output file size too large for BMP header"); return ZINT_ERROR_MEMORY; } - memset(bitmap_file_start, 0, file_size); /* Not required but keeps padding bytes consistent */ - bitmap = bitmap_file_start + data_offset; - - /* Pixel Plotting */ - if (bits_per_pixel == 4) { - for (row = 0; row < symbol->bitmap_height; row++) { - const unsigned char *pb = pixelbuf + (symbol->bitmap_width * (symbol->bitmap_height - row - 1)); - for (column = 0; column < symbol->bitmap_width; column++) { - bitmap[(column >> 1) + (row * row_size)] |= map[pb[column]] << (!(column & 1) << 2); - } - } - } else { /* bits_per_pixel == 1 */ - for (row = 0; row < symbol->bitmap_height; row++) { - const unsigned char *pb = pixelbuf + (symbol->bitmap_width * (symbol->bitmap_height - row - 1)); - for (column = 0; column < symbol->bitmap_width; column++) { - bitmap[(column >> 3) + (row * row_size)] |= map[pb[column]] >> (column & 7); - } - } + if (!(rowbuf = (unsigned char *) malloc(row_size))) { + strcpy(symbol->errtxt, "602: Insufficient memory for BMP row buffer"); + return ZINT_ERROR_MEMORY; } file_header.header_field = 0x4d42; /* "BM" */ - file_header.file_size = file_size; + file_header.file_size = (uint32_t) file_size; file_header.reserved = 0; - file_header.data_offset = data_offset; + file_header.data_offset = (uint32_t) data_offset; info_header.header_size = sizeof(bitmap_info_header_t); info_header.width = symbol->bitmap_width; @@ -136,34 +118,12 @@ INTERNAL int bmp_pixel_plot(struct zint_symbol *symbol, const unsigned char *pix info_header.colours = colour_count; info_header.important_colours = colour_count; - bmp_posn = bitmap_file_start; - memcpy(bitmap_file_start, &file_header, sizeof(bitmap_file_header_t)); - bmp_posn += sizeof(bitmap_file_header_t); - memcpy(bmp_posn, &info_header, sizeof(bitmap_info_header_t)); - bmp_posn += sizeof(bitmap_info_header_t); - - memcpy(bmp_posn, &bg, sizeof(color_ref_t)); - bmp_posn += sizeof(color_ref_t); - if (bits_per_pixel == 4) { - for (i = 0; i < 8; i++) { - memcpy(bmp_posn, &palette[i], sizeof(color_ref_t)); - bmp_posn += sizeof(color_ref_t); - } - if (ultra_fg_index == 9) { - memcpy(bmp_posn, &fg, sizeof(color_ref_t)); - /* bmp_posn += sizeof(color_ref_t); */ /* Not needed as long as last */ - } - } else { - memcpy(bmp_posn, &fg, sizeof(color_ref_t)); - /* bmp_posn += sizeof(color_ref_t); */ /* Not needed as long as last */ - } - /* Open output file in binary mode */ if (output_to_stdout) { #ifdef _MSC_VER if (-1 == _setmode(_fileno(stdout), _O_BINARY)) { sprintf(symbol->errtxt, "600: Could not set stdout to binary (%d: %.30s)", errno, strerror(errno)); - free(bitmap_file_start); + free(rowbuf); return ZINT_ERROR_FILE_ACCESS; } #endif @@ -171,16 +131,50 @@ INTERNAL int bmp_pixel_plot(struct zint_symbol *symbol, const unsigned char *pix } else { if (!(bmp_file = out_fopen(symbol->outfile, "wb"))) { sprintf(symbol->errtxt, "601: Could not open output file (%d: %.30s)", errno, strerror(errno)); - free(bitmap_file_start); + free(rowbuf); return ZINT_ERROR_FILE_ACCESS; } } - fwrite(bitmap_file_start, file_header.file_size, 1, bmp_file); + fwrite(&file_header, sizeof(bitmap_file_header_t), 1, bmp_file); + fwrite(&info_header, sizeof(bitmap_info_header_t), 1, bmp_file); + + fwrite(&bg, sizeof(color_ref_t), 1, bmp_file); + if (bits_per_pixel == 4) { + for (i = 0; i < 8; i++) { + fwrite(&palette[i], sizeof(color_ref_t), 1, bmp_file); + } + if (ultra_fg_index == 9) { + fwrite(&fg, sizeof(color_ref_t), 1, bmp_file); + } + } else { + fwrite(&fg, sizeof(color_ref_t), 1, bmp_file); + } + + /* Pixel Plotting */ + if (bits_per_pixel == 4) { + for (row = 0; row < symbol->bitmap_height; row++) { + const unsigned char *pb = pixelbuf + ((size_t) symbol->bitmap_width * (symbol->bitmap_height - row - 1)); + memset(rowbuf, 0, row_size); + for (column = 0; column < symbol->bitmap_width; column++) { + rowbuf[column >> 1] |= map[pb[column]] << (!(column & 1) << 2); + } + fwrite(rowbuf, 1, row_size, bmp_file); + } + } else { /* bits_per_pixel == 1 */ + for (row = 0; row < symbol->bitmap_height; row++) { + const unsigned char *pb = pixelbuf + ((size_t) symbol->bitmap_width * (symbol->bitmap_height - row - 1)); + memset(rowbuf, 0, row_size); + for (column = 0; column < symbol->bitmap_width; column++) { + rowbuf[column >> 3] |= map[pb[column]] >> (column & 7); + } + fwrite(rowbuf, 1, row_size, bmp_file); + } + } + free(rowbuf); if (ferror(bmp_file)) { sprintf(symbol->errtxt, "603: Incomplete write to output (%d: %.30s)", errno, strerror(errno)); - free(bitmap_file_start); if (!output_to_stdout) { (void) fclose(bmp_file); } @@ -190,18 +184,15 @@ INTERNAL int bmp_pixel_plot(struct zint_symbol *symbol, const unsigned char *pix if (output_to_stdout) { if (fflush(bmp_file) != 0) { sprintf(symbol->errtxt, "604: Incomplete flush to output (%d: %.30s)", errno, strerror(errno)); - free(bitmap_file_start); return ZINT_ERROR_FILE_WRITE; } } else { if (fclose(bmp_file) != 0) { sprintf(symbol->errtxt, "605: Failure on closing output file (%d: %.30s)", errno, strerror(errno)); - free(bitmap_file_start); return ZINT_ERROR_FILE_WRITE; } } - free(bitmap_file_start); return 0; } diff --git a/backend/gif.c b/backend/gif.c index 64dbcd8c..245e15ce 100644 --- a/backend/gif.c +++ b/backend/gif.c @@ -43,47 +43,35 @@ #define GIF_LZW_PAGE_SIZE 0x100000 /* Megabyte */ typedef struct s_statestruct { + FILE *file; unsigned char *pOut; const unsigned char *pIn; - unsigned int InLen; - unsigned int OutLength; - unsigned int OutPosCur; - unsigned int OutByteCountPos; + const unsigned char *pInEnd; + size_t OutLength; + size_t OutPosCur; + size_t OutByteCountPos; unsigned short ClearCode; unsigned short FreeCode; char fByteCountByteSet; + char fOutPaged; unsigned char OutBitsFree; unsigned short NodeAxon[4096]; unsigned short NodeNext[4096]; unsigned char NodePix[4096]; - unsigned char colourCode[10]; - unsigned char colourPaletteIndex[10]; - int colourCount; + unsigned char map[256]; } statestruct; -/* Transform a Pixel to a lzw colourmap index and move to next pixel. - * All colour values are listed in colourCode with corresponding palette index - */ -static unsigned char NextPaletteIndex(statestruct *pState) -{ - unsigned char pixelColour; - int colourIndex; - pixelColour = *(pState->pIn); - (pState->pIn)++; - (pState->InLen)--; - for (colourIndex = 0; colourIndex < pState->colourCount; colourIndex++) { - if (pixelColour == pState->colourCode[colourIndex]) - return pState->colourPaletteIndex[colourIndex]; - - } - return 0; /* Not reached */ -} - - -static int BufferNextByte(statestruct *pState) { +static void BufferNextByte(statestruct *pState) { (pState->OutPosCur)++; + if (pState->fOutPaged && pState->OutPosCur + 2 >= pState->OutLength) { + /* Keep last 256 bytes so `OutByteCountPos` within range */ + fwrite(pState->pOut, 1, pState->OutPosCur - 256, pState->file); + memmove(pState->pOut, pState->pOut + pState->OutPosCur - 256, 256); + pState->OutByteCountPos -= pState->OutPosCur - 256; + pState->OutPosCur = 256; + } /* Check if this position is a byte count position - * fg_f_bytecountbyte_set indicates, if byte count position bytes should be + * `fByteCountByteSet` indicates, if byte count position bytes should be * inserted in general. * If this is true, and the distance to the last byte count position is 256 * (e.g. 255 bytes in between), a byte count byte is inserted, and the value @@ -94,34 +82,22 @@ static int BufferNextByte(statestruct *pState) { pState->OutByteCountPos = pState->OutPosCur; (pState->OutPosCur)++; } - if (pState->OutPosCur >= pState->OutLength) { - unsigned char *pOut; - pState->OutLength += GIF_LZW_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; } -static int AddCodeToBuffer(statestruct *pState, unsigned short CodeIn, unsigned char CodeBits) { +static void AddCodeToBuffer(statestruct *pState, unsigned short CodeIn, unsigned char CodeBits) { /* Check, if we may fill up the current byte completely */ if (CodeBits >= pState->OutBitsFree) { (pState->pOut)[pState->OutPosCur] |= (unsigned char) (CodeIn << (8 - pState->OutBitsFree)); - if (BufferNextByte(pState)) - return -1; + BufferNextByte(pState); CodeIn = (unsigned short) (CodeIn >> pState->OutBitsFree); CodeBits -= pState->OutBitsFree; pState->OutBitsFree = 8; /* Write a full byte if there are at least 8 code bits left */ if (CodeBits >= pState->OutBitsFree) { (pState->pOut)[pState->OutPosCur] = (unsigned char) CodeIn; - if (BufferNextByte(pState)) - return -1; + BufferNextByte(pState); CodeIn = (unsigned short) (CodeIn >> 8); CodeBits -= 8; } @@ -131,7 +107,6 @@ static int AddCodeToBuffer(statestruct *pState, unsigned short CodeIn, unsigned (pState->pOut)[pState->OutPosCur] |= (unsigned char) (CodeIn << (8 - pState->OutBitsFree)); pState->OutBitsFree -= CodeBits; } - return 0; } static void FlushStringTable(statestruct *pState) { @@ -158,21 +133,22 @@ static int NextCode(statestruct *pState, unsigned char *pPixelValueCur, unsigned unsigned short DownNode; /* start with the root node for last pixel chain */ UpNode = *pPixelValueCur; - if ((pState->InLen) == 0) - return AddCodeToBuffer(pState, UpNode, CodeBits); - - *pPixelValueCur = NextPaletteIndex(pState); + if (pState->pIn == pState->pInEnd) { + AddCodeToBuffer(pState, UpNode, CodeBits); + return 0; + } + *pPixelValueCur = pState->map[*pState->pIn++]; /* Follow the string table and the data stream to the end of the longest string that has a code */ while (0 != (DownNode = FindPixelOutlet(pState, UpNode, *pPixelValueCur))) { UpNode = DownNode; - if ((pState->InLen) == 0) - return AddCodeToBuffer(pState, UpNode, CodeBits); - - *pPixelValueCur = NextPaletteIndex(pState); + if (pState->pIn == pState->pInEnd) { + AddCodeToBuffer(pState, UpNode, CodeBits); + return 0; + } + *pPixelValueCur = pState->map[*pState->pIn++]; } /* Submit 'UpNode' which is the code of the longest string */ - if (AddCodeToBuffer(pState, UpNode, CodeBits)) - return -1; + AddCodeToBuffer(pState, UpNode, CodeBits); /* ... and extend the string by appending 'PixelValueCur' */ /* Create a successor node for 'PixelValueCur' whose code is 'freecode' */ (pState->NodePix)[pState->FreeCode] = *pPixelValueCur; @@ -196,9 +172,9 @@ static int gif_lzw(statestruct *pState, int paletteBitSize) { unsigned short Pos; /* > Get first data byte */ - if (pState->InLen == 0) + if (pState->pIn == pState->pInEnd) return 0; - PixelValueCur = NextPaletteIndex(pState); + PixelValueCur = pState->map[*pState->pIn++]; /* Number of bits per data item (=pixel) * We need at least a value of 2, otherwise the cc and eoi code consumes * the whole string table @@ -211,12 +187,9 @@ static int gif_lzw(statestruct *pState, int paletteBitSize) { pState->ClearCode = (1 << paletteBitSize); pState->FreeCode = pState->ClearCode + 2; pState->OutBitsFree = 8; - pState->OutPosCur = -1; + pState->OutPosCur = 0; pState->fByteCountByteSet = 0; - if (BufferNextByte(pState)) - return 0; - for (Pos = 0; Pos < pState->ClearCode; Pos++) (pState->NodePix)[Pos] = (unsigned char) Pos; @@ -225,31 +198,21 @@ static int gif_lzw(statestruct *pState, int paletteBitSize) { /* Write what the GIF specification calls the "code size". */ (pState->pOut)[pState->OutPosCur] = paletteBitSize; /* Reserve first bytecount byte */ - if (BufferNextByte(pState)) - return 0; + BufferNextByte(pState); pState->OutByteCountPos = pState->OutPosCur; - if (BufferNextByte(pState)) - return 0; + BufferNextByte(pState); pState->fByteCountByteSet = 1; /* Submit one 'ClearCode' as the first code */ - if (AddCodeToBuffer(pState, pState->ClearCode, CodeBits)) - return 0; + AddCodeToBuffer(pState, pState->ClearCode, CodeBits); for (;;) { - int Res; /* generate and save the next code, which may consist of multiple input pixels. */ - Res = NextCode(pState, &PixelValueCur, CodeBits); - if (Res < 0) - return 0; - /* Check for end of data stream */ - if (!Res) { + if (!NextCode(pState, &PixelValueCur, CodeBits)) { /* Check for end of data stream */ /* submit 'eoi' as the last item of the code stream */ - if (AddCodeToBuffer(pState, (unsigned short) (pState->ClearCode + 1), CodeBits)) - return 0; + AddCodeToBuffer(pState, (unsigned short) (pState->ClearCode + 1), CodeBits); pState->fByteCountByteSet = 0; if (pState->OutBitsFree < 8) { - if (BufferNextByte(pState)) - return 0; + BufferNextByte(pState); } /* > Update last bytecount byte; */ if (pState->OutByteCountPos < pState->OutPosCur) { @@ -257,17 +220,16 @@ static int gif_lzw(statestruct *pState, int paletteBitSize) { = (unsigned char) (pState->OutPosCur - pState->OutByteCountPos - 1); } pState->OutPosCur++; - return pState->OutPosCur; + return 1; } /* Check for currently last code */ if (pState->FreeCode == (1U << CodeBits)) CodeBits++; pState->FreeCode++; - /* Check for full stringtable */ + /* Check for full stringtable - for widest compatibility with gif decoders, empty when 0xfff, not 0x1000 */ if (pState->FreeCode == 0xfff) { FlushStringTable(pState); - if (AddCodeToBuffer(pState, pState->ClearCode, CodeBits)) - return 0; + AddCodeToBuffer(pState, pState->ClearCode, CodeBits); CodeBits = (unsigned char) (1 + paletteBitSize); pState->FreeCode = (unsigned short) (pState->ClearCode + 2); @@ -280,13 +242,9 @@ static int gif_lzw(statestruct *pState, int paletteBitSize) { */ INTERNAL int gif_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf) { unsigned char outbuf[10]; - FILE *gif_file; unsigned short usTemp; - int byte_out; - int colourCount; unsigned char paletteRGB[10][3]; - int paletteCount, paletteCountCur, paletteIndex; - unsigned int pixelIndex; + int paletteCount, i; int paletteBitSize; int paletteSize; statestruct State; @@ -294,30 +252,49 @@ INTERNAL int gif_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf) int bgindex = -1, fgindex = -1; const int output_to_stdout = symbol->output_options & BARCODE_STDOUT; - unsigned char backgroundColourIndex; - unsigned char RGBCur[3]; - unsigned char RGBUnused[3] = {0,0,0}; + static const unsigned char RGBUnused[3] = {0,0,0}; unsigned char RGBfg[3]; unsigned char RGBbg[3]; unsigned char fgalpha; unsigned char bgalpha; - int colourIndex; - - int fFound; - - unsigned char pixelColour; - unsigned int bitmapSize = symbol->bitmap_height * symbol->bitmap_width; - - /* Allow for overhead of 4 == code size + byte count + overflow byte + zero terminator */ - unsigned int lzoutbufSize = bitmapSize + 4; - if (lzoutbufSize > GIF_LZW_PAGE_SIZE) { - lzoutbufSize = GIF_LZW_PAGE_SIZE; - } + const size_t bitmapSize = (size_t) symbol->bitmap_height * symbol->bitmap_width; (void) out_colour_get_rgb(symbol->fgcolour, &RGBfg[0], &RGBfg[1], &RGBfg[2], &fgalpha); (void) out_colour_get_rgb(symbol->bgcolour, &RGBbg[0], &RGBbg[1], &RGBbg[2], &bgalpha); + /* prepare state array */ + State.pIn = pixelbuf; + State.pInEnd = pixelbuf + bitmapSize; + /* Allow for overhead of 4 == code size + byte count + overflow byte + zero terminator */ + State.OutLength = bitmapSize + 4; + State.fOutPaged = State.OutLength > GIF_LZW_PAGE_SIZE; + if (State.fOutPaged) { + State.OutLength = GIF_LZW_PAGE_SIZE; + } + if (!(State.pOut = (unsigned char *) malloc(State.OutLength))) { + strcpy(symbol->errtxt, "614: Insufficient memory for LZW buffer"); + return ZINT_ERROR_MEMORY; + } + + /* Open output file in binary mode */ + if (output_to_stdout) { +#ifdef _MSC_VER + if (-1 == _setmode(_fileno(stdout), _O_BINARY)) { + sprintf(symbol->errtxt, "610: Could not set stdout to binary (%d: %.30s)", errno, strerror(errno)); + free(State.pOut); + return ZINT_ERROR_FILE_ACCESS; + } +#endif + State.file = stdout; + } else { + if (!(State.file = out_fopen(symbol->outfile, "wb"))) { + sprintf(symbol->errtxt, "611: Could not open output file (%d: %.30s)", errno, strerror(errno)); + free(State.pOut); + return ZINT_ERROR_FILE_ACCESS; + } + } + /* * Build a table of the used palette items. * Currently, there are the following 10 colour codes: @@ -332,138 +309,73 @@ INTERNAL int gif_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf) * 'G': green * 'K': black * '0' and '1' may be identical to one of the other values - * - * A data structure is set up as follows: - * state.colourCode: list of colour codes - * paletteIndex: palette index of the corresponding colour code - * There are colourCount entries in the upper lists. - * paletteRGB: RGB value at the palette position - * There are paletteCount entries. - * This value is smaller to colourCount, if multiple colour codes have the - * same RGB value and point to the same palette value. - * Example: - * 0 1 W K are present. 0 is equal to white, while 1 is blue - * The resulting tables are: - * paletteItem: ['0']=0 (white), ['1']=1 (blue), ['W']=0 (white), - * ['K']=2 (black) - * Thus, there are 4 colour codes and 3 palette entries. - */ - colourCount = 0; paletteCount = 0; - /* loop over all pixels */ - for (pixelIndex = 0; pixelIndex < bitmapSize; pixelIndex++) { - fFound = 0; - /* get pixel colour code */ - pixelColour = pixelbuf[pixelIndex]; - /* look, if colour code is already in colour list */ - for (colourIndex = 0; colourIndex < colourCount; colourIndex++) { - if ((State.colourCode)[colourIndex] == pixelColour) { - fFound = 1; - break; + memset(State.map, 0, sizeof(State.map)); + if (symbol->symbology == BARCODE_ULTRA) { + static const unsigned char ultra_chars[8] = { 'W', 'C', 'B', 'M', 'R', 'Y', 'G', 'K' }; + for (i = 0; i < 8; i++) { + State.map[ultra_chars[i]] = i; + out_colour_char_to_rgb(ultra_chars[i], &paletteRGB[i][0], &paletteRGB[i][1], &paletteRGB[i][2]); + } + paletteCount = 8; + paletteBitSize = 3; + + /* For Ultracode, have foreground only if have bind/box */ + if (symbol->border_width > 0 && (symbol->output_options & (BARCODE_BIND | BARCODE_BOX | BARCODE_BIND_TOP))) { + /* Check whether can re-use black */ + if (RGBfg[0] == 0 && RGBfg[1] == 0 && RGBfg[2] == 0) { + State.map['1'] = fgindex = 7; /* Re-use black */ + } else { + State.map['1'] = fgindex = paletteCount; + memcpy(paletteRGB[paletteCount++], RGBfg, 3); + paletteBitSize = 4; } } - /* If colour is already present, go to next colour code */ - if (fFound) - continue; - /* Colour code not present - add colour code */ - /* Get RGB value */ - switch (pixelColour) { - case '0': /* standard background */ - RGBCur[0] = RGBbg[0]; RGBCur[1] = RGBbg[1]; RGBCur[2] = RGBbg[2]; - break; - case '1': /* standard foreground */ - RGBCur[0] = RGBfg[0]; RGBCur[1] = RGBfg[1]; RGBCur[2] = RGBfg[2]; - break; - default: /* Colour or error case */ - if (!out_colour_char_to_rgb(pixelColour, &RGBCur[0], &RGBCur[1], &RGBCur[2])) { - strcpy(symbol->errtxt, "612: unknown pixel colour"); - return ZINT_ERROR_INVALID_DATA; - } - break; - } - /* Search, if RGB value is already present */ - fFound = 0; - for (paletteIndex = 0; paletteIndex < paletteCount; paletteIndex++) { - if (RGBCur[0] == paletteRGB[paletteIndex][0] - && RGBCur[1] == paletteRGB[paletteIndex][1] - && RGBCur[2] == paletteRGB[paletteIndex][2]) - { - fFound = 1; - break; + /* For Ultracode, have background only if have whitespace/quiet zones */ + if (symbol->whitespace_width > 0 || symbol->whitespace_height > 0 + || ((symbol->output_options & BARCODE_QUIET_ZONES) + && !(symbol->output_options & BARCODE_NO_QUIET_ZONES))) { + /* Check whether can re-use white */ + if (RGBbg[0] == 0xff && RGBbg[1] == 0xff && RGBbg[2] == 0xff && bgalpha == fgalpha) { + State.map['0'] = bgindex = 0; /* Re-use white */ + } else { + State.map['0'] = bgindex = paletteCount; + memcpy(paletteRGB[paletteCount++], RGBbg, 3); + paletteBitSize = 4; } } - /* RGB not present, add it */ - if (!fFound) { - paletteIndex = paletteCount; - paletteRGB[paletteIndex][0] = RGBCur[0]; - paletteRGB[paletteIndex][1] = RGBCur[1]; - - paletteRGB[paletteIndex][2] = RGBCur[2]; - - paletteCount++; - - if (pixelColour == '0') bgindex = paletteIndex; - if (pixelColour == '1') fgindex = paletteIndex; - } - /* Add palette index to current colour code */ - (State.colourCode)[colourCount] = pixelColour; - (State.colourPaletteIndex)[colourCount] = paletteIndex; - colourCount++; + } else { + State.map['0'] = bgindex = 0; + memcpy(paletteRGB[bgindex], RGBbg, 3); + State.map['1'] = fgindex = 1; + memcpy(paletteRGB[fgindex], RGBfg, 3); + paletteCount = 2; + paletteBitSize = 1; } - State.colourCount = colourCount; /* Set transparency */ /* Note: does not allow both transparent foreground and background - * background takes priority */ transparent_index = -1; - if (bgalpha == 0) { + if (bgalpha == 0 && bgindex != -1) { /* Transparent background */ transparent_index = bgindex; - } else if (fgalpha == 0) { + } else if (fgalpha == 0 && fgindex != -1) { /* Transparent foreground */ transparent_index = fgindex; } - /* find palette bit size from palette size*/ - - /* 1,2 -> 1, 3,4 ->2, 5,6,7,8->3 */ - paletteBitSize = 0; - paletteCountCur = paletteCount - 1; - while (paletteCountCur != 0) { - paletteBitSize++; - paletteCountCur >>= 1; - } - /* Minimum is 1 */ - if (paletteBitSize == 0) - paletteBitSize = 1; - /* palette size 2 ^ bit size */ paletteSize = 1 << paletteBitSize; - /* Open output file in binary mode */ - if (output_to_stdout) { -#ifdef _MSC_VER - if (-1 == _setmode(_fileno(stdout), _O_BINARY)) { - sprintf(symbol->errtxt, "610: Could not set stdout to binary (%d: %.30s)", errno, strerror(errno)); - return ZINT_ERROR_FILE_ACCESS; - } -#endif - gif_file = stdout; - } else { - if (!(gif_file = out_fopen(symbol->outfile, "wb"))) { - sprintf(symbol->errtxt, "611: Could not open output file (%d: %.30s)", errno, strerror(errno)); - return ZINT_ERROR_FILE_ACCESS; - } - } - /* GIF signature (6) */ memcpy(outbuf, "GIF87a", 6); if (transparent_index != -1) outbuf[4] = '9'; - fwrite(outbuf, 6, 1, gif_file); + fwrite(outbuf, 1, 6, State.file); /* Screen Descriptor (7) */ /* Screen Width */ usTemp = (unsigned short) symbol->bitmap_width; @@ -479,7 +391,7 @@ INTERNAL int gif_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf) * 1 : Global colour map * 111 : 8 bit colour depth of the palette * 0 : Not ordered in decreasing importance - * xxx : palette bit zize - 1 + * xxx : palette bit size - 1 */ outbuf[4] = (unsigned char) (0xf0 | (0x7 & (paletteBitSize - 1))); @@ -487,23 +399,15 @@ INTERNAL int gif_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf) * Background colour index * Default to 0. If colour code 0 or K is present, it is used as index */ - - backgroundColourIndex = 0; - for (colourIndex = 0; colourIndex < colourCount; colourIndex++) { - if ((State.colourCode)[colourIndex] == '0' || (State.colourCode)[colourIndex] == 'W') { - backgroundColourIndex = (State.colourPaletteIndex)[colourIndex]; - break; - } - } - outbuf[5] = backgroundColourIndex; + outbuf[5] = bgindex == -1 ? 0 : bgindex; /* Byte 7 must be 0x00 */ outbuf[6] = 0x00; - fwrite(outbuf, 7, 1, gif_file); + fwrite(outbuf, 1, 7, State.file); /* Global Color Table (paletteSize*3) */ - fwrite(paletteRGB, 3*paletteCount, 1, gif_file); + fwrite(paletteRGB, 1, 3*paletteCount, State.file); /* add unused palette items to fill palette size */ - for (paletteIndex = paletteCount; paletteIndex < paletteSize; paletteIndex++) { - fwrite(RGBUnused, 3, 1, gif_file); + for (i = paletteCount; i < paletteSize; i++) { + fwrite(RGBUnused, 1, 3, State.file); } /* Graphic control extension (8) */ @@ -531,7 +435,7 @@ INTERNAL int gif_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf) outbuf[6] = (unsigned char) transparent_index; /* Block Terminator */ outbuf[7] = 0; - fwrite(outbuf, 8, 1, gif_file); + fwrite(outbuf, 1, 8, State.file); } /* Image Descriptor */ /* Image separator character = ',' */ @@ -554,51 +458,38 @@ INTERNAL int gif_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf) * There is no local color table if its most significant bit is reset. */ outbuf[9] = 0x00; - fwrite(outbuf, 10, 1, gif_file); - - /* prepare state array */ - State.pIn = pixelbuf; - State.InLen = bitmapSize; - if (!(State.pOut = (unsigned char *) malloc(lzoutbufSize))) { - if (!output_to_stdout) { - (void) fclose(gif_file); - } - strcpy(symbol->errtxt, "614: Insufficient memory for LZW buffer"); - return ZINT_ERROR_MEMORY; - } - State.OutLength = lzoutbufSize; + fwrite(outbuf, 1, 10, State.file); /* call lzw encoding */ - byte_out = gif_lzw(&State, paletteBitSize); - if (byte_out <= 0) { + if (!gif_lzw(&State, paletteBitSize)) { free(State.pOut); if (!output_to_stdout) { - (void) fclose(gif_file); + (void) fclose(State.file); } strcpy(symbol->errtxt, "613: Insufficient memory for LZW buffer"); return ZINT_ERROR_MEMORY; } - fwrite((const char *) State.pOut, byte_out, 1, gif_file); + fwrite((const char *) State.pOut, 1, State.OutPosCur, State.file); free(State.pOut); /* GIF terminator */ - fputc('\x3b', gif_file); + fputc('\x3b', State.file); - if (ferror(gif_file)) { + if (ferror(State.file)) { sprintf(symbol->errtxt, "615: Incomplete write to output (%d: %.30s)", errno, strerror(errno)); if (!output_to_stdout) { - (void) fclose(gif_file); + (void) fclose(State.file); } return ZINT_ERROR_FILE_WRITE; } if (output_to_stdout) { - if (fflush(gif_file) != 0) { + if (fflush(State.file) != 0) { sprintf(symbol->errtxt, "616: Incomplete flush to output (%d: %.30s)", errno, strerror(errno)); return ZINT_ERROR_FILE_WRITE; } } else { - if (fclose(gif_file) != 0) { + if (fclose(State.file) != 0) { sprintf(symbol->errtxt, "617: Failure on closing output file (%d: %.30s)", errno, strerror(errno)); return ZINT_ERROR_FILE_WRITE; } diff --git a/backend/pcx.c b/backend/pcx.c index 8a286e95..4e6825ea 100644 --- a/backend/pcx.c +++ b/backend/pcx.c @@ -48,9 +48,10 @@ INTERNAL int pcx_pixel_plot(struct zint_symbol *symbol, const unsigned char *pix int run_count; FILE *pcx_file; pcx_header_t header; - int bytes_per_line = symbol->bitmap_width + (symbol->bitmap_width & 1); /* Must be even */ unsigned char previous; + const unsigned char *pb; const int output_to_stdout = symbol->output_options & BARCODE_STDOUT; /* Suppress gcc -fanalyzer warning */ + const int bytes_per_line = symbol->bitmap_width + (symbol->bitmap_width & 1); /* Must be even */ unsigned char *rle_row = (unsigned char *) z_alloca(bytes_per_line); rle_row[bytes_per_line - 1] = 0; /* Will remain zero if bitmap_width odd */ @@ -104,8 +105,7 @@ INTERNAL int pcx_pixel_plot(struct zint_symbol *symbol, const unsigned char *pix fwrite(&header, sizeof(pcx_header_t), 1, pcx_file); - for (row = 0; row < symbol->bitmap_height; row++) { - const unsigned char *const pb = pixelbuf + row * symbol->bitmap_width; + for (row = 0, pb = pixelbuf; row < symbol->bitmap_height; row++, pb += symbol->bitmap_width) { for (colour = 0; colour < header.number_of_planes; colour++) { for (column = 0; column < symbol->bitmap_width; column++) { const unsigned char ch = pb[column]; diff --git a/backend/raster.c b/backend/raster.c index 6017b123..c30bb8df 100644 --- a/backend/raster.c +++ b/backend/raster.c @@ -63,6 +63,17 @@ INTERNAL int tif_pixel_plot(struct zint_symbol *symbol, const unsigned char *pix static const char ultra_colour[] = "0CBMRYGKW"; +/* Wrapper to pre-check `size` on `malloc()` isn't too big (`size2` given if doing X `malloc()`s in a row) */ +static void *raster_malloc(size_t size, size_t size2) { + /* Check for large image `malloc`s, which produce very large files most systems can't handle anyway */ + /* Also `malloc()` on Linux will (usually) succeed regardless of request, and then get untrappably killed on + access by OOM killer if too much, so this is a crude mitigation */ + if (size + size2 > 0x40000000) { /* 1GB */ + return NULL; + } + return malloc(size); +} + static int buffer_plot(struct zint_symbol *symbol, const unsigned char *pixelbuf) { /* Place pixelbuffer into symbol */ unsigned char alpha[2]; @@ -80,6 +91,7 @@ static int buffer_plot(struct zint_symbol *symbol, const unsigned char *pixelbuf int row; int plot_alpha = 0; const size_t bm_bitmap_width = (size_t) symbol->bitmap_width * 3; + const size_t bm_bitmap_size = bm_bitmap_width * symbol->bitmap_height; if (out_colour_get_rgb(symbol->fgcolour, &map[DEFAULT_INK][0], &map[DEFAULT_INK][1], &map[DEFAULT_INK][2], &alpha[0])) { @@ -100,27 +112,28 @@ static int buffer_plot(struct zint_symbol *symbol, const unsigned char *pixelbuf symbol->alphamap = NULL; } - symbol->bitmap = (unsigned char *) malloc(bm_bitmap_width * symbol->bitmap_height); + symbol->bitmap = (unsigned char *) raster_malloc(bm_bitmap_size, 0); if (symbol->bitmap == NULL) { strcpy(symbol->errtxt, "661: Insufficient memory for bitmap buffer"); return ZINT_ERROR_MEMORY; } if (plot_alpha) { - symbol->alphamap = (unsigned char *) malloc((size_t) symbol->bitmap_width * symbol->bitmap_height); + const size_t alpha_size = (size_t) symbol->bitmap_width * symbol->bitmap_height; + symbol->alphamap = (unsigned char *) raster_malloc(alpha_size, bm_bitmap_size); if (symbol->alphamap == NULL) { strcpy(symbol->errtxt, "662: Insufficient memory for alphamap buffer"); return ZINT_ERROR_MEMORY; } for (row = 0; row < symbol->bitmap_height; row++) { - int p = row * symbol->bitmap_width; + size_t p = (size_t) symbol->bitmap_width * row; const unsigned char *pb = pixelbuf + p; unsigned char *bitmap = symbol->bitmap + p * 3; if (row && memcmp(pb, pb - symbol->bitmap_width, symbol->bitmap_width) == 0) { memcpy(bitmap, bitmap - bm_bitmap_width, bm_bitmap_width); memcpy(symbol->alphamap + p, symbol->alphamap + p - symbol->bitmap_width, symbol->bitmap_width); } else { - const int pe = p + symbol->bitmap_width; + const size_t pe = p + symbol->bitmap_width; for (; p < pe; p++, bitmap += 3) { memcpy(bitmap, map[pixelbuf[p]], 3); symbol->alphamap[p] = alpha[pixelbuf[p] == DEFAULT_PAPER]; @@ -129,7 +142,7 @@ static int buffer_plot(struct zint_symbol *symbol, const unsigned char *pixelbuf } } else { for (row = 0; row < symbol->bitmap_height; row++) { - const int r = row * symbol->bitmap_width; + const size_t r = (size_t) symbol->bitmap_width * row; const unsigned char *pb = pixelbuf + r; unsigned char *bitmap = symbol->bitmap + r * 3; if (row && memcmp(pb, pb - symbol->bitmap_width, symbol->bitmap_width) == 0) { @@ -169,7 +182,7 @@ static int save_raster_image_to_file(struct zint_symbol *symbol, const int image } if (rotate_angle) { - if (!(rotated_pixbuf = (unsigned char *) malloc((size_t) image_width * image_height))) { + if (!(rotated_pixbuf = (unsigned char *) raster_malloc((size_t) image_width * image_height, 0 /*size2*/))) { strcpy(symbol->errtxt, "650: Insufficient memory for pixel buffer"); return ZINT_ERROR_MEMORY; } @@ -182,25 +195,28 @@ static int save_raster_image_to_file(struct zint_symbol *symbol, const int image break; case 90: /* Plot 90 degrees clockwise */ for (row = 0; row < image_width; row++) { + const size_t h_offset = (size_t) image_height * row; for (column = 0; column < image_height; column++) { - rotated_pixbuf[(row * image_height) + column] = - *(pixelbuf + (image_width * (image_height - column - 1)) + row); + const size_t w_offset = (size_t) image_width * (image_height - column - 1); + rotated_pixbuf[h_offset + column] = *(pixelbuf + w_offset + row); } } break; case 180: /* Plot upside down */ for (row = 0; row < image_height; row++) { + const size_t w_offset = (size_t) image_width * row; + const size_t wh_offset = (size_t) image_width * (image_height - row - 1); for (column = 0; column < image_width; column++) { - rotated_pixbuf[(row * image_width) + column] = - *(pixelbuf + (image_width * (image_height - row - 1)) + (image_width - column - 1)); + rotated_pixbuf[w_offset + column] = *(pixelbuf + wh_offset + (image_width - column - 1)); } } break; case 270: /* Plot 90 degrees anti-clockwise */ for (row = 0; row < image_width; row++) { + const size_t h_offset = (size_t) image_height * row; for (column = 0; column < image_height; column++) { - rotated_pixbuf[(row * image_height) + column] = - *(pixelbuf + (image_width * column) + (image_width - row - 1)); + const size_t w_offset = (size_t) image_width * column; + rotated_pixbuf[h_offset + column] = *(pixelbuf + w_offset + (image_width - row - 1)); } } break; @@ -713,12 +729,14 @@ static void draw_bind_box(const struct zint_symbol *symbol, unsigned char *pixel static int plot_raster_maxicode(struct zint_symbol *symbol, const int rotate_angle, const int file_type) { int row, column; int image_height, image_width; + size_t image_size; unsigned char *pixelbuf; int error_number; float xoffset, yoffset, roffset, boffset; float scaler = symbol->scale; unsigned char *scaled_hexagon; int hex_width, hex_height; + size_t hex_size; int hx_start, hy_start, hx_end, hy_end; int hex_image_width, hex_image_height; int yposn_offset; @@ -758,19 +776,21 @@ static int plot_raster_maxicode(struct zint_symbol *symbol, const int rotate_ang image_width = (int) ceilf(hex_image_width + xoffset_si + roffset_si); image_height = (int) ceilf(hex_image_height + yoffset_si + boffset_si); assert(image_width && image_height); + image_size = (size_t) image_width * image_height; - if (!(pixelbuf = (unsigned char *) malloc((size_t) image_width * image_height))) { + if (!(pixelbuf = (unsigned char *) raster_malloc(image_size, 0 /*size*/))) { strcpy(symbol->errtxt, "655: Insufficient memory for pixel buffer"); return ZINT_ERROR_MEMORY; } - memset(pixelbuf, DEFAULT_PAPER, (size_t) image_width * image_height); + memset(pixelbuf, DEFAULT_PAPER, image_size); - if (!(scaled_hexagon = (unsigned char *) malloc((size_t) hex_width * hex_height))) { + hex_size = (size_t) hex_width * hex_height; + if (!(scaled_hexagon = (unsigned char *) raster_malloc(hex_size, image_size))) { strcpy(symbol->errtxt, "656: Insufficient memory for pixel buffer"); free(pixelbuf); return ZINT_ERROR_MEMORY; } - memset(scaled_hexagon, DEFAULT_PAPER, (size_t) hex_width * hex_height); + memset(scaled_hexagon, DEFAULT_PAPER, hex_size); plot_hexagon(scaled_hexagon, hex_width, hex_height, hx_start, hy_start, hx_end, hy_end); @@ -814,6 +834,7 @@ static int plot_raster_dotty(struct zint_symbol *symbol, const int rotate_angle, unsigned char *scaled_pixelbuf; int r, i; int scale_width, scale_height; + size_t scale_size; int error_number = 0; float xoffset, yoffset, roffset, boffset; float dot_offset_s; @@ -848,13 +869,14 @@ static int plot_raster_dotty(struct zint_symbol *symbol, const int rotate_angle, scale_width = (int) (symbol->width * scaler + xoffset_si + roffset_si + dot_overspill_si); scale_height = (int) (symbol_height_si + yoffset_si + boffset_si + dot_overspill_si); + scale_size = (size_t) scale_width * scale_height; - /* Apply scale options by creating another pixel buffer */ - if (!(scaled_pixelbuf = (unsigned char *) malloc((size_t) scale_width * scale_height))) { + /* Apply scale options by creating pixel buffer */ + if (!(scaled_pixelbuf = (unsigned char *) raster_malloc(scale_size, 0 /*size2*/))) { strcpy(symbol->errtxt, "657: Insufficient memory for pixel buffer"); return ZINT_ERROR_MEMORY; } - memset(scaled_pixelbuf, DEFAULT_PAPER, (size_t) scale_width * scale_height); + memset(scaled_pixelbuf, DEFAULT_PAPER, scale_size); /* Plot the body of the symbol to the pixel buffer */ for (r = 0; r < symbol->rows; r++) { @@ -946,6 +968,7 @@ static int plot_raster_default(struct zint_symbol *symbol, const int rotate_angl int row_heights_si[200]; int symbol_height_si; int image_width, image_height; + size_t image_size; unsigned char *pixelbuf; float scaler = symbol->scale; int si; @@ -1013,12 +1036,13 @@ static int plot_raster_default(struct zint_symbol *symbol, const int rotate_angl image_height = symbol_height_si + (int) ceilf(textoffset * si) + yoffset_si + boffset_si; assert(image_width && image_height); + image_size = (size_t) image_width * image_height; - if (!(pixelbuf = (unsigned char *) malloc((size_t) image_width * image_height))) { + if (!(pixelbuf = (unsigned char *) raster_malloc(image_size, 0 /*size2*/))) { strcpy(symbol->errtxt, "658: Insufficient memory for pixel buffer"); return ZINT_ERROR_MEMORY; } - memset(pixelbuf, DEFAULT_PAPER, (size_t) image_width * image_height); + memset(pixelbuf, DEFAULT_PAPER, image_size); yposn_si = yoffset_si; @@ -1341,7 +1365,7 @@ static int plot_raster_default(struct zint_symbol *symbol, const int rotate_angl const int scale_height = (int) stripf(image_height * scaler); /* Apply scale options by creating another pixel buffer */ - if (!(scaled_pixelbuf = (unsigned char *) malloc((size_t) scale_width * scale_height))) { + if (!(scaled_pixelbuf = (unsigned char *) raster_malloc((size_t) scale_width * scale_height, image_size))) { free(pixelbuf); strcpy(symbol->errtxt, "659: Insufficient memory for scaled pixel buffer"); return ZINT_ERROR_MEMORY; @@ -1350,7 +1374,7 @@ static int plot_raster_default(struct zint_symbol *symbol, const int rotate_angl /* Interpolate */ for (r = 0; r < scale_height; r++) { - size_t scaled_row = r * scale_width; + size_t scaled_row = (size_t) scale_width * r; size_t image_row = (size_t) stripf(r / scaler) * image_width; if (r && (image_row == prev_image_row || memcmp(pixelbuf + image_row, pixelbuf + prev_image_row, image_width) == 0)) { diff --git a/backend/tests/data/gif/datamatrix_seq2of9.gif b/backend/tests/data/gif/datamatrix_seq2of9.gif index 4c857ccc..19634a87 100644 Binary files a/backend/tests/data/gif/datamatrix_seq2of9.gif and b/backend/tests/data/gif/datamatrix_seq2of9.gif differ diff --git a/backend/tests/data/gif/itf14_height0.5_1.1.gif b/backend/tests/data/gif/itf14_height0.5_1.1.gif index 396bae1e..5692d600 100644 Binary files a/backend/tests/data/gif/itf14_height0.5_1.1.gif and b/backend/tests/data/gif/itf14_height0.5_1.1.gif differ 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 index a443cd3e..eb329f23 100644 Binary files a/backend/tests/data/gif/itf14_height61.8_bind4_wsp24_3.gif and b/backend/tests/data/gif/itf14_height61.8_bind4_wsp24_3.gif differ diff --git a/backend/tests/data/gif/ultra_fgbg_hvwsp1_box1.gif b/backend/tests/data/gif/ultra_fgbg_hvwsp1_box1.gif index 1043b448..a09738d6 100644 Binary files a/backend/tests/data/gif/ultra_fgbg_hvwsp1_box1.gif and b/backend/tests/data/gif/ultra_fgbg_hvwsp1_box1.gif differ diff --git a/backend/tests/data/gif/ultra_fgbg_hvwsp1_box1_bgfgtrans.gif b/backend/tests/data/gif/ultra_fgbg_hvwsp1_box1_bgfgtrans.gif new file mode 100644 index 00000000..7b757b6b Binary files /dev/null and b/backend/tests/data/gif/ultra_fgbg_hvwsp1_box1_bgfgtrans.gif differ diff --git a/backend/tests/data/gif/ultra_fgbg_hvwsp1_box1_bgtrans.gif b/backend/tests/data/gif/ultra_fgbg_hvwsp1_box1_bgtrans.gif new file mode 100644 index 00000000..7b757b6b Binary files /dev/null and b/backend/tests/data/gif/ultra_fgbg_hvwsp1_box1_bgtrans.gif differ diff --git a/backend/tests/data/gif/ultra_fgbg_hvwsp1_box1_fgtrans.gif b/backend/tests/data/gif/ultra_fgbg_hvwsp1_box1_fgtrans.gif new file mode 100644 index 00000000..9a59c136 Binary files /dev/null and b/backend/tests/data/gif/ultra_fgbg_hvwsp1_box1_fgtrans.gif differ diff --git a/backend/tests/data/gif/ultra_rev2.gif b/backend/tests/data/gif/ultra_rev2.gif index d843b5ef..38bc0656 100644 Binary files a/backend/tests/data/gif/ultra_rev2.gif and b/backend/tests/data/gif/ultra_rev2.gif differ diff --git a/backend/tests/data/print/eps/code128_aim.eps b/backend/tests/data/print/eps/code128_aim.eps index 5969fdfa..88b33b73 100644 --- a/backend/tests/data/print/eps/code128_aim.eps +++ b/backend/tests/data/print/eps/code128_aim.eps @@ -1,5 +1,5 @@ %!PS-Adobe-3.0 EPSF-3.0 -%%Creator: Zint 2.12.0.9 +%%Creator: Zint 2.13.0.9 %%Title: Zint Generated Symbol %%Pages: 0 %%BoundingBox: 0 0 136 117 diff --git a/backend/tests/data/print/eps/qr_v1_m.eps b/backend/tests/data/print/eps/qr_v1_m.eps index 891032bb..ab4c0b89 100644 --- a/backend/tests/data/print/eps/qr_v1_m.eps +++ b/backend/tests/data/print/eps/qr_v1_m.eps @@ -1,5 +1,5 @@ %!PS-Adobe-3.0 EPSF-3.0 -%%Creator: Zint 2.12.0.9 +%%Creator: Zint 2.13.0.9 %%Title: Zint Generated Symbol %%Pages: 0 %%BoundingBox: 0 0 42 42 diff --git a/backend/tests/data/print/eps/ultracode_a.eps b/backend/tests/data/print/eps/ultracode_a.eps index 1c57b0a9..6fc45efe 100644 --- a/backend/tests/data/print/eps/ultracode_a.eps +++ b/backend/tests/data/print/eps/ultracode_a.eps @@ -1,5 +1,5 @@ %!PS-Adobe-3.0 EPSF-3.0 -%%Creator: Zint 2.12.0.9 +%%Creator: Zint 2.13.0.9 %%Title: Zint Generated Symbol %%Pages: 0 %%BoundingBox: 0 0 26 26 diff --git a/backend/tests/data/print/gif/code128_aim.gif b/backend/tests/data/print/gif/code128_aim.gif index c7e7f7b4..86e5cb41 100644 Binary files a/backend/tests/data/print/gif/code128_aim.gif and b/backend/tests/data/print/gif/code128_aim.gif differ diff --git a/backend/tests/data/print/gif/qr_v1_m.gif b/backend/tests/data/print/gif/qr_v1_m.gif index 6e61e093..2647ac0f 100644 Binary files a/backend/tests/data/print/gif/qr_v1_m.gif and b/backend/tests/data/print/gif/qr_v1_m.gif differ diff --git a/backend/tests/data/print/gif/ultracode_a.gif b/backend/tests/data/print/gif/ultracode_a.gif index c6bb8b56..d21ffb88 100644 Binary files a/backend/tests/data/print/gif/ultracode_a.gif and b/backend/tests/data/print/gif/ultracode_a.gif differ diff --git a/backend/tests/test_gif.c b/backend/tests/test_gif.c index 8e2d3e50..9b0578c1 100644 --- a/backend/tests/test_gif.c +++ b/backend/tests/test_gif.c @@ -37,6 +37,7 @@ static void test_pixel_plot(const testCtx *const p_ctx) { int debug = p_ctx->debug; struct item { + int symbology; int width; int height; char *pattern; @@ -45,21 +46,21 @@ static void test_pixel_plot(const testCtx *const p_ctx) { }; /* s/\/\*[ 0-9]*\*\//\=printf("\/\*%3d*\/", line(".") - line("'<")): */ struct item data[] = { - /* 0*/ { 1, 1, "1", 0, 0 }, - /* 1*/ { 2, 1, "11", 0, 0 }, - /* 2*/ { 3, 1, "101", 0, 0 }, - /* 3*/ { 4, 1, "1010", 0, 0 }, - /* 4*/ { 5, 1, "10101", 0, 0 }, - /* 5*/ { 3, 2, "101010", 0, 0 }, - /* 6*/ { 3, 3, "101010101", 0, 0 }, - /* 7*/ { 8, 2, "CBMWKRYGGYRKWMBC", 0, 0 }, - /* 8*/ { 20, 30, "WWCWBWMWRWYWGWKCCWCMCRCYCGCKBWBCBBMBRBYBGBKMWMCMBMMRMYMGMKRWRCRBRMRRYRGRKYWYCYBYMYRYYGYKGWGCGBGMGRGYGGKKWKCKBKMKRKYKGKK", 1, 0 }, /* Single LZW block, size 255 */ - /* 9*/ { 19, 32, "WWCWBWMWRWYWGWKCCWCMCRCYCGCKBWBCBBMBRBYBGBKMWMCMBMMRMYMGMKRWRCRBRMRRYRGRKYWYCYBYMYRYYGYKGWGCGBGMGRGYGGKKWK", 1, 0 }, /* Two LZW blocks, last size 1 */ - /* 10*/ { 1, 1, "D", 0, ZINT_ERROR_INVALID_DATA }, + /* 0*/ { BARCODE_CODE128, 1, 1, "1", 0, 0 }, + /* 1*/ { BARCODE_CODE128, 2, 1, "11", 0, 0 }, + /* 2*/ { BARCODE_CODE128, 3, 1, "101", 0, 0 }, + /* 3*/ { BARCODE_CODE128, 4, 1, "1010", 0, 0 }, + /* 4*/ { BARCODE_CODE128, 5, 1, "10101", 0, 0 }, + /* 5*/ { BARCODE_CODE128, 3, 2, "101010", 0, 0 }, + /* 6*/ { BARCODE_CODE128, 3, 3, "101010101", 0, 0 }, + /* 7*/ { BARCODE_ULTRA, 8, 2, "CBMWKRYGGYRKWMBC", 0, 0 }, + /* 8*/ { BARCODE_ULTRA, 20, 30, "WWCWBWMWRWYWGWKCCWCMCRCYCGCKBWBCBBMBRBYBGBKMWMCMBMMRMYMGMKRWRCRBRMRRYRGRKYWYCYBYMYRYYGYKGWGCGBGMGRGYGGKKWKCKBKMKRKYKGKK", 1, 0 }, /* Single LZW block, size 255 */ + /* 9*/ { BARCODE_ULTRA, 19, 32, "WWCWBWMWRWYWGWKCCWCMCRCYCGCKBWBCBBMBRBYBGBKMWMCMBMMRMYMGMKRWRCRBRMRRYRGRKYWYCYBYMYRYYGYKGWGCGBGMGRGYGGKKWK", 1, 0 }, /* Two LZW blocks, last size 1 */ + /* 10*/ { BARCODE_ULTRA, 1, 1, "D", 0, 0 }, /* This used to fail, now just maps unknown codes to 0 (1st colour index) */ }; int data_size = ARRAY_SIZE(data); int i, ret; - struct zint_symbol *symbol; + struct zint_symbol *symbol = NULL; char *gif = "out.gif"; @@ -79,6 +80,7 @@ static void test_pixel_plot(const testCtx *const p_ctx) { strcpy(symbol->outfile, gif); + symbol->symbology = data[i].symbology; symbol->bitmap_width = data[i].width; symbol->bitmap_height = data[i].height; symbol->debug |= debug; @@ -103,7 +105,7 @@ static void test_pixel_plot(const testCtx *const p_ctx) { ret = testUtilVerifyIdentify(have_identify, symbol->outfile, debug); assert_zero(ret, "i:%d identify %s ret %d != 0\n", i, symbol->outfile, ret); } - if (!(debug & ZINT_DEBUG_TEST_KEEP_OUTFILE)) { + if (!(debug & ZINT_DEBUG_TEST_KEEP_OUTFILE)) { /* -d 64 */ assert_zero(testUtilRemove(symbol->outfile), "i:%d testUtilRemove(%s) != 0\n", i, symbol->outfile); } } else { @@ -168,18 +170,21 @@ static void test_print(const testCtx *const p_ctx) { /* 23*/ { BARCODE_DOTCODE, -1, -1, -1, -1, -1, -1, 0, 0, 0, { 0, 0, "" }, "000000", "FFFFFF00", "12", "dotcode_bgtrans.gif", "" }, /* 24*/ { BARCODE_DOTCODE, -1, CMYK_COLOUR, -1, -1, -1, -1, 0, 0, 0, { 0, 0, "" }, "71,0,40,44", "", "12", "dotcode_cmyk_fg.gif", "" }, /* 25*/ { BARCODE_ULTRA, 1, BARCODE_BOX, 1, 1, -1, -1, 0, 0, 0, { 0, 0, "" }, "0000FF", "FF0000", "12", "ultra_fgbg_hvwsp1_box1.gif", "" }, - /* 26*/ { BARCODE_ITF14, 4, BARCODE_BIND, 24, -1, -1, -1, 61.8, 3, 0, { 0, 0, "" }, "", "", "0501054800395", "itf14_height61.8_bind4_wsp24_3.gif", "#204 ARM-Cortex crash" }, - /* 27*/ { BARCODE_ITF14, 0, BARCODE_BIND, -1, -1, -1, -1, 0.5, 0.5, 0, { 0, 0, "" }, "", "", "0501054800395", "itf14_height0.5_box0_0.5.gif", "No box, no text" }, - /* 28*/ { BARCODE_ITF14, -1, -1, -1, -1, -1, -1, 0.5, 1.1, 0, { 0, 0, "" }, "", "", "0501054800395", "itf14_height0.5_1.1.gif", "" }, - /* 29*/ { BARCODE_CODE16K, -1, -1, 3, 5, -1, -1, 0.5, 0, 0, { 0, 0, "" }, "", "", "1234567890", "code16k_height0.5_wsp3_vwsp5.gif", "Separator covers bars" }, - /* 30*/ { BARCODE_CODE16K, -1, -1, 3, 5, -1, -1, 1.5, 0, 0, { 0, 0, "" }, "", "", "1234567890", "code16k_height1.5_wsp3_vwsp5.gif", "" }, - /* 31*/ { BARCODE_DATAMATRIX, -1, -1, -1, -1, -1, -1, 0, 0, 0, { 2, 9, "001002" }, "", "", "1234567890", "datamatrix_seq2of9.gif", "" }, - /* 32*/ { BARCODE_ULTRA, -1, -1, 1, -1, -1, 2, 0, 0, 0, { 0, 0, "" }, "", "", "12", "ultra_rev2.gif", "Revision 2" }, - /* 33*/ { BARCODE_DPD, -1, BARCODE_QUIET_ZONES | COMPLIANT_HEIGHT, -1, -1, -1, -1, 0, 0, 0, { 0, 0, "" }, "", "", "008182709980000020028101276", "dpd_compliant.gif", "Now with bind top 3X default" }, + /* 26*/ { BARCODE_ULTRA, 1, BARCODE_BOX, 1, 1, -1, -1, 0, 0, 0, { 0, 0, "" }, "0000FF00", "FF000000", "12", "ultra_fgbg_hvwsp1_box1_bgfgtrans.gif", "" }, + /* 27*/ { BARCODE_ULTRA, 1, BARCODE_BOX, 1, 1, -1, -1, 0, 0, 0, { 0, 0, "" }, "0000FF", "FF000000", "12", "ultra_fgbg_hvwsp1_box1_bgtrans.gif", "" }, + /* 28*/ { BARCODE_ULTRA, 1, BARCODE_BOX, 1, 1, -1, -1, 0, 0, 0, { 0, 0, "" }, "0000FF00", "FF0000", "12", "ultra_fgbg_hvwsp1_box1_fgtrans.gif", "" }, + /* 29*/ { BARCODE_ITF14, 4, BARCODE_BIND, 24, -1, -1, -1, 61.8, 3, 0, { 0, 0, "" }, "", "", "0501054800395", "itf14_height61.8_bind4_wsp24_3.gif", "#204 ARM-Cortex crash" }, + /* 30*/ { BARCODE_ITF14, 0, BARCODE_BIND, -1, -1, -1, -1, 0.5, 0.5, 0, { 0, 0, "" }, "", "", "0501054800395", "itf14_height0.5_box0_0.5.gif", "No box, no text" }, + /* 31*/ { BARCODE_ITF14, -1, -1, -1, -1, -1, -1, 0.5, 1.1, 0, { 0, 0, "" }, "", "", "0501054800395", "itf14_height0.5_1.1.gif", "" }, + /* 32*/ { BARCODE_CODE16K, -1, -1, 3, 5, -1, -1, 0.5, 0, 0, { 0, 0, "" }, "", "", "1234567890", "code16k_height0.5_wsp3_vwsp5.gif", "Separator covers bars" }, + /* 33*/ { BARCODE_CODE16K, -1, -1, 3, 5, -1, -1, 1.5, 0, 0, { 0, 0, "" }, "", "", "1234567890", "code16k_height1.5_wsp3_vwsp5.gif", "" }, + /* 34*/ { BARCODE_DATAMATRIX, -1, -1, -1, -1, -1, -1, 0, 0, 0, { 2, 9, "001002" }, "", "", "1234567890", "datamatrix_seq2of9.gif", "" }, + /* 35*/ { BARCODE_ULTRA, -1, -1, 1, -1, -1, 2, 0, 0, 0, { 0, 0, "" }, "", "", "12", "ultra_rev2.gif", "Revision 2" }, + /* 36*/ { BARCODE_DPD, -1, BARCODE_QUIET_ZONES | COMPLIANT_HEIGHT, -1, -1, -1, -1, 0, 0, 0, { 0, 0, "" }, "", "", "008182709980000020028101276", "dpd_compliant.gif", "Now with bind top 3X default" }, }; int data_size = ARRAY_SIZE(data); int i, length, ret; - struct zint_symbol *symbol; + struct zint_symbol *symbol = NULL; const char *data_dir = "/backend/tests/data/gif"; const char *gif = "out.gif"; @@ -189,7 +194,7 @@ static void test_print(const testCtx *const p_ctx) { const char *const have_identify = testUtilHaveIdentify(); - testStart("test_print"); + testStartSymbol("test_print", &symbol); if (p_ctx->generate) { char data_dir_path[1024]; @@ -319,13 +324,11 @@ static void test_large_scale(const testCtx *const p_ctx) { length = (int) strlen(data); + ZBarcode_Reset(&symbol); 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; + symbol.scale = 12.5f; /* 70.0f would cause paging as LZW > 1MB but very slow */ 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); @@ -340,6 +343,35 @@ static void test_large_scale(const testCtx *const p_ctx) { testFinish(); } +static void test_too_big(const testCtx *const p_ctx) { + int debug = p_ctx->debug; + + int length, ret; + struct zint_symbol symbol = {0}; + char data[] = "12345"; + + (void)debug; + + testStart("test_too_big"); + + length = (int) strlen(data); + + ZBarcode_Reset(&symbol); + symbol.symbology = BARCODE_EANX; + strcpy(symbol.outfile, "out.gif"); + symbol.scale = 200.0f; + symbol.whitespace_width = 32; + + /* Fails in `plot_raster_default()` with `image_size` 0x4029C800 > 1GB */ + ret = ZBarcode_Encode_and_Print(&symbol, (unsigned char *) data, length, 0 /*rotate_angle*/); + assert_equal(ret, ZINT_ERROR_MEMORY, "%s ZBarcode_Encode_and_Print ret %d != ZINT_ERROR_MEMORY %s\n", + testUtilBarcodeName(symbol.symbology), ret, symbol.errtxt); + + ZBarcode_Clear(&symbol); + + testFinish(); +} + int main(int argc, char *argv[]) { testFunction funcs[] = { /* name, func */ @@ -347,6 +379,7 @@ int main(int argc, char *argv[]) { { "test_print", test_print }, { "test_outfile", test_outfile }, { "test_large_scale", test_large_scale }, + { "test_too_big", test_too_big }, }; testRun(argc, argv, funcs, ARRAY_SIZE(funcs)); diff --git a/backend/tests/test_png.c b/backend/tests/test_png.c index 0ffb54f2..5a8ecac7 100644 --- a/backend/tests/test_png.c +++ b/backend/tests/test_png.c @@ -65,7 +65,7 @@ static void test_pixel_plot(const testCtx *const p_ctx) { const char *const have_identify = testUtilHaveIdentify(); - testStartSymbol("test_pixel_plot", &symbol); + testStart("test_pixel_plot"); for (i = 0; i < data_size; i++) { int size; diff --git a/backend/tests/test_tif.c b/backend/tests/test_tif.c index 07c16863..9382aec1 100644 --- a/backend/tests/test_tif.c +++ b/backend/tests/test_tif.c @@ -98,7 +98,7 @@ static void test_pixel_plot(const testCtx *const p_ctx) { int have_tiffinfo = testUtilHaveTiffInfo(); const char *const have_identify = testUtilHaveIdentify(); - testStartSymbol("test_pixel_plot", &symbol); + testStart("test_pixel_plot"); symbol = ZBarcode_Create(); assert_nonnull(symbol, "Symbol not created\n"); diff --git a/backend/tif.c b/backend/tif.c index f2c1b8fd..2b50df40 100644 --- a/backend/tif.c +++ b/backend/tif.c @@ -92,7 +92,7 @@ INTERNAL int tif_pixel_plot(struct zint_symbol *symbol, const unsigned char *pix unsigned char palette[32][5]; int color_map_size = 0; int extra_samples = 0; - uint32_t free_memory; + size_t free_memory; int row, column, strip; int strip_row; unsigned int bytes_put; @@ -291,7 +291,7 @@ INTERNAL int tif_pixel_plot(struct zint_symbol *symbol, const unsigned char *pix free_memory = sizeof(tiff_header_t); for (i = 0; i < strip_count; i++) { - strip_offset[i] = free_memory; + strip_offset[i] = (uint32_t) free_memory; if (i != (strip_count - 1)) { strip_bytes[i] = bytes_per_strip; } else { @@ -339,7 +339,7 @@ INTERNAL int tif_pixel_plot(struct zint_symbol *symbol, const unsigned char *pix header.byte_order = 0x4949; /* "II" little-endian */ } header.identity = 42; - header.offset = free_memory; + header.offset = (uint32_t) free_memory; fwrite(&header, sizeof(tiff_header_t), 1, tif_file); total_bytes_put = sizeof(tiff_header_t); @@ -425,7 +425,16 @@ INTERNAL int tif_pixel_plot(struct zint_symbol *symbol, const unsigned char *pix file_pos = ftell(tif_file); fseek(tif_file, 4, SEEK_SET); free_memory = file_pos; - fwrite(&free_memory, 4, 1, tif_file); + temp32 = (uint32_t) free_memory; + /* Shouldn't happen as `free_memory` checked above to be <= 0xffff0000 & should only decrease */ + if (free_memory != temp32 || (long) free_memory != file_pos) { + strcpy(symbol->errtxt, "982: Output file size too big"); + if (!output_to_stdout) { + (void) fclose(tif_file); + } + return ZINT_ERROR_MEMORY; + } + fwrite(&temp32, 4, 1, tif_file); fseek(tif_file, file_pos, SEEK_SET); } @@ -450,7 +459,7 @@ INTERNAL int tif_pixel_plot(struct zint_symbol *symbol, const unsigned char *pix tags[entries++].offset = (bits_per_sample << 16) | bits_per_sample; } else { update_offsets[offsets++] = entries; - tags[entries++].offset = free_memory; + tags[entries++].offset = (uint32_t) free_memory; free_memory += samples_per_pixel * 2; } } @@ -472,7 +481,7 @@ INTERNAL int tif_pixel_plot(struct zint_symbol *symbol, const unsigned char *pix tags[entries++].offset = strip_offset[0]; } else { update_offsets[offsets++] = entries; - tags[entries++].offset = free_memory; + tags[entries++].offset = (uint32_t) free_memory; free_memory += strip_count * 4; } @@ -527,7 +536,7 @@ INTERNAL int tif_pixel_plot(struct zint_symbol *symbol, const unsigned char *pix tags[entries].type = 3; /* SHORT */ tags[entries].count = color_map_size * 3; update_offsets[offsets++] = entries; - tags[entries++].offset = free_memory; + tags[entries++].offset = (uint32_t) free_memory; /* free_memory += color_map_size * 3 * 2; Unnecessary as long as last use */ } diff --git a/frontend_qt/extScale.ui b/frontend_qt/extScale.ui index c6dc8bcf..83a56898 100644 --- a/frontend_qt/extScale.ui +++ b/frontend_qt/extScale.ui @@ -54,7 +54,7 @@ 0.001000000000000 - 100.000000000000000 + 10.000000000000000 0.010000000000000 @@ -140,7 +140,7 @@ - 9999 + 1000 1 diff --git a/frontend_qt/mainWindow.ui b/frontend_qt/mainWindow.ui index e087cee0..7ae2147f 100644 --- a/frontend_qt/mainWindow.ui +++ b/frontend_qt/mainWindow.ui @@ -2008,7 +2008,7 @@ the barcode in X-dimensions 0.100000000000000 - 100.000000000000000 + 200.000000000000000 0.500000000000000 diff --git a/frontend_qt/scalewindow.cpp b/frontend_qt/scalewindow.cpp index 179e4e59..67d3dcfa 100644 --- a/frontend_qt/scalewindow.cpp +++ b/frontend_qt/scalewindow.cpp @@ -86,6 +86,7 @@ ScaleWindow::ScaleWindow(BarcodeItem *bc, Zint::QZintXdimDpVars *vars, double or } else { spnResolution->setSingleStep(1); } + set_maxima(); size_msg_ui_set(); @@ -179,6 +180,7 @@ void ScaleWindow::update_scale() emit scaleChanged(scale); m_unset = false; btnScaleUnset->setEnabled(true); + set_maxima(); } } @@ -248,6 +250,22 @@ const char *ScaleWindow::getFileType() const return filetypes[std::max(std::min(cmbFileType->currentIndex(), 2), 0)]; } +void ScaleWindow::set_maxima() +{ + float maxXdim = m_bc->bc.getXdimDpFromScale(200.0f, get_dpmm(), getFileType()); + if (cmbXdimUnits->currentIndex() == 1) { // Inches + spnXdim->setMaximum(maxXdim / 25.4); + } else { + spnXdim->setMaximum(maxXdim); + } + float maxRes = m_bc->bc.getXdimDpFromScale(200.0f, get_x_dim_mm(), getFileType()); + if (cmbResolutionUnits->currentIndex() == 1) { // Inches + spnResolution->setMaximum(maxRes * 25.4); + } else { + spnResolution->setMaximum(maxRes); + } +} + double ScaleWindow::update_vars() { double scale = (double) m_bc->bc.getScaleFromXdimDp(get_x_dim_mm(), get_dpmm(), getFileType()); diff --git a/frontend_qt/scalewindow.h b/frontend_qt/scalewindow.h index 3453f9ef..5be80cc9 100644 --- a/frontend_qt/scalewindow.h +++ b/frontend_qt/scalewindow.h @@ -57,6 +57,7 @@ private: float get_x_dim_mm() const; float get_dpmm() const; const char *getFileType() const; + void set_maxima(); double update_vars(); double m_originalScale; bool m_unset;