xref: /plan9/sys/src/cmd/gs/src/gdevpdfj.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 2000 Aladdin Enterprises.  All rights reserved.
2 
3   This software is provided AS-IS with no warranty, either express or
4   implied.
5 
6   This software is distributed under license and may not be copied,
7   modified or distributed except as expressly authorized under the terms
8   of the license contained in the file LICENSE in this distribution.
9 
10   For more information about licensing, please refer to
11   http://www.ghostscript.com/licensing/. For information on
12   commercial licensing, go to http://www.artifex.com/licensing/ or
13   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15 */
16 
17 /* $Id: gdevpdfj.c,v 1.49 2005/09/06 13:47:10 leonardo Exp $ */
18 /* Image-writing utilities for pdfwrite driver */
19 #include "memory_.h"
20 #include "string_.h"
21 #include "gx.h"
22 #include "gserrors.h"
23 #include "gdevpdfx.h"
24 #include "gdevpdfg.h"
25 #include "gdevpdfo.h"
26 #include "gxcspace.h"
27 #include "gsiparm4.h"
28 #include "gdevpsds.h"
29 #include "spngpx.h"
30 
31 #define CHECK(expr)\
32   BEGIN if ((code = (expr)) < 0) return code; END
33 
34 /* GC descriptors */
35 public_st_pdf_image_writer();
36 private ENUM_PTRS_WITH(pdf_image_writer_enum_ptrs, pdf_image_writer *piw)
37      index -= 4;
38      if (index < psdf_binary_writer_max_ptrs * piw->alt_writer_count) {
39 	 gs_ptr_type_t ret =
40 	     ENUM_USING(st_psdf_binary_writer, &piw->binary[index / psdf_binary_writer_max_ptrs],
41 			sizeof(psdf_binary_writer), index % psdf_binary_writer_max_ptrs);
42 
43 	 if (ret == 0)		/* don't stop early */
44 	     ENUM_RETURN(0);
45 	 return ret;
46     }
47     return 0;
48 case 0: ENUM_RETURN(piw->pres);
49 case 1: ENUM_RETURN(piw->data);
50 case 2: ENUM_RETURN(piw->named);
51 case 3: ENUM_RETURN(piw->pres_mask);
52 ENUM_PTRS_END
RELOC_PTRS_WITH(pdf_image_writer_reloc_ptrs,pdf_image_writer * piw)53 private RELOC_PTRS_WITH(pdf_image_writer_reloc_ptrs, pdf_image_writer *piw)
54 {
55     int i;
56 
57     for (i = 0; i < piw->alt_writer_count; ++i)
58 	RELOC_USING(st_psdf_binary_writer, &piw->binary[i],
59 		    sizeof(psdf_binary_writer));
60     RELOC_VAR(piw->pres);
61     RELOC_VAR(piw->data);
62     RELOC_VAR(piw->named);
63     RELOC_VAR(piw->pres_mask);
64 }
65 RELOC_PTRS_END
66 
67 /* ---------------- Image stream dictionaries ---------------- */
68 
69 const pdf_image_names_t pdf_image_names_full = {
70     { PDF_COLOR_SPACE_NAMES },
71     { PDF_FILTER_NAMES },
72     PDF_IMAGE_PARAM_NAMES
73 };
74 const pdf_image_names_t pdf_image_names_short = {
75     { PDF_COLOR_SPACE_NAMES_SHORT },
76     { PDF_FILTER_NAMES_SHORT },
77     PDF_IMAGE_PARAM_NAMES_SHORT
78 };
79 
80 /* Store the values of image parameters other than filters. */
81 /* pdev is used only for updating procsets. */
82 /* pcsvalue is not used for masks. */
83 private int
pdf_put_pixel_image_values(cos_dict_t * pcd,gx_device_pdf * pdev,const gs_pixel_image_t * pim,const gs_color_space * pcs,const pdf_image_names_t * pin,const cos_value_t * pcsvalue)84 pdf_put_pixel_image_values(cos_dict_t *pcd, gx_device_pdf *pdev,
85 			   const gs_pixel_image_t *pim,
86 			   const gs_color_space *pcs,
87 			   const pdf_image_names_t *pin,
88 			   const cos_value_t *pcsvalue)
89 {
90     int num_components;
91     float indexed_decode[2];
92     const float *default_decode = NULL;
93     int code;
94 
95     if (pcs) {
96 	CHECK(cos_dict_put_c_key(pcd, pin->ColorSpace, pcsvalue));
97 	pdf_color_space_procsets(pdev, pcs);
98 	num_components = gs_color_space_num_components(pcs);
99 	if (gs_color_space_get_index(pcs) == gs_color_space_index_Indexed) {
100 	    indexed_decode[0] = 0;
101 	    indexed_decode[1] = (float)((1 << pim->BitsPerComponent) - 1);
102 	    default_decode = indexed_decode;
103 	}
104     } else
105 	num_components = 1;
106     CHECK(cos_dict_put_c_key_int(pcd, pin->Width, pim->Width));
107     CHECK(cos_dict_put_c_key_int(pcd, pin->Height, pim->Height));
108     CHECK(cos_dict_put_c_key_int(pcd, pin->BitsPerComponent,
109 				 pim->BitsPerComponent));
110     {
111 	int i;
112 
113 	for (i = 0; i < num_components * 2; ++i) {
114 	    if (pim->Decode[i] !=
115 		(default_decode ? default_decode[i] : i & 1)
116 		)
117 		break;
118 	}
119 	if (i < num_components * 2) {
120 	    cos_array_t *pca =
121 		cos_array_alloc(pdev, "pdf_put_pixel_image_values(decode)");
122 
123 	    if (pca == 0)
124 		return_error(gs_error_VMerror);
125 	    if (pcs == NULL) {
126 		/* 269-01.ps sets /Decode[0 100] with a mask image. */
127 		for (i = 0; i < num_components * 2; ++i)
128 		    CHECK(cos_array_add_real(pca, min(pim->Decode[i], 1)));
129 	    } else {
130 		for (i = 0; i < num_components * 2; ++i)
131 		    CHECK(cos_array_add_real(pca, pim->Decode[i]));
132 	    }
133 	    CHECK(cos_dict_put_c_key_object(pcd, pin->Decode,
134 					    COS_OBJECT(pca)));
135 	}
136     }
137     if (pim->Interpolate)
138 	CHECK(cos_dict_put_c_strings(pcd, pin->Interpolate, "true"));
139     return 0;
140 }
141 int
pdf_put_image_values(cos_dict_t * pcd,gx_device_pdf * pdev,const gs_pixel_image_t * pic,const pdf_image_names_t * pin,const cos_value_t * pcsvalue)142 pdf_put_image_values(cos_dict_t *pcd, gx_device_pdf *pdev,
143 		     const gs_pixel_image_t *pic,
144 		     const pdf_image_names_t *pin,
145 		     const cos_value_t *pcsvalue)
146 {
147     const gs_color_space *pcs = pic->ColorSpace;
148     int code;
149 
150     switch (pic->type->index) {
151     case 1: {
152 	const gs_image1_t *pim = (const gs_image1_t *)pic;
153 
154 	if (pim->ImageMask) {
155 	    CHECK(cos_dict_put_c_strings(pcd, pin->ImageMask, "true"));
156 	    pdev->procsets |= ImageB;
157 	    pcs = NULL;
158 	}
159     }
160 	break;
161     case 3: {
162 	/*
163 	 * Clients must treat this as a special case: they must call
164 	 * pdf_put_image_values for the MaskDict separately, and must
165 	 * add the Mask entry to the main image stream (dictionary).
166 	 */
167 	/*const gs_image3_t *pim = (const gs_image3_t *)pic;*/
168 
169 	/* Masked images are only supported starting in PDF 1.3. */
170 	if (pdev->CompatibilityLevel < 1.3)
171 	    return_error(gs_error_rangecheck);
172     }
173 	break;
174     case 4: {
175 	const gs_image4_t *pim = (const gs_image4_t *)pic;
176 	int num_components = gs_color_space_num_components(pcs);
177 	cos_array_t *pca;
178 	int i;
179 
180 	/* Masked images are only supported starting in PDF 1.3. */
181 	if (pdev->CompatibilityLevel < 1.3)
182 	    break; /* Will convert into an imagemask with a pattern color. */
183 	pca = cos_array_alloc(pdev, "pdf_put_image_values(mask)");
184 	if (pca == 0)
185 	    return_error(gs_error_VMerror);
186 	for (i = 0; i < num_components; ++i) {
187 	    int lo, hi;
188 
189 	    if (pim->MaskColor_is_range)
190 		lo = pim->MaskColor[i * 2], hi = pim->MaskColor[i * 2 + 1];
191 	    else
192 		lo = hi = pim->MaskColor[i];
193 	    CHECK(cos_array_add_int(pca, lo));
194 	    CHECK(cos_array_add_int(pca, hi));
195 	}
196 	CHECK(cos_dict_put_c_key_object(pcd, "/Mask", COS_OBJECT(pca)));
197     }
198 	break;
199     default:
200 	return_error(gs_error_rangecheck);
201     }
202     return pdf_put_pixel_image_values(pcd, pdev, pic, pcs, pin, pcsvalue);
203 }
204 
205 /* Store filters for an image. */
206 /* Currently this only saves parameters for CCITTFaxDecode. */
207 int
pdf_put_image_filters(cos_dict_t * pcd,gx_device_pdf * pdev,const psdf_binary_writer * pbw,const pdf_image_names_t * pin)208 pdf_put_image_filters(cos_dict_t *pcd, gx_device_pdf *pdev,
209 		      const psdf_binary_writer * pbw,
210 		      const pdf_image_names_t *pin)
211 {
212     return pdf_put_filters(pcd, pdev, pbw->strm, &pin->filter_names);
213 }
214 
215 /* ---------------- Image writing ---------------- */
216 
217 /*
218  * Fill in the image parameters for a device space bitmap.
219  * PDF images are always specified top-to-bottom.
220  * data_h is the actual number of data rows, which may be less than h.
221  */
222 void
pdf_make_bitmap_matrix(gs_matrix * pmat,int x,int y,int w,int h,int h_actual)223 pdf_make_bitmap_matrix(gs_matrix * pmat, int x, int y, int w, int h,
224 		       int h_actual)
225 {
226     pmat->xx = (float)w;
227     pmat->xy = 0;
228     pmat->yx = 0;
229     pmat->yy = (float)(-h_actual);
230     pmat->tx = (float)x;
231     pmat->ty = (float)(y + h);
232 }
233 
234 /*
235  * Put out the gsave and matrix for an image.  y_scale adjusts the matrix
236  * for images that end prematurely.
237  */
238 void
pdf_put_image_matrix(gx_device_pdf * pdev,const gs_matrix * pmat,floatp y_scale)239 pdf_put_image_matrix(gx_device_pdf * pdev, const gs_matrix * pmat,
240 		     floatp y_scale)
241 {
242     gs_matrix imat;
243 
244     gs_matrix_translate(pmat, 0.0, 1.0 - y_scale, &imat);
245     gs_matrix_scale(&imat, 1.0, y_scale, &imat);
246     pdf_put_matrix(pdev, "q ", &imat, "cm\n");
247 }
248 
249 /* Put out a reference to an image resource. */
250 int
pdf_do_image_by_id(gx_device_pdf * pdev,double scale,const gs_matrix * pimat,bool in_contents,gs_id id)251 pdf_do_image_by_id(gx_device_pdf * pdev, double scale,
252 	     const gs_matrix * pimat, bool in_contents, gs_id id)
253 {
254     /* fixme : in_contents is always true (there are no calls with false). */
255     if (in_contents) {
256 	int code = pdf_open_contents(pdev, PDF_IN_STREAM);
257 
258 	if (code < 0)
259 	    return code;
260     }
261     if (pimat)
262 	pdf_put_image_matrix(pdev, pimat, scale);
263     pprintld1(pdev->strm, "/R%ld Do\nQ\n", id);
264     return pdf_register_charproc_resource(pdev, id, resourceXObject);
265 }
266 int
pdf_do_image(gx_device_pdf * pdev,const pdf_resource_t * pres,const gs_matrix * pimat,bool in_contents)267 pdf_do_image(gx_device_pdf * pdev, const pdf_resource_t * pres,
268 	     const gs_matrix * pimat, bool in_contents)
269 {
270     /* fixme : call pdf_do_image_by_id when pimam == NULL. */
271     double scale = 1;
272 
273     if (pimat) {
274 	/* Adjust the matrix to account for short images. */
275 	const pdf_x_object_t *const pxo = (const pdf_x_object_t *)pres;
276 	scale = (double)pxo->data_height / pxo->height;
277     }
278     return pdf_do_image_by_id(pdev, scale, pimat, in_contents, pdf_resource_id(pres));
279 }
280 
281 /* ------ Begin / finish ------ */
282 
283 /* Initialize image writer. */
284 void
pdf_image_writer_init(pdf_image_writer * piw)285 pdf_image_writer_init(pdf_image_writer * piw)
286 {
287     memset(piw, 0, sizeof(*piw));
288     piw->alt_writer_count = 1; /* Default. */
289 }
290 
291 /*
292  * Begin writing an image, creating the resource if not in-line, and setting
293  * up the binary writer.  If pnamed != 0, it is a stream object created by a
294  * NI pdfmark.
295  */
296 int
pdf_begin_write_image(gx_device_pdf * pdev,pdf_image_writer * piw,gx_bitmap_id id,int w,int h,cos_dict_t * named,bool in_line)297 pdf_begin_write_image(gx_device_pdf * pdev, pdf_image_writer * piw,
298 		      gx_bitmap_id id, int w, int h, cos_dict_t *named,
299 		      bool in_line)
300 {
301     /* Patch pdev->strm so the right stream gets into the writer. */
302     stream *save_strm = pdev->strm;
303     cos_stream_t *data;
304     bool mask = (piw->data != NULL);
305     int alt_stream_index = (!mask ? 0 : piw->alt_writer_count);
306     int code;
307 
308     if (in_line) {
309 	piw->pres = 0;
310 	piw->pin = &pdf_image_names_short;
311 	data = cos_stream_alloc(pdev, "pdf_begin_image_data");
312 	if (data == 0)
313 	    return_error(gs_error_VMerror);
314 	piw->end_string = " Q";
315 	piw->named = 0;		/* must have named == 0 */
316     } else {
317 	pdf_x_object_t *pxo;
318 	cos_stream_t *pcos;
319 	pdf_resource_t *pres;
320 
321 	/*
322 	 * Note that if named != 0, there are two objects with the same id
323 	 * while the image is being accumulated: named, and pres->object.
324 	 */
325 	code = pdf_alloc_resource(pdev, resourceXObject, id, &pres,
326 				  (named ? named->id : -1L));
327 	if (code < 0)
328 	    return code;
329 	*(mask ? &piw->pres_mask : &piw->pres) = pres;
330 	cos_become(pres->object, cos_type_stream);
331 	pres->rid = id;
332 	piw->pin = &pdf_image_names_full;
333 	pxo = (pdf_x_object_t *)pres;
334 	pcos = (cos_stream_t *)pxo->object;
335 	CHECK(cos_dict_put_c_strings(cos_stream_dict(pcos), "/Subtype",
336 				     "/Image"));
337 	pxo->width = w;
338 	pxo->height = h;
339 	/* Initialize data_height for the benefit of copy_{mono,color}. */
340 	pxo->data_height = h;
341 	data = pcos;
342 	if (!mask)
343 	    piw->named = named;
344     }
345     pdev->strm = pdev->streams.strm;
346     pdev->strm = cos_write_stream_alloc(data, pdev, "pdf_begin_write_image");
347     if (pdev->strm == 0)
348 	return_error(gs_error_VMerror);
349     if (!mask)
350 	piw->data = data;
351     piw->height = h;
352     code = psdf_begin_binary((gx_device_psdf *) pdev, &piw->binary[alt_stream_index]);
353     piw->binary[alt_stream_index].target = NULL; /* We don't need target with cos_write_stream. */
354     pdev->strm = save_strm;
355     return code;
356 }
357 
358 /*
359  *  Make alternative stream for image compression choice.
360  */
361 int
pdf_make_alt_stream(gx_device_pdf * pdev,psdf_binary_writer * pbw)362 pdf_make_alt_stream(gx_device_pdf * pdev, psdf_binary_writer * pbw)
363 {
364     stream *save_strm = pdev->strm;
365     cos_stream_t *pcos = cos_stream_alloc(pdev, "pdf_make_alt_stream");
366     int code;
367 
368     if (pcos == 0)
369         return_error(gs_error_VMerror);
370     pcos->id = 0;
371     CHECK(cos_dict_put_c_strings(cos_stream_dict(pcos), "/Subtype", "/Image"));
372     pbw->strm = cos_write_stream_alloc(pcos, pdev, "pdf_make_alt_stream");
373     if (pbw->strm == 0)
374         return_error(gs_error_VMerror);
375     pbw->dev = (gx_device_psdf *)pdev;
376     pbw->memory = pdev->pdf_memory;
377     pdev->strm = pbw->strm;
378     code = psdf_begin_binary((gx_device_psdf *) pdev, pbw);
379     pdev->strm = save_strm;
380     pbw->target = NULL; /* We don't need target with cos_write_stream. */
381     return code;
382 }
383 
384 /* Begin writing the image data, setting up the dictionary and filters. */
385 int
pdf_begin_image_data(gx_device_pdf * pdev,pdf_image_writer * piw,const gs_pixel_image_t * pim,const cos_value_t * pcsvalue,int alt_writer_index)386 pdf_begin_image_data(gx_device_pdf * pdev, pdf_image_writer * piw,
387 		     const gs_pixel_image_t * pim, const cos_value_t *pcsvalue,
388 		     int alt_writer_index)
389 {
390 
391     cos_stream_t *s = cos_stream_from_pipeline(piw->binary[alt_writer_index].strm);
392     cos_dict_t *pcd = cos_stream_dict(s);
393     int code = pdf_put_image_values(pcd, pdev, pim, piw->pin, pcsvalue);
394 
395     if (code >= 0)
396 	code = pdf_put_image_filters(pcd, pdev, &piw->binary[alt_writer_index], piw->pin);
397     if (code < 0) {
398 	if (!piw->pres)
399 	    COS_FREE(piw->data, "pdf_begin_image_data");
400 	piw->data = 0;
401     }
402     return code;
403 }
404 
405 /* Complete image data. */
406 int
pdf_complete_image_data(gx_device_pdf * pdev,pdf_image_writer * piw,int data_h,int width,int bits_per_pixel)407 pdf_complete_image_data(gx_device_pdf *pdev, pdf_image_writer *piw, int data_h,
408 			int width, int bits_per_pixel)
409 {
410     if (data_h != piw->height) {
411 	if (piw->binary[0].strm->procs.process == s_DCTE_template.process ||
412 	    piw->binary[0].strm->procs.process == s_PNGPE_template.process ) {
413 	    /* 	Since DCTE and PNGPE can't safely close with incomplete data,
414 		we add stub data to complete the stream.
415 	    */
416 	    int bytes_per_line = (width * bits_per_pixel + 7) / 8;
417 	    int lines_left = piw->height - data_h;
418 	    byte buf[256];
419 	    const uint lb = sizeof(buf);
420 	    int i, l, status;
421 	    uint ignore;
422 
423 	    memset(buf, 128, lb);
424 	    for (; lines_left; lines_left--)
425 		for (i = 0; i < piw->alt_writer_count; i++) {
426 		    for (l = bytes_per_line; l > 0; l -= lb)
427 			if ((status = sputs(piw->binary[i].strm, buf, min(l, lb),
428 					    &ignore)) < 0)
429 			    return_error(gs_error_ioerror);
430 		}
431 	}
432     }
433     return 0;
434 }
435 
436 /* Finish writing the binary image data. */
437 int
pdf_end_image_binary(gx_device_pdf * pdev,pdf_image_writer * piw,int data_h)438 pdf_end_image_binary(gx_device_pdf *pdev, pdf_image_writer *piw, int data_h)
439 {
440     int code, code1 = 0;
441 
442     if (piw->alt_writer_count > 2)
443 	code = pdf_choose_compression(piw, true);
444     else
445 	code = psdf_end_binary(&piw->binary[0]);
446     /* If the image ended prematurely, update the Height. */
447     if (data_h != piw->height)
448 	code1 = cos_dict_put_c_key_int(cos_stream_dict(piw->data),
449 				      piw->pin->Height, data_h);
450     return code < 0 ? code : code1;
451 }
452 
453 /*
454  * Finish writing an image.  If in-line, write the BI/dict/ID/data/EI and
455  * return 1; if a resource, write the resource definition and return 0.
456  */
457 int
pdf_end_write_image(gx_device_pdf * pdev,pdf_image_writer * piw)458 pdf_end_write_image(gx_device_pdf * pdev, pdf_image_writer * piw)
459 {
460     pdf_resource_t *pres = piw->pres;
461 
462     if (pres) {			/* image resource */
463 	cos_object_t *const pco = pres->object;
464 	cos_stream_t *const pcs = (cos_stream_t *)pco;
465 	cos_dict_t *named = piw->named;
466 	int code;
467 
468 	if (named) {
469 	    if (pdev->ForOPDFRead) {
470 		code = cos_dict_put_c_key_bool(named, "/.Global", true);
471 		if (code < 0)
472 		    return code;
473 	    }
474 	    /*
475 	     * This image was named by NI.  Copy any dictionary elements
476 	     * from the named dictionary to the image stream, and then
477 	     * associate the name with the stream.
478 	     */
479 	    code = cos_dict_move_all(cos_stream_dict(pcs), named);
480 	    if (code < 0)
481 		return code;
482 	    pres->named = true;
483 	    /*
484 	     * We need to make the entry in the name dictionary point to
485 	     * the stream (pcs) rather than the object created by NI (named).
486 	     * Unfortunately, we no longer know what dictionary to use.
487 	     * Instead, overwrite the latter with the former's contents,
488 	     * and change the only relevant pointer.
489 	     */
490 	    *(cos_object_t *)named = *pco;
491 	    pres->object = COS_OBJECT(named);
492 	} else if (!pres->named) { /* named objects are written at the end */
493 	    code = pdf_substitute_resource(pdev, &piw->pres, resourceXObject, NULL, false);
494 	    if (code < 0)
495 		return code;
496 	    /*  Warning : If the substituted image used alternate streams,
497 		its space in the pdev->streams.strm file won't be released. */
498 	    piw->pres->where_used |= pdev->used_mask;
499 	}
500 	code = pdf_add_resource(pdev, pdev->substream_Resources, "/XObject", piw->pres);
501 	if (code < 0)
502 	    return code;
503 	return 0;
504     } else {			/* in-line image */
505 	stream *s = pdev->strm;
506 	uint KeyLength = pdev->KeyLength;
507 
508 	stream_puts(s, "BI\n");
509 	cos_stream_elements_write(piw->data, pdev);
510 	stream_puts(s, (pdev->binary_ok ? "ID " : "ID\n"));
511 	pdev->KeyLength = 0; /* Disable encryption for the inline image. */
512 	cos_stream_contents_write(piw->data, pdev);
513 	pdev->KeyLength = KeyLength;
514 	pprints1(s, "\nEI%s\n", piw->end_string);
515 	COS_FREE(piw->data, "pdf_end_write_image");
516 	return 1;
517     }
518 }
519 
520 /* ------ Copy data ------ */
521 
522 /* Copy the data for a mask or monobit bitmap. */
523 int
pdf_copy_mask_bits(stream * s,const byte * base,int sourcex,int raster,int w,int h,byte invert)524 pdf_copy_mask_bits(stream *s, const byte *base, int sourcex, int raster,
525 		   int w, int h, byte invert)
526 {
527     int yi;
528 
529     for (yi = 0; yi < h; ++yi) {
530 	const byte *data = base + yi * raster + (sourcex >> 3);
531 	int sbit = sourcex & 7;
532 
533 	if (sbit == 0) {
534 	    int nbytes = (w + 7) >> 3;
535 	    int i;
536 
537 	    for (i = 0; i < nbytes; ++data, ++i)
538 		sputc(s, (byte)(*data ^ invert));
539 	} else {
540 	    int wleft = w;
541 	    int rbit = 8 - sbit;
542 
543 	    for (; wleft + sbit > 8; ++data, wleft -= 8)
544 		sputc(s, (byte)(((*data << sbit) + (data[1] >> rbit)) ^ invert));
545 	    if (wleft > 0)
546 		sputc(s, (byte)(((*data << sbit) ^ invert) &
547 		      (byte) (0xff00 >> wleft)));
548 	}
549     }
550     return 0;
551 }
552 
553 /* Copy the data for a colored image (device pixels). */
554 int
pdf_copy_color_bits(stream * s,const byte * base,int sourcex,int raster,int w,int h,int bytes_per_pixel)555 pdf_copy_color_bits(stream *s, const byte *base, int sourcex, int raster,
556 		    int w, int h, int bytes_per_pixel)
557 {
558     int yi;
559 
560     for (yi = 0; yi < h; ++yi) {
561 	uint ignore;
562 
563 	sputs(s, base + sourcex * bytes_per_pixel + yi * raster,
564 	      w * bytes_per_pixel, &ignore);
565     }
566     return 0;
567 }
568 
569 /* Choose image compression - auxiliary procs */
much_bigger__DL(long l1,long l2)570 private inline bool much_bigger__DL(long l1, long l2)
571 {
572     return l1 > 1024*1024 && l2 < l1 / 3;
573 }
574 private void
pdf_choose_compression_cos(pdf_image_writer * piw,cos_stream_t * s[2],bool force)575 pdf_choose_compression_cos(pdf_image_writer *piw, cos_stream_t *s[2], bool force)
576 {   /*	Assume s[0] is Flate, s[1] is DCT, s[2] is chooser. */
577     long l0, l1;
578     int k0, k1;
579 
580     l0 = cos_stream_length(s[0]);
581     l1 = cos_stream_length(s[1]);
582 
583     if (force && l0 <= l1)
584 	k0 = 1; /* Use Flate if it is not longer. */
585     else {
586 	k0 = s_compr_chooser__get_choice(
587 	    (stream_compr_chooser_state *)piw->binary[2].strm->state, force);
588 	if (k0 && l0 > 0 && l1 > 0)
589 	    k0--;
590 	else if (much_bigger__DL(l0, l1))
591 	    k0 = 0;
592 	else if (much_bigger__DL(l1, l0) || force)
593 	    k0 = 1;
594 	else
595 	   return;
596     }
597     k1 = 1 - k0;
598     s_close_filters(&piw->binary[k0].strm, piw->binary[k0].target);
599     s[k0]->cos_procs->release((cos_object_t *)s[k0], "pdf_image_choose_filter");
600     s[k0]->written = 1;
601     piw->binary[0].strm = piw->binary[k1].strm;
602     s_close_filters(&piw->binary[2].strm, piw->binary[2].target);
603     piw->binary[1].strm = piw->binary[2].strm = 0; /* for GC */
604     piw->binary[1].target = piw->binary[2].target = 0;
605     s[k1]->id = piw->pres->object->id;
606     piw->pres->object = (cos_object_t *)s[k1];
607     piw->data = s[k1];
608     if (piw->alt_writer_count > 3) {
609 	piw->binary[1] = piw->binary[3];
610 	piw->binary[3].strm = 0; /* for GC */
611 	piw->binary[3].target = 0;
612     }
613     piw->alt_writer_count -= 2;
614 }
615 
616 /* End binary with choosing image compression. */
617 int
pdf_choose_compression(pdf_image_writer * piw,bool end_binary)618 pdf_choose_compression(pdf_image_writer * piw, bool end_binary)
619 {
620     cos_stream_t *s[2];
621     s[0] = cos_stream_from_pipeline(piw->binary[0].strm);
622     s[1] = cos_stream_from_pipeline(piw->binary[1].strm);
623     if (end_binary) {
624 	int status;
625 
626     	status = s_close_filters(&piw->binary[0].strm, piw->binary[0].target);
627 	if (status < 0)
628 	    return status;
629 	status = s_close_filters(&piw->binary[1].strm, piw->binary[1].target);
630 	if (status < 0)
631 	    return status;
632     }
633     pdf_choose_compression_cos(piw, s, end_binary);
634     return 0;
635 }
636