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