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