diff --git a/backend/tests/data/print/tif/code128_aim.tif b/backend/tests/data/print/tif/code128_aim.tif index 70c67f0c..5cc663a5 100644 Binary files a/backend/tests/data/print/tif/code128_aim.tif and b/backend/tests/data/print/tif/code128_aim.tif differ diff --git a/backend/tests/data/print/tif/dotcode_aim_fig7.tif b/backend/tests/data/print/tif/dotcode_aim_fig7.tif index a668466e..f518d1f9 100644 Binary files a/backend/tests/data/print/tif/dotcode_aim_fig7.tif and b/backend/tests/data/print/tif/dotcode_aim_fig7.tif differ diff --git a/backend/tests/data/print/tif/maxicode_fig_2.tif b/backend/tests/data/print/tif/maxicode_fig_2.tif index 56603e78..d18f02ee 100644 Binary files a/backend/tests/data/print/tif/maxicode_fig_2.tif and b/backend/tests/data/print/tif/maxicode_fig_2.tif differ diff --git a/backend/tests/data/print/tif/qr_v1_m.tif b/backend/tests/data/print/tif/qr_v1_m.tif index 6ebd18e5..f0c8568d 100644 Binary files a/backend/tests/data/print/tif/qr_v1_m.tif and b/backend/tests/data/print/tif/qr_v1_m.tif differ diff --git a/backend/tests/data/print/tif/ultracode_a.tif b/backend/tests/data/print/tif/ultracode_a.tif index c9449936..554d06f9 100644 Binary files a/backend/tests/data/print/tif/ultracode_a.tif and b/backend/tests/data/print/tif/ultracode_a.tif differ diff --git a/backend/tests/data/tif/aztec_v32_fg.tif b/backend/tests/data/tif/aztec_v32_fg.tif new file mode 100644 index 00000000..e23a8d89 Binary files /dev/null and b/backend/tests/data/tif/aztec_v32_fg.tif differ diff --git a/backend/tests/data/tif/code128_bgalpha.tif b/backend/tests/data/tif/code128_bgalpha.tif new file mode 100644 index 00000000..43f69622 Binary files /dev/null and b/backend/tests/data/tif/code128_bgalpha.tif differ diff --git a/backend/tests/data/tif/code128_cmyk.tif b/backend/tests/data/tif/code128_cmyk.tif new file mode 100644 index 00000000..ff83c6e7 Binary files /dev/null and b/backend/tests/data/tif/code128_cmyk.tif differ diff --git a/backend/tests/data/tif/code128_cmyk_fgbgalpha.tif b/backend/tests/data/tif/code128_cmyk_fgbgalpha.tif new file mode 100644 index 00000000..632e355d Binary files /dev/null and b/backend/tests/data/tif/code128_cmyk_fgbgalpha.tif differ diff --git a/backend/tests/data/tif/code128_fgalpha.tif b/backend/tests/data/tif/code128_fgalpha.tif new file mode 100644 index 00000000..e2a6b232 Binary files /dev/null and b/backend/tests/data/tif/code128_fgalpha.tif differ diff --git a/backend/tests/data/tif/code128_fgbg.tif b/backend/tests/data/tif/code128_fgbg.tif new file mode 100644 index 00000000..77c66a68 Binary files /dev/null and b/backend/tests/data/tif/code128_fgbg.tif differ diff --git a/backend/tests/data/tif/code128_fgbgalpha.tif b/backend/tests/data/tif/code128_fgbgalpha.tif new file mode 100644 index 00000000..194e8a43 Binary files /dev/null and b/backend/tests/data/tif/code128_fgbgalpha.tif differ diff --git a/backend/tests/data/tif/daft_scale0.5.tif b/backend/tests/data/tif/daft_scale0.5.tif new file mode 100644 index 00000000..45e77ce4 Binary files /dev/null and b/backend/tests/data/tif/daft_scale0.5.tif differ diff --git a/backend/tests/data/tif/hanxin_v84_l4_scale2.tif b/backend/tests/data/tif/hanxin_v84_l4_scale2.tif new file mode 100644 index 00000000..c908cb18 Binary files /dev/null and b/backend/tests/data/tif/hanxin_v84_l4_scale2.tif differ diff --git a/backend/tests/data/tif/ultra_bgalpha.tif b/backend/tests/data/tif/ultra_bgalpha.tif new file mode 100644 index 00000000..4495076a Binary files /dev/null and b/backend/tests/data/tif/ultra_bgalpha.tif differ diff --git a/backend/tests/data/tif/ultra_bgalpha_nofg.tif b/backend/tests/data/tif/ultra_bgalpha_nofg.tif new file mode 100644 index 00000000..4495076a Binary files /dev/null and b/backend/tests/data/tif/ultra_bgalpha_nofg.tif differ diff --git a/backend/tests/data/tif/ultra_fgalpha.tif b/backend/tests/data/tif/ultra_fgalpha.tif new file mode 100644 index 00000000..05708be9 Binary files /dev/null and b/backend/tests/data/tif/ultra_fgalpha.tif differ diff --git a/backend/tests/data/tif/ultra_fgalpha_nobg.tif b/backend/tests/data/tif/ultra_fgalpha_nobg.tif new file mode 100644 index 00000000..e87a4fdf Binary files /dev/null and b/backend/tests/data/tif/ultra_fgalpha_nobg.tif differ diff --git a/backend/tests/data/tif/ultra_fgbgalpha.tif b/backend/tests/data/tif/ultra_fgbgalpha.tif new file mode 100644 index 00000000..8e929d45 Binary files /dev/null and b/backend/tests/data/tif/ultra_fgbgalpha.tif differ diff --git a/backend/tests/data/tif/ultra_odd.tif b/backend/tests/data/tif/ultra_odd.tif new file mode 100644 index 00000000..907f606b Binary files /dev/null and b/backend/tests/data/tif/ultra_odd.tif differ diff --git a/backend/tests/test_tif.c b/backend/tests/test_tif.c index c07b0225..15edeb52 100644 --- a/backend/tests/test_tif.c +++ b/backend/tests/test_tif.c @@ -30,6 +30,7 @@ /* vim: set ts=4 sw=4 et : */ #include "testcommon.h" +#include INTERNAL int tif_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf); @@ -68,24 +69,24 @@ static void test_pixel_plot(int index, int debug) { /* 13*/ { 3, 3, "101010101", 0 }, /* 14*/ { 4, 3, "10", 1 }, /* 15*/ { 3, 4, "10", 1 }, - /* 16*/ { 52, 51, "10", 1 }, // Strip Count 1, Rows Per Strip 51 (52 * 51 * 3 == 7956) - /* 17*/ { 52, 52, "10", 1 }, // Strip Count 1, Rows Per Strip 52 (52 * 52 * 3 == 8112) - /* 18*/ { 53, 52, "10", 1 }, // Strip Count 2, Rows Per Strip 51 (53 * 51 * 3 == 8109) - /* 19*/ { 53, 53, "10", 1 }, // Strip Count 2, Rows Per Strip 51 - /* 20*/ { 2730, 1, "10", 1 }, // Strip Count 1, Rows Per Strip 1 (2730 * 1 * 3 == 8190) - /* 21*/ { 1, 2730, "10", 1 }, // Strip Count 1, Rows Per Strip 2730 - /* 22*/ { 2730, 2, "10", 1 }, // Strip Count 2, Rows Per Strip 1 - /* 23*/ { 2, 2730, "10", 1 }, // Strip Count 2, Rows Per Strip 1365 (2 * 1365 * 3 == 8190) - /* 24*/ { 2730, 3, "10", 1 }, // Strip Count 3, Rows Per Strip 1 - /* 25*/ { 3, 2730, "10", 1 }, // Strip Count 3, Rows Per Strip 910 (3 * 910 * 3 == 8190) - /* 26*/ { 2731, 4, "10", 1 }, // Strip Count 4, Rows Per Strip 1 (2731 * 1 * 3 == 8193) - large rows in 1 strip, even if > 8192 - /* 27*/ { 4, 2731, "10", 1 }, // Strip Count 5, Rows Per Strip 682 (4 * 682 * 3 == 8184) + /* 16*/ { 45, 44, "10", 1 }, // Strip Count 1, Rows Per Strip 44 (45 * 44 * 4 == 7920) + /* 17*/ { 45, 45, "10", 1 }, // Strip Count 1, Rows Per Strip 45 (45 * 45 * 4 == 8100) + /* 18*/ { 46, 45, "10", 1 }, // Strip Count 2, Rows Per Strip 44 (46 * 45 * 4 == 8280) + /* 19*/ { 46, 46, "10", 1 }, // Strip Count 2, Rows Per Strip 44 + /* 20*/ { 2048, 1, "10", 1 }, // Strip Count 1, Rows Per Strip 1 (2048 * 4 == 8192) + /* 21*/ { 1, 2048, "10", 1 }, // Strip Count 1, Rows Per Strip 2048 + /* 22*/ { 2048, 2, "10", 1 }, // Strip Count 2, Rows Per Strip 1 + /* 23*/ { 2, 2048, "10", 1 }, // Strip Count 2, Rows Per Strip 1024 (2 * 1024 * 4 == 8192) + /* 24*/ { 2048, 3, "10", 1 }, // Strip Count 3, Rows Per Strip 1 + /* 25*/ { 3, 2048, "10", 1 }, // Strip Count 4, Rows Per Strip 682 ((3 * 682 + 2) * 4 == 8192) + /* 26*/ { 2049, 4, "10", 1 }, // Strip Count 4, Rows Per Strip 1 (2049 * 1 * 4 == 8196) - large rows in 1 strip, even if > 8192 + /* 27*/ { 4, 2049, "10", 1 }, // Strip Count 5, Rows Per Strip 512 ((4 * 512 + 1) * 4 == 8196) }; int data_size = ARRAY_SIZE(data); char *tif = "out.tif"; - char data_buf[2731 * 4 + 1]; + char data_buf[65536]; for (int i = 0; i < data_size; i++) { @@ -98,10 +99,11 @@ static void test_pixel_plot(int index, int debug) { symbol->bitmap_width = data[i].width; symbol->bitmap_height = data[i].height; + strcpy(symbol->bgcolour, "FFFFFFEE"); // Use alpha background to force RGB symbol->debug |= debug; int size = data[i].width * data[i].height; - assert_nonzero(size < (int) sizeof(data_buf), "i:%d tif_pixel_plot size %d < sizeof(data_buf) %d\n", i, size, (int) sizeof(data_buf)); + assert_nonzero(size < (int) sizeof(data_buf), "i:%d tif_pixel_plot size %d >= sizeof(data_buf) %d\n", i, size, (int) sizeof(data_buf)); if (data[i].repeat) { testUtilStrCpyRepeat(data_buf, data[i].pattern, size); @@ -130,10 +132,138 @@ static void test_pixel_plot(int index, int debug) { testFinish(); } +static void test_print(int index, int generate, int debug) { + + testStart(""); + + int have_identify = testUtilHaveIdentify(); + + int ret; + struct item { + int symbology; + int input_mode; + int border_width; + int output_options; + int whitespace_width; + int show_hrt; + int option_1; + int option_2; + int height; + float scale; + char *fgcolour; + char *bgcolour; + char *data; + char *composite; + char *expected_file; + char *comment; + }; + struct item data[] = { + /* 0*/ { BARCODE_CODE128, -1, -1, -1, 1, -1, -1, -1, 0, 0, "112233", "EEDDCC", "A", "", "../data/tif/code128_fgbg.tif", "" }, + /* 1*/ { BARCODE_CODE128, -1, -1, -1, 1, -1, -1, -1, 0, 0, "C00000", "FEDCBACC", "A", "", "../data/tif/code128_bgalpha.tif", "" }, + /* 2*/ { BARCODE_CODE128, -1, -1, -1, 1, -1, -1, -1, 0, 0, "00000099", "FEDCBA", "A", "", "../data/tif/code128_fgalpha.tif", "" }, + /* 3*/ { BARCODE_CODE128, -1, -1, -1, 1, -1, -1, -1, 0, 0, "00000099", "FEDCBACC", "A", "", "../data/tif/code128_fgbgalpha.tif", "" }, + /* 4*/ { BARCODE_CODE128, -1, -1, CMYK_COLOUR, 1, -1, -1, -1, 0, 0, "C00000", "FEDCBA", "A", "", "../data/tif/code128_cmyk.tif", "" }, + /* 5*/ { BARCODE_CODE128, -1, -1, CMYK_COLOUR, 1, -1, -1, -1, 0, 0, "C0000099", "FEDCBACC", "A", "", "../data/tif/code128_cmyk_fgbgalpha.tif", "" }, + /* 6*/ { BARCODE_ULTRA, -1, -1, -1, 1, -1, -1, -1, 0, 0, "C00000", "FEDCBACC", "1234", "", "../data/tif/ultra_bgalpha.tif", "" }, + /* 7*/ { BARCODE_ULTRA, -1, -1, -1, 1, -1, -1, -1, 0, 0, "000000BB", "FEDCBA", "1234", "", "../data/tif/ultra_fgalpha.tif", "" }, + /* 8*/ { BARCODE_ULTRA, -1, -1, -1, 1, -1, -1, -1, 0, 0, "000000BB", "FEDCBACC", "1234", "", "../data/tif/ultra_fgbgalpha.tif", "" }, + /* 9*/ { BARCODE_ULTRA, -1, -1, -1, 1, -1, -1, -1, 0, 0, "000000BB", "", "1234", "", "../data/tif/ultra_fgalpha_nobg.tif", "" }, + /* 10*/ { BARCODE_ULTRA, -1, -1, -1, 1, -1, -1, -1, 0, 0, "", "FEDCBACC", "1234", "", "../data/tif/ultra_bgalpha_nofg.tif", "" }, + /* 11*/ { BARCODE_ULTRA, -1, -1, -1, -1, -1, -1, -1, 0, 0.5f, "", "", "1", "", "../data/tif/ultra_odd.tif", "" }, + /* 12*/ { BARCODE_HANXIN, UNICODE_MODE, -1, -1, -1, -1, 4, 84, 0, 2, "", "", "1", "", "../data/tif/hanxin_v84_l4_scale2.tif", "" }, + /* 13*/ { BARCODE_AZTEC, -1, -1, -1, -1, -1, -1, 32, 0, 0, "4BE055", "", "1", "", "../data/tif/aztec_v32_fg.tif", "" }, + /* 14*/ { BARCODE_DAFT, -1, -1, -1, -1, -1, -1, -1, 1, 0.5f, "", "", "F", "", "../data/tif/daft_scale0.5.tif", "" }, + }; + int data_size = ARRAY_SIZE(data); + + char *data_dir = "../data/tif"; + char *tif = "out.tif"; + char escaped[1024]; + int escaped_size = 1024; + char *text; + + if (generate) { + if (!testUtilExists(data_dir)) { + ret = mkdir(data_dir, 0755); + assert_zero(ret, "mkdir(%s) ret %d != 0\n", data_dir, ret); + } + } + + for (int i = 0; i < data_size; i++) { + + if (index != -1 && i != index) continue; + + struct zint_symbol* symbol = ZBarcode_Create(); + assert_nonnull(symbol, "Symbol not created\n"); + + int length = testUtilSetSymbol(symbol, data[i].symbology, data[i].input_mode, -1 /*eci*/, data[i].option_1, data[i].option_2, -1, data[i].output_options, data[i].data, -1, debug); + if (data[i].show_hrt != -1) { + symbol->show_hrt = data[i].show_hrt; + } + if (data[i].height) { + symbol->height = data[i].height; + } + if (data[i].scale) { + symbol->scale = data[i].scale; + } + if (data[i].border_width != -1) { + symbol->border_width = data[i].border_width; + } + if (data[i].whitespace_width != -1) { + symbol->whitespace_width = data[i].whitespace_width; + } + if (*data[i].fgcolour) { + strcpy(symbol->fgcolour, data[i].fgcolour); + } + if (*data[i].bgcolour) { + strcpy(symbol->bgcolour, data[i].bgcolour); + } + if (strlen(data[i].composite)) { + text = data[i].composite; + strcpy(symbol->primary, data[i].data); + } else { + text = data[i].data; + } + int text_length = strlen(text); + + ret = ZBarcode_Encode(symbol, (unsigned char *) text, text_length); + assert_zero(ret, "i:%d %s ZBarcode_Encode ret %d != 0 %s\n", i, testUtilBarcodeName(data[i].symbology), ret, symbol->errtxt); + + strcpy(symbol->outfile, tif); + ret = ZBarcode_Print(symbol, 0); + assert_zero(ret, "i:%d %s ZBarcode_Print %s ret %d != 0\n", i, testUtilBarcodeName(data[i].symbology), symbol->outfile, ret); + + if (generate) { + printf(" /*%3d*/ { %s, %s, %d, %s, %d, %d, %d, %d, %d, %.5g, \"%s\",\"%s\", \"%s\", \"%s\", \"%s\", \"%s\" },\n", + i, testUtilBarcodeName(data[i].symbology), testUtilInputModeName(data[i].input_mode), data[i].border_width, testUtilOutputOptionsName(data[i].output_options), + data[i].whitespace_width, data[i].show_hrt, data[i].option_1, data[i].option_2, data[i].height, data[i].scale, data[i].fgcolour, data[i].bgcolour, + testUtilEscape(data[i].data, length, escaped, escaped_size), data[i].composite, data[i].expected_file, data[i].comment); + ret = rename(symbol->outfile, data[i].expected_file); + assert_zero(ret, "i:%d rename(%s, %s) ret %d != 0\n", i, symbol->outfile, data[i].expected_file, ret); + if (have_identify) { + ret = testUtilVerifyIdentify(data[i].expected_file, debug); + assert_zero(ret, "i:%d %s identify %s ret %d != 0\n", i, testUtilBarcodeName(data[i].symbology), data[i].expected_file, ret); + } + } else { + assert_nonzero(testUtilExists(symbol->outfile), "i:%d testUtilExists(%s) == 0\n", i, symbol->outfile); + assert_nonzero(testUtilExists(data[i].expected_file), "i:%d testUtilExists(%s) == 0\n", i, data[i].expected_file); + + ret = testUtilCmpBins(symbol->outfile, data[i].expected_file); + assert_zero(ret, "i:%d %s testUtilCmpBins(%s, %s) %d != 0\n", i, testUtilBarcodeName(data[i].symbology), symbol->outfile, data[i].expected_file, ret); + assert_zero(remove(symbol->outfile), "i:%d remove(%s) != 0\n", i, symbol->outfile); + } + + ZBarcode_Delete(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 }, }; testRun(argc, argv, funcs, ARRAY_SIZE(funcs)); diff --git a/backend/tif.c b/backend/tif.c index b02056be..72cbe80b 100644 --- a/backend/tif.c +++ b/backend/tif.c @@ -3,7 +3,7 @@ /* libzint - the open source barcode library - Copyright (C) 2016 - 2020 Robin Stuart + Copyright (C) 2016 - 2021 Robin Stuart Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -35,43 +35,238 @@ #include #include #include +#include #include "common.h" #include "tif.h" +#include "tif_lzw.h" #ifdef _MSC_VER #include #include #include #endif +#if !defined(_WIN32) && !defined(__APPLE__) +#include +# if __BYTE_ORDER == __BIG_ENDIAN +#define TIF_BIG_ENDIAN +#endif +#endif + +/* PhotometricInterpretation */ +#define TIF_PMI_WHITEISZERO 0 +#define TIF_PMI_BLACKISZERO 1 +#define TIF_PMI_RGB 2 +#define TIF_PMI_PALETTE_COLOR 3 +#define TIF_PMI_SEPARATED 5 /* CMYK */ + +/* Compression */ +#define TIF_NO_COMPRESSION 1 +#define TIF_LZW 5 + +static void to_color_map(unsigned char rgb[4], tiff_color_t *color_map_entry) { + color_map_entry->red = (rgb[0] << 8) | rgb[0]; + color_map_entry->green = (rgb[1] << 8) | rgb[1]; + color_map_entry->blue = (rgb[2] << 8) | rgb[2]; +} + +static void to_cmyk(unsigned char rgb[3], unsigned char alpha, unsigned char *cmyk) { + unsigned char max = rgb[0]; + if (rgb[1] > max) { + max = rgb[1]; + } + if (rgb[2] > max) { + max = rgb[2]; + } + cmyk[0] = max - rgb[0]; + cmyk[1] = max - rgb[1]; + cmyk[2] = max - rgb[2]; + cmyk[3] = 0xff - max; + cmyk[4] = alpha; + +} + INTERNAL int tif_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf) { - int fgred, fggrn, fgblu, bgred, bggrn, bgblu; + unsigned char fg[4], bg[4]; int i; + int pmi; /* PhotometricInterpretation */ int rows_per_strip, strip_count; - unsigned int free_memory; + int rows_last_strip; + int bytes_per_strip; + uint16_t bits_per_sample; + int samples_per_pixel; + int pixels_per_sample; + unsigned char map[128]; + tiff_color_t color_map[256] = {0}; + unsigned char palette[32][5]; + int color_map_size = 0; + int extra_samples = 0; + uint32_t free_memory; int row, column, strip; + int strip_row; unsigned int bytes_put; + long total_bytes_put; FILE *tif_file; + unsigned char *pb; + int compression = TIF_NO_COMPRESSION; + tif_lzw_state lzw_state; + long file_pos; #ifdef _MSC_VER uint32_t* strip_offset; uint32_t* strip_bytes; + unsigned char *strip_buf; #endif tiff_header_t header; - tiff_ifd_t ifd; - uint16_t temp; + uint16_t entries = 0; + tiff_tag_t tags[20]; + uint32_t offset = 0; + int update_offsets[20]; + int offsets = 0; + int ifd_size; uint32_t temp32; - fgred = (16 * ctoi(symbol->fgcolour[0])) + ctoi(symbol->fgcolour[1]); - fggrn = (16 * ctoi(symbol->fgcolour[2])) + ctoi(symbol->fgcolour[3]); - fgblu = (16 * ctoi(symbol->fgcolour[4])) + ctoi(symbol->fgcolour[5]); - bgred = (16 * ctoi(symbol->bgcolour[0])) + ctoi(symbol->bgcolour[1]); - bggrn = (16 * ctoi(symbol->bgcolour[2])) + ctoi(symbol->bgcolour[3]); - bgblu = (16 * ctoi(symbol->bgcolour[4])) + ctoi(symbol->bgcolour[5]); + fg[0] = (16 * ctoi(symbol->fgcolour[0])) + ctoi(symbol->fgcolour[1]); + fg[1] = (16 * ctoi(symbol->fgcolour[2])) + ctoi(symbol->fgcolour[3]); + fg[2] = (16 * ctoi(symbol->fgcolour[4])) + ctoi(symbol->fgcolour[5]); + bg[0] = (16 * ctoi(symbol->bgcolour[0])) + ctoi(symbol->bgcolour[1]); + bg[1] = (16 * ctoi(symbol->bgcolour[2])) + ctoi(symbol->bgcolour[3]); + bg[2] = (16 * ctoi(symbol->bgcolour[4])) + ctoi(symbol->bgcolour[5]); + + if (strlen(symbol->fgcolour) > 6) { + fg[3] = (16 * ctoi(symbol->fgcolour[6])) + ctoi(symbol->fgcolour[7]); + } else { + fg[3] = 0xff; + } + + if (strlen(symbol->bgcolour) > 6) { + bg[3] = (16 * ctoi(symbol->bgcolour[6])) + ctoi(symbol->bgcolour[7]); + } else { + bg[3] = 0xff; + } + + if (symbol->symbology == BARCODE_ULTRA) { + static const int ultra_chars[8] = { 'W', 'C', 'B', 'M', 'R', 'Y', 'G', 'K' }; + static unsigned char ultra_rgbs[8][3] = { + { 0xff, 0xff, 0xff, }, /* White */ + { 0, 0xff, 0xff, }, /* Cyan */ + { 0, 0, 0xff, }, /* Blue */ + { 0xff, 0, 0xff, }, /* Magenta */ + { 0xff, 0, 0, }, /* Red */ + { 0xff, 0xff, 0, }, /* Yellow */ + { 0, 0xff, 0, }, /* Green */ + { 0, 0, 0, }, /* Black */ + }; + + if (symbol->output_options & CMYK_COLOUR) { + for (i = 0; i < 8; i++) { + map[ultra_chars[i]] = i; + to_cmyk(ultra_rgbs[i], fg[3], palette[i]); + } + map['0'] = 8; + to_cmyk(bg, bg[3], palette[8]); + map['1'] = 9; + to_cmyk(fg, fg[3], palette[9]); + + pmi = TIF_PMI_SEPARATED; + bits_per_sample = 8; + if (fg[3] == 0xff && bg[3] == 0xff) { /* If no alpha */ + samples_per_pixel = 4; + } else { + samples_per_pixel = 5; + extra_samples = 1; /* Associated alpha */ + } + pixels_per_sample = 1; + } else { + for (i = 0; i < 8; i++) { + map[ultra_chars[i]] = i; + memcpy(palette[i], ultra_rgbs[i], 3); + palette[i][3] = fg[3]; + } + map['0'] = 8; + memcpy(palette[8], bg, 4); + map['1'] = 9; + memcpy(palette[9], fg, 4); + + if (fg[3] == 0xff && bg[3] == 0xff) { /* If no alpha */ + pmi = TIF_PMI_PALETTE_COLOR; + for (i = 0; i < 10; i++) { + to_color_map(palette[i], &color_map[i]); + } + bits_per_sample = 4; + samples_per_pixel = 1; + pixels_per_sample = 2; + color_map_size = 16; /* 2**BitsPerSample */ + } else { + pmi = TIF_PMI_RGB; + bits_per_sample = 8; + samples_per_pixel = 4; + pixels_per_sample = 1; + extra_samples = 1; /* Associated alpha */ + } + } + } else { /* fg/bg only */ + if (symbol->output_options & CMYK_COLOUR) { + map['0'] = 0; + to_cmyk(bg, bg[3], palette[0]); + map['1'] = 1; + to_cmyk(fg, fg[3], palette[1]); + + pmi = TIF_PMI_SEPARATED; + bits_per_sample = 8; + if (fg[3] == 0xff && bg[3] == 0xff) { /* If no alpha */ + samples_per_pixel = 4; + } else { + samples_per_pixel = 5; + extra_samples = 1; /* Associated alpha */ + } + pixels_per_sample = 1; + } else if (bg[0] == 0xff && bg[1] == 0xff && bg[2] == 0xff && bg[3] == 0xff + && fg[0] == 0 && fg[1] == 0 && fg[2] == 0 && fg[3] == 0xff) { + map['0'] = 0; + map['1'] = 1; + + pmi = TIF_PMI_WHITEISZERO; + bits_per_sample = 1; + samples_per_pixel = 1; + pixels_per_sample = 8; + } else if (bg[0] == 0 && bg[1] == 0 && bg[2] == 0 && bg[3] == 0xff + && fg[0] == 0xff && fg[1] == 0xff && fg[2] == 0xff && fg[3] == 0xff) { + map['0'] = 1; + map['1'] = 0; + + pmi = TIF_PMI_BLACKISZERO; + bits_per_sample = 1; + samples_per_pixel = 1; + pixels_per_sample = 8; + } else { + map['0'] = 0; + memcpy(palette[0], bg, 4); + map['1'] = 1; + memcpy(palette[1], fg, 4); + + pmi = TIF_PMI_PALETTE_COLOR; + for (i = 0; i < 2; i++) { + to_color_map(palette[i], &color_map[i]); + } + if (fg[3] == 0xff && bg[3] == 0xff) { /* If no alpha */ + bits_per_sample = 4; + samples_per_pixel = 1; + pixels_per_sample = 2; + color_map_size = 16; /* 2**BitsPerSample */ + } else { + bits_per_sample = 8; + samples_per_pixel = 2; + pixels_per_sample = 1; + color_map_size = 256; /* 2**BitsPerSample */ + extra_samples = 1; /* Associated alpha */ + } + } + } /* TIFF Rev 6 Section 7 p.27 "Set RowsPerStrip such that the size of each strip is about 8K bytes... * Note that extremely wide high resolution images may have rows larger than 8K bytes; in this case, * RowsPerStrip should be 1, and the strip will be larger than 8K." */ - rows_per_strip = 8192 / (symbol->bitmap_width * 3); + rows_per_strip = (8192 * pixels_per_sample) / (symbol->bitmap_width * samples_per_pixel); if (rows_per_strip == 0) { rows_per_strip = 1; } @@ -79,43 +274,56 @@ INTERNAL int tif_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf) /* Suppresses clang-tidy clang-analyzer-core.VLASize warning */ assert(symbol->bitmap_height > 0); - strip_count = symbol->bitmap_height / rows_per_strip; - if ((symbol->bitmap_height % rows_per_strip) != 0) { - strip_count++; - } - - if (rows_per_strip > symbol->bitmap_height) { - rows_per_strip = symbol->bitmap_height; + if (rows_per_strip >= symbol->bitmap_height) { + strip_count = 1; + rows_per_strip = rows_last_strip = symbol->bitmap_height; + } else { + strip_count = symbol->bitmap_height / rows_per_strip; + rows_last_strip = symbol->bitmap_height % rows_per_strip; + if (rows_last_strip != 0) { + strip_count++; + } + if (rows_per_strip > symbol->bitmap_height) { + rows_per_strip = rows_last_strip = symbol->bitmap_height; + } } + assert(strip_count > 0); /* Suppress clang-analyzer-core.UndefinedBinaryOperatorResult */ if (symbol->debug & ZINT_DEBUG_PRINT) { - printf("TIFF (%dx%d) Strip Count %d, Rows Per Strip %d\n", symbol->bitmap_width, symbol->bitmap_height, strip_count, rows_per_strip); + printf("TIFF (%dx%d) Strip Count %d, Rows Per Strip %d\n", symbol->bitmap_width, symbol->bitmap_height, + strip_count, rows_per_strip); } + bytes_per_strip = rows_per_strip * ((symbol->bitmap_width + pixels_per_sample - 1) / pixels_per_sample) + * samples_per_pixel; + #ifndef _MSC_VER uint32_t strip_offset[strip_count]; uint32_t strip_bytes[strip_count]; + unsigned char strip_buf[bytes_per_strip + 1]; #else - strip_offset = (uint32_t*) _alloca(strip_count * sizeof(uint32_t)); - strip_bytes = (uint32_t*) _alloca(strip_count * sizeof(uint32_t)); + strip_offset = (uint32_t *) _alloca(strip_count * sizeof(uint32_t)); + strip_bytes = (uint32_t *) _alloca(strip_count * sizeof(uint32_t)); + strip_buf = (unsigned char *) _alloca(bytes_per_strip + 1); #endif - free_memory = 8; + free_memory = sizeof(tiff_header_t); - for(i = 0; i < strip_count; i++) { + for (i = 0; i < strip_count; i++) { strip_offset[i] = free_memory; if (i != (strip_count - 1)) { - strip_bytes[i] = rows_per_strip * symbol->bitmap_width * 3; + strip_bytes[i] = bytes_per_strip; } else { - if ((symbol->bitmap_height % rows_per_strip) != 0) { - strip_bytes[i] = (symbol->bitmap_height % rows_per_strip) * symbol->bitmap_width * 3; + if (rows_last_strip) { + strip_bytes[i] = rows_last_strip * ((symbol->bitmap_width + pixels_per_sample - 1) / pixels_per_sample) + * samples_per_pixel; } else { - strip_bytes[i] = rows_per_strip * symbol->bitmap_width * 3; + strip_bytes[i] = bytes_per_strip; } } free_memory += strip_bytes[i]; - if ((free_memory % 2) == 1) { - free_memory++; - } + } + if (free_memory & 1) { + free_memory++; // IFD must be on word boundary } if (free_memory > 0xffff0000) { @@ -133,184 +341,232 @@ INTERNAL int tif_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf) #endif tif_file = stdout; } else { - if (!(tif_file = fopen(symbol->outfile, "wb"))) { + if (!(tif_file = fopen(symbol->outfile, "wb+"))) { strcpy(symbol->errtxt, "672: Can't open output file"); return ZINT_ERROR_FILE_ACCESS; } + compression = TIF_LZW; + tif_lzw_init(&lzw_state); } /* Header */ - header.byte_order = 0x4949; +#ifdef TIF_BIG_ENDIAN + header.byte_order = 0x4D4D; // "MM" big-endian +#else + header.byte_order = 0x4949; // "II" little-endian +#endif header.identity = 42; header.offset = free_memory; fwrite(&header, sizeof(tiff_header_t), 1, tif_file); - free_memory += sizeof(tiff_ifd_t); + total_bytes_put = sizeof(tiff_header_t); /* Pixel data */ + pb = pixelbuf; strip = 0; + strip_row = 0; bytes_put = 0; for (row = 0; row < symbol->bitmap_height; row++) { - for (column = 0; column < symbol->bitmap_width; column++) { - switch(pixelbuf[(row * symbol->bitmap_width) + column]) { - case 'W': // White - putc(255, tif_file); - putc(255, tif_file); - putc(255, tif_file); - break; - case 'C': // Cyan - putc(0, tif_file); - putc(255, tif_file); - putc(255, tif_file); - break; - case 'B': // Blue - putc(0, tif_file); - putc(0, tif_file); - putc(255, tif_file); - break; - case 'M': // Magenta - putc(255, tif_file); - putc(0, tif_file); - putc(255, tif_file); - break; - case 'R': // Red - putc(255, tif_file); - putc(0, tif_file); - putc(0, tif_file); - break; - case 'Y': // Yellow - putc(255, tif_file); - putc(255, tif_file); - putc(0, tif_file); - break; - case 'G': // Green - putc(0, tif_file); - putc(255, tif_file); - putc(0, tif_file); - break; - case 'K': // Black - putc(0, tif_file); - putc(0, tif_file); - putc(0, tif_file); - break; - case '1': - putc(fgred, tif_file); - putc(fggrn, tif_file); - putc(fgblu, tif_file); - break; - default: - putc(bgred, tif_file); - putc(bggrn, tif_file); - putc(bgblu, tif_file); - break; + if (samples_per_pixel == 1) { + if (bits_per_sample == 1) { /* WHITEISZERO or BLACKISZERO */ + for (column = 0; column < symbol->bitmap_width; column += 8) { + unsigned char byte = 0; + for (i = 0; i < 8 && column + i < symbol->bitmap_width; i++, pb++) { + byte |= map[*pb] << (7 - i); + } + strip_buf[bytes_put++] = byte; + } + } else { /* bits_per_sample == 4, PALETTE_COLOR with no alpha */ + for (column = 0; column < symbol->bitmap_width; column += 2) { + unsigned char byte = map[*pb++] << 4; + if (column + 1 < symbol->bitmap_width) { + byte |= map[*pb++]; + } + strip_buf[bytes_put++] = byte; + } + } + } else if (samples_per_pixel == 2) { /* PALETTE_COLOR with alpha */ + for (column = 0; column < symbol->bitmap_width; column++) { + int idx = map[*pb++]; + strip_buf[bytes_put++] = idx; + strip_buf[bytes_put++] = palette[idx][3]; + } + } else { /* samples_per_pixel >= 4, RGB with alpha (4) or CMYK with (5) or without (4) alpha */ + for (column = 0; column < symbol->bitmap_width; column++) { + int idx = map[*pb++]; + memcpy(&strip_buf[bytes_put], &palette[idx], samples_per_pixel); + bytes_put += samples_per_pixel; } - bytes_put += 3; } - if (strip < strip_count && (bytes_put + 3) >= strip_bytes[strip]) { - // End of strip, pad if strip length is odd - if (strip_bytes[strip] % 2 == 1) { - putc(0, tif_file); + strip_row++; + + if (strip_row == rows_per_strip || (strip == strip_count - 1 && strip_row == rows_last_strip)) { + // End of strip + if (compression == TIF_LZW) { + file_pos = ftell(tif_file); + if (!tif_lzw_encode(&lzw_state, tif_file, strip_buf, bytes_put)) { /* Only fails if can't malloc */ + tif_lzw_cleanup(&lzw_state); + fclose(tif_file); /* Only use LZW if not STDOUT, so ok to close */ + strcpy(symbol->errtxt, "673: Failed to malloc LZW hash table"); + return ZINT_ERROR_MEMORY; + } + bytes_put = ftell(tif_file) - file_pos; + if (bytes_put != strip_bytes[strip]) { + int diff = bytes_put - strip_bytes[strip]; + strip_bytes[strip] = bytes_put; + for (i = strip + 1; i < strip_count; i++) { + strip_offset[i] += diff; + } + } + } else { + fwrite(strip_buf, 1, bytes_put, tif_file); } strip++; + total_bytes_put += bytes_put; bytes_put = 0; + strip_row = 0; + /* Suppress clang-analyzer-core.UndefinedBinaryOperatorResult */ + assert(strip < strip_count || row + 1 == symbol->bitmap_height); } } + if (total_bytes_put & 1) { + putc(0, tif_file); // IFD must be on word boundary + total_bytes_put++; + } + + if (compression == TIF_LZW) { + tif_lzw_cleanup(&lzw_state); + + file_pos = ftell(tif_file); + fseek(tif_file, 4, SEEK_SET); + free_memory = file_pos; + fwrite(&free_memory, 4, 1, tif_file); + fseek(tif_file, file_pos, SEEK_SET); + } + /* Image File Directory */ - ifd.entries = 14; - ifd.offset = 0; + tags[entries].tag = 0x0100; // ImageWidth + tags[entries].type = 3; // SHORT + tags[entries].count = 1; + tags[entries++].offset = symbol->bitmap_width; - ifd.new_subset.tag = 0xfe; - ifd.new_subset.type = 4; - ifd.new_subset.count = 1; - ifd.new_subset.offset = 0; + tags[entries].tag = 0x0101; // ImageLength - number of rows + tags[entries].type = 3; // SHORT + tags[entries].count = 1; + tags[entries++].offset = symbol->bitmap_height; - ifd.image_width.tag = 0x0100; - ifd.image_width.type = 3; // SHORT - ifd.image_width.count = 1; - ifd.image_width.offset = symbol->bitmap_width; + if (samples_per_pixel != 1 || bits_per_sample != 1) { + tags[entries].tag = 0x0102; // BitsPerSample + tags[entries].type = 3; // SHORT + tags[entries].count = samples_per_pixel; + if (samples_per_pixel == 1) { + tags[entries++].offset = bits_per_sample; + } else if (samples_per_pixel == 2) { /* 2 SHORTS fit into LONG offset so packed into offset */ + tags[entries++].offset = (bits_per_sample << 16) | bits_per_sample; + } else { + update_offsets[offsets++] = entries; + tags[entries++].offset = free_memory; + free_memory += samples_per_pixel * 2; + } + } - ifd.image_length.tag = 0x0101; - ifd.image_length.type = 3; // SHORT - ifd.image_length.count = 1; - ifd.image_length.offset = symbol->bitmap_height; + tags[entries].tag = 0x0103; // Compression + tags[entries].type = 3; // SHORT + tags[entries].count = 1; + tags[entries++].offset = compression; - ifd.bits_per_sample.tag = 0x0102; - ifd.bits_per_sample.type = 3; // SHORT - ifd.bits_per_sample.count = 3; - ifd.bits_per_sample.offset = free_memory; - free_memory += 6; + tags[entries].tag = 0x0106; // PhotometricInterpretation + tags[entries].type = 3; // SHORT + tags[entries].count = 1; + tags[entries++].offset = pmi; - ifd.compression.tag = 0x0103; - ifd.compression.type = 3; - ifd.compression.count = 1; - ifd.compression.offset = 1; // Uncompressed - - ifd.photometric.tag = 0x0106; - ifd.photometric.type = 3; // SHORT - ifd.photometric.count = 1; - ifd.photometric.offset = 2; // RGB Model - - ifd.strip_offsets.tag = 0x0111; - ifd.strip_offsets.type = 4; // LONG - ifd.strip_offsets.count = strip_count; + tags[entries].tag = 0x0111; // StripOffsets + tags[entries].type = 4; // LONG + tags[entries].count = strip_count; if (strip_count == 1) { - ifd.strip_offsets.offset = strip_offset[0]; + tags[entries++].offset = strip_offset[0]; } else { - ifd.strip_offsets.offset = free_memory; + update_offsets[offsets++] = entries; + tags[entries++].offset = free_memory; free_memory += strip_count * 4; } - ifd.samples_per_pixel.tag = 0x0115; - ifd.samples_per_pixel.type = 3; - ifd.samples_per_pixel.count = 1; - ifd.samples_per_pixel.offset = 3; + if (samples_per_pixel > 1) { + tags[entries].tag = 0x0115; // SamplesPerPixel + tags[entries].type = 3; // SHORT + tags[entries].count = 1; + tags[entries++].offset = samples_per_pixel; + } - ifd.rows_per_strip.tag = 0x0116; - ifd.rows_per_strip.type = 4; - ifd.rows_per_strip.count = 1; - ifd.rows_per_strip.offset = rows_per_strip; + tags[entries].tag = 0x0116; // RowsPerStrip + tags[entries].type = 4; // LONG + tags[entries].count = 1; + tags[entries++].offset = rows_per_strip; - ifd.strip_byte_counts.tag = 0x0117; - ifd.strip_byte_counts.type = 4; - ifd.strip_byte_counts.count = strip_count; + tags[entries].tag = 0x0117; // StripByteCounts + tags[entries].type = 4; // LONG + tags[entries].count = strip_count; if (strip_count == 1) { - ifd.strip_byte_counts.offset = strip_bytes[0]; + tags[entries++].offset = strip_bytes[0]; } else { - ifd.strip_byte_counts.offset = free_memory; + update_offsets[offsets++] = entries; + tags[entries++].offset = free_memory; free_memory += strip_count * 4; } - ifd.x_resolution.tag = 0x011a; - ifd.x_resolution.type = 5; - ifd.x_resolution.count = 1; - ifd.x_resolution.offset = free_memory; + tags[entries].tag = 0x011a; // XResolution + tags[entries].type = 5; // RATIONAL + tags[entries].count = 1; + update_offsets[offsets++] = entries; + tags[entries++].offset = free_memory; free_memory += 8; - ifd.y_resolution.tag = 0x011b; - ifd.y_resolution.type = 5; - ifd.y_resolution.count = 1; - ifd.y_resolution.offset = free_memory; -// free_memory += 8; + tags[entries].tag = 0x011b; // YResolution + tags[entries].type = 5; // RATIONAL + tags[entries].count = 1; + update_offsets[offsets++] = entries; + tags[entries++].offset = free_memory; + free_memory += 8; - ifd.planar_config.tag = 0x11c; - ifd.planar_config.type = 3; - ifd.planar_config.count = 1; - ifd.planar_config.offset = 1; + tags[entries].tag = 0x0128; // ResolutionUnit + tags[entries].type = 3; // SHORT + tags[entries].count = 1; + tags[entries++].offset = 2; // Inches - ifd.resolution_unit.tag = 0x0128; - ifd.resolution_unit.type = 3; - ifd.resolution_unit.count = 1; - ifd.resolution_unit.offset = 2; // Inches + if (color_map_size) { + tags[entries].tag = 0x0140; // ColorMap + tags[entries].type = 3; // SHORT + tags[entries].count = color_map_size * 3; + update_offsets[offsets++] = entries; + tags[entries++].offset = free_memory; + //free_memory += color_map_size * 3 * 2; /* Unnecessary as long as last use */ + } - fwrite(&ifd, sizeof(tiff_ifd_t), 1, tif_file); + if (extra_samples) { + tags[entries].tag = 0x0152; // ExtraSamples + tags[entries].type = 3; // SHORT + tags[entries].count = 1; + tags[entries++].offset = extra_samples; + } - /* Bits per sample */ - temp = 8; - fwrite(&temp, 2, 1, tif_file); // Red Bytes - fwrite(&temp, 2, 1, tif_file); // Green Bytes - fwrite(&temp, 2, 1, tif_file); // Blue Bytes + ifd_size = sizeof(entries) + sizeof(tiff_tag_t) * entries + sizeof(offset); + for (i = 0; i < offsets; i++) { + tags[update_offsets[i]].offset += ifd_size; + } + + fwrite(&entries, sizeof(entries), 1, tif_file); + fwrite(&tags, sizeof(tiff_tag_t), entries, tif_file); + fwrite(&offset, sizeof(offset), 1, tif_file); + total_bytes_put += ifd_size; + + if (samples_per_pixel > 2) { + for (i = 0; i < samples_per_pixel; i++) { + fwrite(&bits_per_sample, sizeof(bits_per_sample), 1, tif_file); + } + total_bytes_put += sizeof(bits_per_sample) * samples_per_pixel; + } if (strip_count != 1) { /* Strip offsets */ @@ -322,6 +578,7 @@ INTERNAL int tif_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf) for (i = 0; i < strip_count; i++) { fwrite(&strip_bytes[i], 4, 1, tif_file); } + total_bytes_put += strip_count * 8; } /* X Resolution */ @@ -329,16 +586,36 @@ INTERNAL int tif_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf) fwrite(&temp32, 4, 1, tif_file); temp32 = 1; fwrite(&temp32, 4, 1, tif_file); + total_bytes_put += 8; /* Y Resolution */ temp32 = 72; fwrite(&temp32, 4, 1, tif_file); temp32 = 1; fwrite(&temp32, 4, 1, tif_file); + total_bytes_put += 8; + + if (color_map_size) { + for (i = 0; i < color_map_size; i++) { + fwrite(&color_map[i].red, 2, 1, tif_file); + } + for (i = 0; i < color_map_size; i++) { + fwrite(&color_map[i].green, 2, 1, tif_file); + } + for (i = 0; i < color_map_size; i++) { + fwrite(&color_map[i].blue, 2, 1, tif_file); + } + total_bytes_put += 6 * color_map_size; + } if (symbol->output_options & BARCODE_STDOUT) { fflush(tif_file); } else { + if (ftell(tif_file) != total_bytes_put) { + fclose(tif_file); + strcpy(symbol->errtxt, "674: Failed to write all output"); + return ZINT_ERROR_FILE_WRITE; + } fclose(tif_file); } diff --git a/backend/tif.h b/backend/tif.h index 3e3c4596..80b31ec3 100644 --- a/backend/tif.h +++ b/backend/tif.h @@ -2,7 +2,7 @@ /* libzint - the open source barcode library - Copyright (C) 2016-2017 Robin Stuart + Copyright (C) 2016-2021 Robin Stuart Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -29,10 +29,12 @@ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TIF_H -#define TIF_H +/* vim: set ts=4 sw=4 et : */ -#ifdef __cplusplus +#ifndef TIF_H +#define TIF_H + +#ifdef __cplusplus extern "C" { #endif @@ -58,31 +60,16 @@ extern "C" { uint32_t offset; } tiff_tag_t; - typedef struct tiff_ifd { - uint16_t entries; - tiff_tag_t new_subset; - tiff_tag_t image_width; - tiff_tag_t image_length; - tiff_tag_t bits_per_sample; - tiff_tag_t compression; - tiff_tag_t photometric; - tiff_tag_t strip_offsets; - tiff_tag_t samples_per_pixel; - tiff_tag_t rows_per_strip; - tiff_tag_t strip_byte_counts; - tiff_tag_t x_resolution; - tiff_tag_t y_resolution; - tiff_tag_t planar_config; - tiff_tag_t resolution_unit; - uint32_t offset; - } tiff_ifd_t; + typedef struct tiff_color { + uint16_t red; + uint16_t green; + uint16_t blue; + } tiff_color_t; #pragma pack() -#ifdef __cplusplus +#ifdef __cplusplus } #endif -#endif /* TIF_H */ - - +#endif /* TIF_H */ diff --git a/backend/tif_lzw.h b/backend/tif_lzw.h new file mode 100644 index 00000000..d18b9b2e --- /dev/null +++ b/backend/tif_lzw.h @@ -0,0 +1,373 @@ +/* tif_lzw.h - LZW compression for TIFF + + libzint - the open source barcode library + Copyright (C) 2021 Robin Stuart + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the project nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + */ +/* vim: set ts=4 sw=4 et : */ + +#ifndef TIF_LZW_H +#define TIF_LZW_H + +/* + * Adapted from TIFF Library 4.2.0 libtiff/tif_lzw.c */ +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ +/* + * TIFF Library. + * Rev 5.0 Lempel-Ziv & Welch Compression Support + * + * This code is derived from the compress program whose code is + * derived from software contributed to Berkeley by James A. Woods, + * derived from original work by Spencer Thomas and Joseph Orost. + * + * The original Berkeley copyright notice appears below in its entirety. + */ +/* + * Copyright (c) 1985, 1986 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James A. Woods, derived from original work by Spencer Thomas + * and Joseph Orost. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define MAXCODE(n) ((1L << (n)) - 1) + +/* + * The TIFF spec specifies that encoded bit + * strings range from 9 to 12 bits. + */ +#define BITS_MIN 9 /* start with 9 bits */ +#define BITS_MAX 12 /* max of 12 bit strings */ +/* predefined codes */ +#define CODE_CLEAR 256 /* code to clear string table */ +#define CODE_EOI 257 /* end-of-information code */ +#define CODE_FIRST 258 /* first free code entry */ +#define CODE_MAX MAXCODE(BITS_MAX) +#define HSIZE 9001L /* 91% occupancy */ +#define HSHIFT (13 - 8) + +/* + * Encoding-specific state. + */ +typedef uint16_t tif_lzw_hcode; /* codes fit in 16 bits */ +typedef struct { + long hash; + tif_lzw_hcode code; +} tif_lzw_hash; + +#define CHECK_GAP 10000 /* ratio check interval */ + +/* + * State block. + */ +typedef struct { + tif_lzw_hash *enc_hashtab; /* kept separate for small machines */ +} tif_lzw_state; + +/* + * LZW Encoding. + */ + +/* + * Reset encoding hash table. + */ +static void tif_lzw_cl_hash(tif_lzw_state *sp) { + register tif_lzw_hash *hp = &sp->enc_hashtab[HSIZE - 1]; + register long i = HSIZE - 8; + + do { + i -= 8; + hp[-7].hash = -1; + hp[-6].hash = -1; + hp[-5].hash = -1; + hp[-4].hash = -1; + hp[-3].hash = -1; + hp[-2].hash = -1; + hp[-1].hash = -1; + hp[ 0].hash = -1; + hp -= 8; + } while (i >= 0); + + for (i += 8; i > 0; i--, hp--) { + hp->hash = -1; + } +} + +#define CALCRATIO(sp, rat) { \ + if (incount > 0x007fffff) { /* NB: shift will overflow */ \ + rat = outcount >> 8; \ + rat = (rat == 0 ? 0x7fffffff : incount / rat); \ + } else \ + rat = (incount << 8) / outcount; \ +} + +/* Explicit 0xff masking to make icc -check=conversions happy */ +#define PutNextCode(op_file, c) { \ + nextdata = (nextdata << nbits) | c; \ + nextbits += nbits; \ + putc((nextdata >> (nextbits - 8)) & 0xff, op_file); \ + nextbits -= 8; \ + if (nextbits >= 8) { \ + putc((nextdata >> (nextbits - 8)) & 0xff, op_file); \ + nextbits -= 8; \ + } \ + outcount += nbits; \ +} + +/* + * Encode a chunk of pixels. + * + * Uses an open addressing double hashing (no chaining) on the + * prefix code/next character combination. We do a variant of + * Knuth's algorithm D (vol. 3, sec. 6.4) along with G. Knott's + * relatively-prime secondary probe. Here, the modular division + * first probe is gives way to a faster exclusive-or manipulation. + * Also do block compression with an adaptive reset, whereby the + * code table is cleared when the compression ratio decreases, + * but after the table fills. The variable-length output codes + * are re-sized at this point, and a CODE_CLEAR is generated + * for the decoder. + */ +static int tif_lzw_encode(tif_lzw_state *sp, FILE *op_file, const unsigned char *bp, int cc) { + register long fcode; + register tif_lzw_hash *hp; + register int h, c; + tif_lzw_hcode ent; + long disp; + + int nbits; /* # of bits/code */ + int maxcode; /* maximum code for nbits */ + int free_ent; /* next free entry in hash table */ + unsigned long nextdata; /* next bits of i/o */ + long nextbits; /* # of valid bits in nextdata */ + long checkpoint; /* point at which to clear table */ + long ratio; /* current compression ratio */ + long incount; /* (input) data bytes encoded */ + long outcount; /* encoded (output) bytes */ + + /* + * Reset encoding state at the start of a strip. + */ + if (sp->enc_hashtab == NULL) { + sp->enc_hashtab = (tif_lzw_hash *) malloc(HSIZE * sizeof(tif_lzw_hash)); + if (sp->enc_hashtab == NULL) { + return 0; + } + } + + tif_lzw_cl_hash(sp); /* clear hash table */ + + nbits = BITS_MIN; + maxcode = MAXCODE(BITS_MIN); + free_ent = CODE_FIRST; + nextdata = 0; + nextbits = 0; + checkpoint = CHECK_GAP; + ratio = 0; + incount = 0; + outcount = 0; + + ent = (tif_lzw_hcode) -1; + + if (cc > 0) { + PutNextCode(op_file, CODE_CLEAR); + ent = *bp++; cc--; incount++; + } + while (cc > 0) { + c = *bp++; cc--; incount++; + fcode = ((long)c << BITS_MAX) + ent; + h = (c << HSHIFT) ^ ent; /* xor hashing */ +#ifdef _WINDOWS + /* + * Check hash index for an overflow. + */ + if (h >= HSIZE) { + h -= HSIZE; + } +#endif + hp = &sp->enc_hashtab[h]; + if (hp->hash == fcode) { + ent = hp->code; + continue; + } + if (hp->hash >= 0) { + /* + * Primary hash failed, check secondary hash. + */ + disp = HSIZE - h; + if (h == 0) { + disp = 1; + } + do { + /* + * Avoid pointer arithmetic because of + * wraparound problems with segments. + */ + if ((h -= disp) < 0) { + h += HSIZE; + } + hp = &sp->enc_hashtab[h]; + if (hp->hash == fcode) { + ent = hp->code; + goto hit; + } + } while (hp->hash >= 0); + } + /* + * New entry, emit code and add to table. + */ + PutNextCode(op_file, ent); + ent = (tif_lzw_hcode) c; + hp->code = (tif_lzw_hcode) (free_ent++); + hp->hash = fcode; + if (free_ent == CODE_MAX - 1) { + /* table is full, emit clear code and reset */ + tif_lzw_cl_hash(sp); + ratio = 0; + incount = 0; + outcount = 0; + free_ent = CODE_FIRST; + PutNextCode(op_file, CODE_CLEAR); + nbits = BITS_MIN; + maxcode = MAXCODE(BITS_MIN); + } else { + /* + * If the next entry is going to be too big for + * the code size, then increase it, if possible. + */ + if (free_ent > maxcode) { + nbits++; + assert(nbits <= BITS_MAX); + maxcode = (int) MAXCODE(nbits); + } else if (incount >= checkpoint) { + long rat; + /* + * Check compression ratio and, if things seem + * to be slipping, clear the hash table and + * reset state. The compression ratio is a + * 24+8-bit fractional number. + */ + checkpoint = incount + CHECK_GAP; + CALCRATIO(sp, rat); + if (rat <= ratio) { + tif_lzw_cl_hash(sp); + ratio = 0; + incount = 0; + outcount = 0; + free_ent = CODE_FIRST; + PutNextCode(op_file, CODE_CLEAR); + nbits = BITS_MIN; + maxcode = MAXCODE(BITS_MIN); + } else { + ratio = rat; + } + } + } + hit: + ; + } + + /* + * Finish off an encoded strip by flushing the last + * string and tacking on an End Of Information code. + */ + if (ent != (tif_lzw_hcode) -1) { + + PutNextCode(op_file, ent); + free_ent++; + + if (free_ent == CODE_MAX - 1) { + /* table is full, emit clear code and reset */ + outcount = 0; + PutNextCode(op_file, CODE_CLEAR); + nbits = BITS_MIN; + } else { + /* + * If the next entry is going to be too big for + * the code size, then increase it, if possible. + */ + if (free_ent > maxcode) { + nbits++; + assert(nbits <= BITS_MAX); + } + } + } + PutNextCode(op_file, CODE_EOI); + /* Explicit 0xff masking to make icc -check=conversions happy */ + if (nextbits > 0) { + putc((nextdata << (8 - nextbits)) & 0xff, op_file); + } + + return 1; +} + +static void tif_lzw_cleanup(tif_lzw_state *sp) { + if (sp->enc_hashtab) { + free(sp->enc_hashtab); + } +} + +static void tif_lzw_init(tif_lzw_state *sp) { + sp->enc_hashtab = NULL; +} + +#endif /* TIF_LZW_H */ diff --git a/backend/zint.h b/backend/zint.h index 90d58b8c..e781159f 100644 --- a/backend/zint.h +++ b/backend/zint.h @@ -275,6 +275,7 @@ extern "C" { #define ZINT_ERROR_ENCODING_PROBLEM 9 #define ZINT_ERROR_FILE_ACCESS 10 #define ZINT_ERROR_MEMORY 11 +#define ZINT_ERROR_FILE_WRITE 12 // File types #define OUT_BUFFER 0 diff --git a/docs/manual.txt b/docs/manual.txt index f2d3f436..c0a98b8a 100644 --- a/docs/manual.txt +++ b/docs/manual.txt @@ -1037,6 +1037,9 @@ ZINT_ERROR_FILE_ACCESS | Zint was unable to open the requested output | problem. ZINT_ERROR_MEMORY | Zint ran out of memory. This should only be a | problem with legacy systems. +ZINT_ERROR_FILE_WRITE | Zint failed to write all contents to the + | requested output file. This should only occur + | if the output disk becomes full. -------------------------------------------------------------------------------- To catch errors use an integer variable as shown in the code below: diff --git a/frontend_qt/frontend_qt.pro b/frontend_qt/frontend_qt.pro index e3f0a639..bd25f8c7 100644 --- a/frontend_qt/frontend_qt.pro +++ b/frontend_qt/frontend_qt.pro @@ -53,6 +53,7 @@ HEADERS += barcodeitem.h \ ..\backend\sjis.h \ ..\backend\stdint_msvc.h \ ..\backend\tif.h \ + ..\backend\tif_lzw.h \ ..\backend\zint.h \ ..\backend\zintconfig.h \ diff --git a/frontend_qt/mainWindow.ui b/frontend_qt/mainWindow.ui index 1a50e829..7bd674fc 100644 --- a/frontend_qt/mainWindow.ui +++ b/frontend_qt/mainWindow.ui @@ -936,10 +936,10 @@ p, li { white-space: pre-wrap; } - Use CMYK colour space in EPS output + Use CMYK colour space in EPS/TIF output - CMY&K (EPS) + CMY&K (EPS/TIF) false diff --git a/win32/libzint.vcxproj b/win32/libzint.vcxproj index 651519bf..581fc1e0 100644 --- a/win32/libzint.vcxproj +++ b/win32/libzint.vcxproj @@ -210,6 +210,7 @@ + diff --git a/win32/vs2008/libzint.vcproj b/win32/vs2008/libzint.vcproj index 4af9e7cb..015edc9b 100644 --- a/win32/vs2008/libzint.vcproj +++ b/win32/vs2008/libzint.vcproj @@ -617,6 +617,10 @@ RelativePath="..\backend\tif.h" > + + diff --git a/win32/vs2015/libzint.vcxproj b/win32/vs2015/libzint.vcxproj index f08100cd..66b8bacb 100644 --- a/win32/vs2015/libzint.vcxproj +++ b/win32/vs2015/libzint.vcxproj @@ -389,6 +389,7 @@ + diff --git a/win32/vs2015/vsx/libzintMD.vcxproj b/win32/vs2015/vsx/libzintMD.vcxproj index aa6a7ab2..9c7f4b52 100644 --- a/win32/vs2015/vsx/libzintMD.vcxproj +++ b/win32/vs2015/vsx/libzintMD.vcxproj @@ -157,6 +157,7 @@ + diff --git a/win32/vs2019/libzint.vcxproj b/win32/vs2019/libzint.vcxproj index 8baece13..473ea5c0 100644 --- a/win32/vs2019/libzint.vcxproj +++ b/win32/vs2019/libzint.vcxproj @@ -210,6 +210,7 @@ +