From 954d7e9021f276f3bc0a0b8d9d0c7f7d77647642 Mon Sep 17 00:00:00 2001 From: Akira Kakuto Date: Mon, 28 Dec 2015 14:22:28 +0000 Subject: texk/dvipdfm-x: patches from S. Hirata git-svn-id: svn://tug.org/texlive/trunk@39216 c570f23f-e606-0410-a88d-b1316a301751 --- Build/source/texk/dvipdfm-x/ChangeLog | 7 + Build/source/texk/dvipdfm-x/bmpimage.c | 6 + Build/source/texk/dvipdfm-x/dvipdfmx.c | 5 + Build/source/texk/dvipdfm-x/pdfobj.c | 406 +++++++++++++++++++++++++++++++-- Build/source/texk/dvipdfm-x/pdfobj.h | 10 +- Build/source/texk/dvipdfm-x/pngimage.c | 13 +- 6 files changed, 420 insertions(+), 27 deletions(-) (limited to 'Build/source') diff --git a/Build/source/texk/dvipdfm-x/ChangeLog b/Build/source/texk/dvipdfm-x/ChangeLog index 6b441147289..dfaf2a28938 100644 --- a/Build/source/texk/dvipdfm-x/ChangeLog +++ b/Build/source/texk/dvipdfm-x/ChangeLog @@ -1,3 +1,10 @@ +2015-12-28 Shunsaku Hirata + + * bmpimage.c, dvipdfmx.c, pdfobj.[ch], pngimage.c: Support for predictor + functions for FlateDecode filter. PNG optimal and TIFF2 predictor functions + are currently implemented. It solves the preblem that PNG images become + sometimes larger in size when embedded in PDF file. + 2015-12-26 Shunsaku Hirata * pngimage.c: Workaround for Adobe Potoshop generated PNGs which have diff --git a/Build/source/texk/dvipdfm-x/bmpimage.c b/Build/source/texk/dvipdfm-x/bmpimage.c index 01b1993d520..d2c9428d5f1 100644 --- a/Build/source/texk/dvipdfm-x/bmpimage.c +++ b/Build/source/texk/dvipdfm-x/bmpimage.c @@ -269,6 +269,12 @@ bmp_include_image (pdf_ximage *ximage, FILE *fp) RELEASE(stream_data_ptr); } + /* Predictor is usually not so efficient for indexed images. */ + if (hdr.bit_count >= 24 && info.bits_per_component >= 8 && + info.height > 64) { + pdf_stream_set_predictor(stream, 15, info.width, + info.bits_per_component, info.num_components); + } pdf_ximage_set_image(ximage, &info, stream); return 0; diff --git a/Build/source/texk/dvipdfm-x/dvipdfmx.c b/Build/source/texk/dvipdfm-x/dvipdfmx.c index 665339d0221..70cf84d4e5c 100644 --- a/Build/source/texk/dvipdfm-x/dvipdfmx.c +++ b/Build/source/texk/dvipdfm-x/dvipdfmx.c @@ -79,6 +79,7 @@ static int opt_flags = 0; #define OPT_CIDFONT_FIXEDPITCH (1 << 2) #define OPT_FONTMAP_FIRST_MATCH (1 << 3) #define OPT_PDFDOC_NO_DEST_REMOVE (1 << 4) +#define OPT_PDFOBJ_NO_PREDICTOR (1 << 5) static char ignore_colors = 0; static double annot_grow = 0.0; @@ -199,6 +200,7 @@ show_usage (void) printf ("\t\t 0x0004 Treat all CIDFont as fixed-pitch font.\n"); printf ("\t\t 0x0008 Do not replace duplicate fontmap entries.\n"); printf ("\t\t 0x0010 Do not optimize PDF destinations.\n"); + printf ("\t\t 0x0020 Do not use predictor filter for Flate compression.\n"); printf ("\t\tPositive values are always ORed with previously given flags.\n"); printf ("\t\tAnd negative values replace old values.\n"); printf (" -D template\tPS->PDF conversion command line template [none]\n"); @@ -1048,6 +1050,9 @@ main (int argc, char *argv[]) if (opt_flags & OPT_TPIC_TRANSPARENT_FILL) tpic_set_fill_mode(1); + if (opt_flags & OPT_PDFOBJ_NO_PREDICTOR) + pdf_set_use_predictor(0); /* No prediction */ + if (mp_mode) { do_mps_pages(); } else { diff --git a/Build/source/texk/dvipdfm-x/pdfobj.c b/Build/source/texk/dvipdfm-x/pdfobj.c index 493e4c6528e..d11ab132660 100644 --- a/Build/source/texk/dvipdfm-x/pdfobj.c +++ b/Build/source/texk/dvipdfm-x/pdfobj.c @@ -26,6 +26,8 @@ #include #include +/* floor and abs */ +#include #include "system.h" #include "mem.h" @@ -102,6 +104,25 @@ struct pdf_dict struct pdf_dict *next; }; +/* DecodeParms for FlateDecode */ + struct decode_parms { + int predictor; + int colors; + int bits_per_component; + int32_t columns; + }; + +/* 2015/12/27 Added support for predictor functions + * + * There are yet no way to specify the use of predictor functions. + * Using TIFF2 or PNG predictor usually gives better compression for images + * but there is a case that compression speed becomes significantly slower. + * Please use -C 0x20 option to disable the use of predictor functions. + * + * See, e.g., for a heuristic approach for selecting filters + * http://www.w3.org/TR/PNG-Encoders.html#E.Filter-selection + */ + struct pdf_stream { struct pdf_obj *dict; @@ -109,7 +130,8 @@ struct pdf_stream int *objstm_data; /* used for object streams */ unsigned int stream_length; unsigned int max_length; - unsigned char _flags; + int32_t _flags; + struct decode_parms decodeparms; }; struct pdf_indirect @@ -221,6 +243,7 @@ static void release_stream (pdf_stream *stream); static int verbose = 0; static char compression_level = 9; +static char compression_use_predictor = 1; void pdf_set_compression (int level) @@ -242,6 +265,12 @@ pdf_set_compression (int level) return; } +void +pdf_set_use_predictor (int bval) +{ + compression_use_predictor = bval ? 1 : 0; +} + static unsigned pdf_version = PDF_VERSION_DEFAULT; void @@ -1570,12 +1599,316 @@ pdf_new_stream (int flags) data->max_length = 0; data->objstm_data = NULL; + data->decodeparms.predictor = 2; + data->decodeparms.columns = 0; + data->decodeparms.bits_per_component = 0; + data->decodeparms.colors = 0; + result->data = data; result->flags |= OBJ_NO_OBJSTM; return result; } +void +pdf_stream_set_predictor (pdf_obj *stream, + int predictor, int32_t columns, int bpc, int colors) +{ + struct pdf_stream *data; + + if (pdf_obj_typeof(stream) != PDF_STREAM) + return; + else if (columns < 0 || bpc < 0 || colors < 0) + return; + + data = (struct pdf_stream *) stream->data; + data->decodeparms.predictor = predictor; + data->decodeparms.columns = columns; + data->decodeparms.bits_per_component = bpc; + data->decodeparms.colors = colors; + data->_flags |= STREAM_USE_PREDICTOR; +} + +/* Adaptive PNG filter + * We use the "minimum sum of absolute differences" heuristic approach + * for finding the most optimal filter to be used. + * + * From http://www.libpng.org/pub/png/book/chapter09.html + * + * For grayscale and truecolor images of 8 or more bits per sample, with or + * without alpha channels, dynamic filtering is almost always beneficial. The + * approach that has by now become standard is known as the minimum sum of + * absolute differences heuristic and was first proposed by Lee Daniel + * Crocker in February 1995. + */ +static unsigned char * +filter_PNG15_apply_filter (unsigned char *raster, + int32_t columns, int32_t rows, + int8_t bpc, int8_t colors, int32_t *length) +{ + unsigned char *dst; + int bits_per_pixel = colors * bpc; + int bytes_per_pixel = (bits_per_pixel + 7) / 8; + int32_t rowbytes = columns * bytes_per_pixel; + int32_t i, j; + + ASSERT(raster && length); + + /* Result */ + dst = NEW((rowbytes+1)*rows, unsigned char); + *length = (rowbytes + 1) * rows; + + for (j = 0; j < rows; j++) { + int type = 0; + unsigned char *pp = dst + j * (rowbytes + 1); + unsigned char *p = raster + j * rowbytes; + uint32_t sum[5] = {0, 0, 0, 0, 0}; + /* First calculated sum of values to make a heuristic guess + * of optimal predictor function. + */ + for (i = 0; i < rowbytes; i++) { + int left = (i - bytes_per_pixel >= 0) ? p[i - bytes_per_pixel] : 0; + int up = (j > 0) ? *(p+i-rowbytes) : 0; + int uplft = (j > 0) ? + ((i - bytes_per_pixel >= 0) ? + *(p+i-rowbytes-bytes_per_pixel) : 0) : 0; + /* Type 0 -- None */ + sum[0] += p[i]; + /* Type 1 -- Sub */ + sum[1] += abs((int) p[i] - left); + /* Type 2 -- Up */ + sum[2] += abs((int) p[i] - up); + /* Type 3 -- Average */ + { + int tmp = floor((up + left) / 2); + sum[3] += abs((int) p[i] - tmp); + } + /* Type 4 -- Peath */ + { + int q = left + up - uplft; + int qa = abs(q - left), qb = abs(q - up), qc = abs(q - uplft); + if (qa <= qb && qa <= qc) + sum[4] += abs((int) p[i] - left); + else if (qb <= qc) + sum[4] += abs((int) p[i] - up); + else + sum[4] += abs((int) p[i] - uplft); + } + } + { + int min = sum[0], min_idx = 0; + for (i = 0; i < 5; i++) { + if (sum[i] < min) { + min = sum[i]; min_idx = i; + } + } + type = min_idx; + } + /* Now we actually apply filter. */ + pp[0] = type; + switch (type) { + case 0: + memcpy(pp+1, p, rowbytes); + break; + case 1: + for (i = 0; i < rowbytes; i++) { + int left = (i - bytes_per_pixel >= 0) ? p[i - bytes_per_pixel] : 0; + pp[i+1] = p[i] - left; + } + break; + case 2: + for (i = 0; i < rowbytes; i++) { + int up = (j > 0) ? *(p+i - rowbytes) : 0; + pp[i+1] = p[i] - up; + } + break; + case 3: + { + for (i = 0; i < rowbytes; i++) { + int up = (j > 0) ? *(p+i-rowbytes) : 0; + int left = (i - bytes_per_pixel >= 0) ? p[i - bytes_per_pixel] : 0; + int tmp = floor((up + left) / 2); + pp[i+1] = p[i] - tmp; + } + } + break; + case 4: /* Peath */ + { + for (i = 0; i < rowbytes; i++) { + int up = (j > 0) ? *(p+i-rowbytes) : 0; + int left = (i - bytes_per_pixel >= 0) ? p[i - bytes_per_pixel] : 0; + int uplft = (j > 0) ? + ((i - bytes_per_pixel >= 0) ? + *(p+i-rowbytes-bytes_per_pixel) : 0) : 0; + int q = left + up - uplft; + int qa = abs(q - left), qb = abs(q - up), qc = abs(q - uplft); + if (qa <= qb && qa <= qc) + pp[i+1] = p[i] - left; + else if (qb <= qc) + pp[i+1] = p[i] - up; + else + pp[i+1] = p[i] - uplft; + } + } + break; + } + } + + return dst; +} + +/* TIFF predictor filter support + * + * Many PDF viewers seems to have broken TIFF 2 predictor support? + * Ony GhostScript and MuPDF render 4bpc grayscale image with TIFF 2 predictor + * filter applied correctly. + * + * Acrobat Reader DC 2015.007.20033 NG + * Adobe Acrobat X 10.1.13 NG + * Foxit Reader 4.1.5.425 NG + * GhostScript 9.16 OK + * SumatraPDF(MuPDF) v3.0 OK + * Evince(poppler) 2.32.0.145 NG (1bit and 4bit broken) + */ + +/* This modifies "raster" itself! */ +static void +apply_filter_TIFF2_1_2_4 (unsigned char *raster, + int32_t width, int32_t height, + int8_t bpc, int8_t num_comp) +{ + int32_t rowbytes = (bpc * num_comp * width + 7) / 8; + uint8_t mask = (1 << bpc) - 1; + uint16_t *prev; + int32_t i, j; + + ASSERT(raster); + ASSERT( bpc > 0 && bpc <= 8 ); + + prev = NEW(num_comp, uint16_t); + + /* Generic routine for 1 to 16 bit. + * It supports, e.g., 7 bpc images too. + * Actually, it is not necessary to have 16 bit inbuf and outbuf + * since we only need 1, 2, and 4 bit support here. 8 bit is enough. + */ + for (j = 0; j < height; j++) { + int32_t k, l, inbits, outbits; + uint16_t inbuf, outbuf; + int c; + + memset(prev, 0, sizeof(uint16_t)*num_comp); + inbuf = outbuf = 0; inbits = outbits = 0; + l = k = j * rowbytes; + for (i = 0; i < width; i++) { + for (c = 0; c < num_comp; c++) { + uint8_t cur; + int8_t sub; + if (inbits < bpc) { /* need more byte */ + inbuf = (inbuf << 8) | raster[l]; l++; + inbits += 8; + } + cur = (inbuf >> (inbits - bpc)) & mask; + inbits -= bpc; /* consumed bpc bits */ + sub = cur - prev[c]; + prev[c] = cur; + if (sub < 0) + sub += (1 << bpc); + /* Append newly filtered component value */ + outbuf = (outbuf << bpc) | sub; + outbits += bpc; + /* flush */ + if (outbits >= 8) { + raster[k] = (outbuf >> (outbits - 8)); k++; + outbits -= 8; + } + } + } + if (outbits > 0) + raster[k] = (outbuf << (8 - outbits)); k++; + } + RELEASE(prev); +} + +unsigned char * +filter_TIFF2_apply_filter (unsigned char *raster, + int32_t columns, int32_t rows, + int8_t bpc, int8_t colors, int32_t *length) +{ + unsigned char *dst; + uint16_t *prev; + int32_t rowbytes = (bpc * colors * columns + 7) / 8; + int32_t i, j; + + ASSERT(raster && length); + + dst = NEW(rowbytes*rows, unsigned char); + memcpy(dst, raster, rowbytes*rows); + *length = rowbytes * rows; + + switch (bpc) { + case 1: case 2: case 4: + apply_filter_TIFF2_1_2_4(dst, columns, rows, bpc, colors); + break; + + case 8: + prev = NEW(colors, uint16_t); + for (j = 0; j < rows; j++) { + memset(prev, 0, sizeof(uint16_t)*colors); + for (i = 0; i < columns; i++) { + int c; + int32_t pos = colors * (columns * j + i); + for (c = 0; c < colors; c++) { + uint8_t cur = raster[pos+c]; + int32_t sub = cur - prev[c]; + prev[c] = cur; + dst[pos+c] = sub; + } + } + } + RELEASE(prev); + break; + + case 16: + prev = NEW(colors, uint16_t); + for (j = 0; j < rows; j++) { + memset(prev, 0, sizeof(uint16_t)*colors); + for (i = 0; i < columns; i++) { + int c; + int32_t pos = 2 * colors * (columns * j + i); + for (c = 0; c < colors; c++) { + uint16_t cur = ((uint8_t)raster[pos+2*c])*256 + + (uint8_t)raster[pos+2*c+1]; + uint16_t sub = cur - prev[c]; + prev[c] = cur; + dst[pos+2*c ] = (sub >> 8) & 0xff; + dst[pos+2*c+1] = sub & 0xff; + } + } + } + RELEASE(prev); + break; + + } + + return dst; +} + +static pdf_obj * +filter_create_predictor_dict (int predictor, int32_t columns, + int bpc, int colors) +{ + pdf_obj *parms; + + parms = pdf_new_dict(); + pdf_add_dict(parms, pdf_new_name("BitsPerComponent"), pdf_new_number(bpc)); + pdf_add_dict(parms, pdf_new_name("Colors"), pdf_new_number(colors)); + pdf_add_dict(parms, pdf_new_name("Columns"), pdf_new_number(columns)); + pdf_add_dict(parms, pdf_new_name("Predictor"), pdf_new_number(predictor)); + + return parms; +} + static void write_stream (pdf_stream *stream, FILE *file) { @@ -1609,8 +1942,54 @@ write_stream (pdf_stream *stream, FILE *file) if (stream->stream_length > 0 && (stream->_flags & STREAM_COMPRESS) && compression_level > 0) { + pdf_obj *filters; + + /* First apply predictor filter if requested. */ + if ( compression_use_predictor && + (stream->_flags & STREAM_USE_PREDICTOR) && + !pdf_lookup_dict(stream->dict, "DecodeParms")) { + int bits_per_pixel = stream->decodeparms.colors * + stream->decodeparms.bits_per_component; + int32_t len = (stream->decodeparms.columns * bits_per_pixel + 7) / 8; + int32_t rows = stream->stream_length / len; + unsigned char *filtered2 = NULL; + int32_t length2 = stream->stream_length; + pdf_obj *parms; + + parms = filter_create_predictor_dict(stream->decodeparms.predictor, + stream->decodeparms.columns, + stream->decodeparms.bits_per_component, + stream->decodeparms.colors); + + switch (stream->decodeparms.predictor) { + case 2: /* TIFF2 */ + filtered2 = filter_TIFF2_apply_filter(filtered, + stream->decodeparms.columns, + rows, + stream->decodeparms.bits_per_component, + stream->decodeparms.colors, &length2); + break; + case 15: /* PNG optimun */ + filtered2 = filter_PNG15_apply_filter(filtered, + stream->decodeparms.columns, + rows, + stream->decodeparms.bits_per_component, + stream->decodeparms.colors, &length2); + break; + default: + WARN("Unknown/unsupported Predictor function %d.", + stream->decodeparms.predictor); + break; + } + if (parms && filtered2) { + RELEASE(filtered); + filtered = filtered2; + filtered_length = length2; + pdf_add_dict(stream->dict, pdf_new_name("DecodeParms"), parms); + } + } - pdf_obj *filters = pdf_lookup_dict(stream->dict, "Filter"); + filters = pdf_lookup_dict(stream->dict, "Filter"); buffer_length = filtered_length + filtered_length/1000 + 14; buffer = NEW(buffer_length, unsigned char); @@ -1824,18 +2203,6 @@ pdf_add_stream_flate (pdf_obj *dst, const void *data, int len) return (inflateEnd(&z) == Z_OK ? 0 : -1); } - -/* DecodeParms for FlateDecode - * - */ - struct decode_parms { - int predictor; - int colors; - int bits_per_component; - int columns; - /* EarlyChange unsupported */ - }; - static int get_decode_parms (struct decode_parms *parms, pdf_obj *dict) { @@ -1879,24 +2246,18 @@ get_decode_parms (struct decode_parms *parms, pdf_obj *dict) /* From Xpdf version 3.04 * I'm not sure if I properly ported... Untested. */ -#define PREDICTOR_TIFF2_MAX_COLORS 32 static int filter_row_TIFF2 (unsigned char *dst, const unsigned char *src, struct decode_parms *parms) { const unsigned char *p = src; - unsigned char col[PREDICTOR_TIFF2_MAX_COLORS]; + unsigned char *col; /* bits_per_component < 8 here */ int mask = (1 << parms->bits_per_component) - 1; int inbuf, outbuf; /* 2 bytes buffer */ int i, ci, j, k, inbits, outbits; - if (parms->colors > PREDICTOR_TIFF2_MAX_COLORS) { - WARN("Sorry, Colors value > %d not supported for TIFF 2 predictor", - PREDICTOR_TIFF2_MAX_COLORS); - return -1; - } - + col = NEW(parms->colors, unsigned char); memset(col, 0, parms->colors); inbuf = outbuf = 0; inbits = outbits = 0; j = k = 0; @@ -1924,6 +2285,7 @@ filter_row_TIFF2 (unsigned char *dst, const unsigned char *src, if (outbits > 0) { dst[k] = (unsigned char) (outbuf << (8 - outbits)); } + RELEASE(col); return 0; } diff --git a/Build/source/texk/dvipdfm-x/pdfobj.h b/Build/source/texk/dvipdfm-x/pdfobj.h index 048a4a958b9..8c47f27ba69 100644 --- a/Build/source/texk/dvipdfm-x/pdfobj.h +++ b/Build/source/texk/dvipdfm-x/pdfobj.h @@ -25,6 +25,7 @@ #include + /* Here is the complete list of PDF object types */ #define PDF_BOOLEAN 1 @@ -41,6 +42,7 @@ #define PDF_OBJ_INVALID 0 #define STREAM_COMPRESS (1 << 0) +#define STREAM_USE_PREDICTOR (1 << 1) /* A deeper object hierarchy will be considered as (illegal) loop. */ #define PDF_OBJ_MAX_DEPTH 30 @@ -160,10 +162,9 @@ extern void pdf_stream_set_flags (pdf_obj *stream, int flags); extern int pdf_stream_get_flags (pdf_obj *stream); #endif extern const void *pdf_stream_dataptr (pdf_obj *stream); - -#if 0 -extern int pdf_stream_pop_filter (pdf_obj *stream); -#endif +extern void pdf_stream_set_predictor (pdf_obj *stream, + int predictor, int32_t columns, + int bpc, int colors); /* Compare label of two indirect reference object. */ @@ -173,6 +174,7 @@ extern int pdf_compare_reference (pdf_obj *ref1, pdf_obj *ref2); */ extern void pdf_set_compression (int level); +extern void pdf_set_use_predictor (int bval); extern void pdf_set_info (pdf_obj *obj); extern void pdf_set_root (pdf_obj *obj); diff --git a/Build/source/texk/dvipdfm-x/pngimage.c b/Build/source/texk/dvipdfm-x/pngimage.c index 7ccec5b91fe..4651f9088f3 100644 --- a/Build/source/texk/dvipdfm-x/pngimage.c +++ b/Build/source/texk/dvipdfm-x/pngimage.c @@ -263,6 +263,8 @@ png_include_image (pdf_ximage *ximage, FILE *png_file) */ break; } + info.num_components = 1; + break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: @@ -331,6 +333,10 @@ png_include_image (pdf_ximage *ximage, FILE *png_file) if (trans_type == PDF_TRANS_TYPE_BINARY) pdf_add_dict(stream_dict, pdf_new_name("Mask"), mask); else if (trans_type == PDF_TRANS_TYPE_ALPHA) { + if (info.bits_per_component >= 8 && info.width > 64) { + pdf_stream_set_predictor(mask, 2, info.width, + info.bits_per_component, 1); + } pdf_add_dict(stream_dict, pdf_new_name("SMask"), pdf_ref_obj(mask)); pdf_release_obj(mask); } else { @@ -394,7 +400,12 @@ png_include_image (pdf_ximage *ximage, FILE *png_file) png_destroy_info_struct(png_ptr, &png_info_ptr); if (png_ptr) png_destroy_read_struct(&png_ptr, NULL, NULL); - + if (color_type != PNG_COLOR_TYPE_PALETTE && + info.bits_per_component >= 8 && + info.height > 64) { + pdf_stream_set_predictor(stream, 15, info.width, + info.bits_per_component, info.num_components); + } pdf_ximage_set_image(ximage, &info, stream); return 0; -- cgit v1.2.3