1 /* Copyright (C) 2000 artofcode LLC.  All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify it
4   under the terms of the GNU General Public License as published by the
5   Free Software Foundation; either version 2 of the License, or (at your
6   option) any later version.
7 
8   This program is distributed in the hope that it will be useful, but
9   WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License along
14   with this program; if not, write to the Free Software Foundation, Inc.,
15   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16 
17 */
18 
19 /*$Id: gdevpdfj.c,v 1.7.2.1.2.1 2003/01/17 00:49:01 giles Exp $ */
20 /* Image-writing utilities for pdfwrite driver */
21 #include "memory_.h"
22 #include "string_.h"
23 #include "gx.h"
24 #include "gserrors.h"
25 #include "gdevpdfx.h"
26 #include "gdevpdfg.h"
27 #include "gdevpdfo.h"
28 #include "gxcspace.h"
29 #include "gsiparm4.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 
37 /* ---------------- Image stream dictionaries ---------------- */
38 
39 const pdf_image_names_t pdf_image_names_full = {
40     { PDF_COLOR_SPACE_NAMES },
41     { PDF_FILTER_NAMES },
42     PDF_IMAGE_PARAM_NAMES
43 };
44 const pdf_image_names_t pdf_image_names_short = {
45     { PDF_COLOR_SPACE_NAMES_SHORT },
46     { PDF_FILTER_NAMES_SHORT },
47     PDF_IMAGE_PARAM_NAMES_SHORT
48 };
49 
50 /* Store the values of image parameters other than filters. */
51 /* pdev is used only for updating procsets. */
52 /* pcsvalue is not used for masks. */
53 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)54 pdf_put_pixel_image_values(cos_dict_t *pcd, gx_device_pdf *pdev,
55 			   const gs_pixel_image_t *pim,
56 			   const gs_color_space *pcs,
57 			   const pdf_image_names_t *pin,
58 			   const cos_value_t *pcsvalue)
59 {
60     int num_components;
61     float indexed_decode[2];
62     const float *default_decode = NULL;
63     int code;
64 
65     if (pcs) {
66 	CHECK(cos_dict_put_c_key(pcd, pin->ColorSpace, pcsvalue));
67 	pdf_color_space_procsets(pdev, pcs);
68 	num_components = gs_color_space_num_components(pcs);
69 	if (gs_color_space_get_index(pcs) == gs_color_space_index_Indexed) {
70 	    indexed_decode[0] = 0;
71 	    indexed_decode[1] = (1 << pim->BitsPerComponent) - 1;
72 	    default_decode = indexed_decode;
73 	}
74     } else
75 	num_components = 1;
76     CHECK(cos_dict_put_c_key_int(pcd, pin->Width, pim->Width));
77     CHECK(cos_dict_put_c_key_int(pcd, pin->Height, pim->Height));
78     CHECK(cos_dict_put_c_key_int(pcd, pin->BitsPerComponent,
79 				 pim->BitsPerComponent));
80     {
81 	int i;
82 
83 	for (i = 0; i < num_components * 2; ++i)
84 	    if (pim->Decode[i] !=
85 		(default_decode ? default_decode[i] : i & 1)
86 		)
87 		break;
88 	if (i < num_components * 2) {
89 	    cos_array_t *pca =
90 		cos_array_alloc(pdev, "pdf_put_pixel_image_values(decode)");
91 
92 	    if (pca == 0)
93 		return_error(gs_error_VMerror);
94 	    for (i = 0; i < num_components * 2; ++i)
95 		CHECK(cos_array_add_real(pca, pim->Decode[i]));
96 	    CHECK(cos_dict_put_c_key_object(pcd, pin->Decode,
97 					    COS_OBJECT(pca)));
98 	}
99     }
100     if (pim->Interpolate)
101 	CHECK(cos_dict_put_c_strings(pcd, pin->Interpolate, "true"));
102     return 0;
103 }
104 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)105 pdf_put_image_values(cos_dict_t *pcd, gx_device_pdf *pdev,
106 		     const gs_pixel_image_t *pic,
107 		     const pdf_image_names_t *pin,
108 		     const cos_value_t *pcsvalue)
109 {
110     const gs_color_space *pcs = pic->ColorSpace;
111     int code;
112 
113     switch (pic->type->index) {
114     case 1: {
115 	const gs_image1_t *pim = (const gs_image1_t *)pic;
116 
117 	if (pim->ImageMask) {
118 	    CHECK(cos_dict_put_c_strings(pcd, pin->ImageMask, "true"));
119 	    pdev->procsets |= ImageB;
120 	    pcs = NULL;
121 	}
122     }
123 	break;
124     case 3: {
125 	/*
126 	 * Clients must treat this as a special case: they must call
127 	 * pdf_put_image_values for the MaskDict separately, and must
128 	 * add the Mask entry to the main image stream (dictionary).
129 	 */
130 	/*const gs_image3_t *pim = (const gs_image3_t *)pic;*/
131 
132 	/* Masked images are only supported starting in PDF 1.3. */
133 	if (pdev->CompatibilityLevel < 1.3)
134 	    return_error(gs_error_rangecheck);
135     }
136 	break;
137     case 4: {
138 	const gs_image4_t *pim = (const gs_image4_t *)pic;
139 	int num_components = gs_color_space_num_components(pcs);
140 	cos_array_t *pca;
141 	int i;
142 
143 	/* Masked images are only supported starting in PDF 1.3. */
144 	if (pdev->CompatibilityLevel < 1.3)
145 	    return_error(gs_error_rangecheck);
146 	pca = cos_array_alloc(pdev, "pdf_put_image_values(mask)");
147 	if (pca == 0)
148 	    return_error(gs_error_VMerror);
149 	for (i = 0; i < num_components; ++i) {
150 	    int lo, hi;
151 
152 	    if (pim->MaskColor_is_range)
153 		lo = pim->MaskColor[i * 2], hi = pim->MaskColor[i * 2 + 1];
154 	    else
155 		lo = hi = pim->MaskColor[i];
156 	    CHECK(cos_array_add_int(pca, lo));
157 	    CHECK(cos_array_add_int(pca, hi));
158 	}
159 	CHECK(cos_dict_put_c_key_object(pcd, "/Mask", COS_OBJECT(pca)));
160     }
161 	break;
162     default:
163 	return_error(gs_error_rangecheck);
164     }
165     return pdf_put_pixel_image_values(pcd, pdev, pic, pcs, pin, pcsvalue);
166 }
167 
168 /* Store filters for an image. */
169 /* Currently this only saves parameters for CCITTFaxDecode. */
170 int
pdf_put_image_filters(cos_dict_t * pcd,gx_device_pdf * pdev,const psdf_binary_writer * pbw,const pdf_image_names_t * pin)171 pdf_put_image_filters(cos_dict_t *pcd, gx_device_pdf *pdev,
172 		      const psdf_binary_writer * pbw,
173 		      const pdf_image_names_t *pin)
174 {
175     return pdf_put_filters(pcd, pdev, pbw->strm, &pin->filter_names);
176 }
177 
178 /* ---------------- Image writing ---------------- */
179 
180 /*
181  * Fill in the image parameters for a device space bitmap.
182  * PDF images are always specified top-to-bottom.
183  * data_h is the actual number of data rows, which may be less than h.
184  */
185 void
pdf_make_bitmap_matrix(gs_matrix * pmat,int x,int y,int w,int h,int h_actual)186 pdf_make_bitmap_matrix(gs_matrix * pmat, int x, int y, int w, int h,
187 		       int h_actual)
188 {
189     pmat->xx = w;
190     pmat->xy = 0;
191     pmat->yx = 0;
192     pmat->yy = -h_actual;
193     pmat->tx = x;
194     pmat->ty = y + h;
195 }
196 
197 /*
198  * Put out the gsave and matrix for an image.  y_scale adjusts the matrix
199  * for images that end prematurely.
200  */
201 void
pdf_put_image_matrix(gx_device_pdf * pdev,const gs_matrix * pmat,floatp y_scale)202 pdf_put_image_matrix(gx_device_pdf * pdev, const gs_matrix * pmat,
203 		     floatp y_scale)
204 {
205     gs_matrix imat;
206 
207     gs_matrix_translate(pmat, 0.0, 1.0 - y_scale, &imat);
208     gs_matrix_scale(&imat, 1.0, y_scale, &imat);
209     pdf_put_matrix(pdev, "q ", &imat, "cm\n");
210 }
211 
212 /* Put out a reference to an image resource. */
213 int
pdf_do_image(gx_device_pdf * pdev,const pdf_resource_t * pres,const gs_matrix * pimat,bool in_contents)214 pdf_do_image(gx_device_pdf * pdev, const pdf_resource_t * pres,
215 	     const gs_matrix * pimat, bool in_contents)
216 {
217     if (in_contents) {
218 	int code = pdf_open_contents(pdev, PDF_IN_STREAM);
219 
220 	if (code < 0)
221 	    return code;
222     }
223     if (pimat) {
224 	/* Adjust the matrix to account for short images. */
225 	const pdf_x_object_t *const pxo = (const pdf_x_object_t *)pres;
226 	double scale = (double)pxo->data_height / pxo->height;
227 
228 	pdf_put_image_matrix(pdev, pimat, scale);
229     }
230     pprintld1(pdev->strm, "/R%ld Do\nQ\n", pdf_resource_id(pres));
231     return 0;
232 }
233 
234 /* ------ Begin / finish ------ */
235 
236 /*
237  * Begin writing an image, creating the resource if not in-line and
238  * pres == 0, and setting up the binary writer.
239  */
240 int
pdf_begin_write_image(gx_device_pdf * pdev,pdf_image_writer * piw,gx_bitmap_id id,int w,int h,pdf_resource_t * pres,bool in_line)241 pdf_begin_write_image(gx_device_pdf * pdev, pdf_image_writer * piw,
242 		      gx_bitmap_id id, int w, int h, pdf_resource_t *pres,
243 		      bool in_line)
244 {
245     /* Patch pdev->strm so the right stream gets into the writer. */
246     stream *save_strm = pdev->strm;
247     int code;
248 
249     if (in_line) {
250 	piw->pres = 0;
251 	piw->pin = &pdf_image_names_short;
252 	piw->data = cos_stream_alloc(pdev, "pdf_begin_image_data");
253 	if (piw->data == 0)
254 	    return_error(gs_error_VMerror);
255 	piw->end_string = " Q";
256     } else {
257 	pdf_x_object_t *pxo;
258 	cos_stream_t *pcos;
259 
260 	if (pres == 0) {
261 	    code = pdf_alloc_resource(pdev, resourceXObject, id, &piw->pres,
262 				      0L);
263 	    if (code < 0)
264 		return code;
265 	    cos_become(piw->pres->object, cos_type_stream);
266 	} else {
267 	    /* Resource already allocated (Mask for ImageType 3 image). */
268 	    piw->pres = pres;
269 	}
270 	piw->pres->rid = id;
271 	piw->pin = &pdf_image_names_full;
272 	pxo = (pdf_x_object_t *)piw->pres;
273 	pcos = (cos_stream_t *)pxo->object;
274 	CHECK(cos_dict_put_c_strings(cos_stream_dict(pcos), "/Subtype",
275 				     "/Image"));
276 	pxo->width = w;
277 	pxo->height = h;
278 	/* Initialize data_height for the benefit of copy_{mono,color}. */
279 	pxo->data_height = h;
280 	piw->data = pcos;
281     }
282     piw->height = h;
283     pdev->strm = pdev->streams.strm;
284     code = psdf_begin_binary((gx_device_psdf *) pdev, &piw->binary);
285     pdev->strm = save_strm;
286     return code;
287 }
288 
289 /* Begin writing the image data, setting up the dictionary and filters. */
290 int
pdf_begin_image_data(gx_device_pdf * pdev,pdf_image_writer * piw,const gs_pixel_image_t * pim,const cos_value_t * pcsvalue)291 pdf_begin_image_data(gx_device_pdf * pdev, pdf_image_writer * piw,
292 		     const gs_pixel_image_t * pim, const cos_value_t *pcsvalue)
293 {
294     cos_dict_t *pcd = cos_stream_dict(piw->data);
295     int code = pdf_put_image_values(pcd, pdev, pim, piw->pin, pcsvalue);
296 
297     if (code >= 0)
298 	code = pdf_put_image_filters(pcd, pdev, &piw->binary, piw->pin);
299     if (code < 0) {
300 	if (!piw->pres)
301 	    COS_FREE(piw->data, "pdf_begin_image_data");
302 	piw->data = 0;
303     }
304     return code;
305 }
306 
307 /* Finish writing the binary image data. */
308 int
pdf_end_image_binary(gx_device_pdf * pdev,pdf_image_writer * piw,int data_h)309 pdf_end_image_binary(gx_device_pdf *pdev, pdf_image_writer *piw, int data_h)
310 {
311     long pos = stell(pdev->streams.strm);  /* piw->binary.target */
312     int code;
313 
314     psdf_end_binary(&piw->binary);
315     code = cos_stream_add_since(piw->data, pos);
316     if (code < 0)
317 	return code;
318     /* If the image ended prematurely, update the Height. */
319     if (data_h != piw->height)
320 	code = cos_dict_put_c_key_int(cos_stream_dict(piw->data),
321 				      piw->pin->Height, data_h);
322     return code;
323 }
324 
325 /*
326  * Finish writing an image.  If in-line, write the BI/dict/ID/data/EI and
327  * return 1; if a resource, write the resource definition and return 0.
328  */
329 int
pdf_end_write_image(gx_device_pdf * pdev,pdf_image_writer * piw)330 pdf_end_write_image(gx_device_pdf * pdev, pdf_image_writer * piw)
331 {
332     pdf_resource_t *pres = piw->pres;
333 
334     if (pres) {			/* image resource */
335 	if (!pres->named) {	/* named objects are written at the end */
336 	    cos_write_object(pres->object, pdev);
337 	    cos_release(pres->object, "pdf_end_write_image");
338 	}
339 	return 0;
340     } else {			/* in-line image */
341 	stream *s = pdev->strm;
342 
343 	stream_puts(s, "BI\n");
344 	cos_stream_elements_write(piw->data, pdev);
345 	stream_puts(s, (pdev->binary_ok ? "ID " : "ID\n"));
346 	cos_stream_contents_write(piw->data, pdev);
347 	pprints1(s, "\nEI%s\n", piw->end_string);
348 	COS_FREE(piw->data, "pdf_end_write_image");
349 	return 1;
350     }
351 }
352 
353 /* ------ Copy data ------ */
354 
355 /* Copy the data for a mask or monobit bitmap. */
356 int
pdf_copy_mask_bits(stream * s,const byte * base,int sourcex,int raster,int w,int h,byte invert)357 pdf_copy_mask_bits(stream *s, const byte *base, int sourcex, int raster,
358 		   int w, int h, byte invert)
359 {
360     int yi;
361 
362     for (yi = 0; yi < h; ++yi) {
363 	const byte *data = base + yi * raster + (sourcex >> 3);
364 	int sbit = sourcex & 7;
365 
366 	if (sbit == 0) {
367 	    int nbytes = (w + 7) >> 3;
368 	    int i;
369 
370 	    for (i = 0; i < nbytes; ++data, ++i)
371 		sputc(s, *data ^ invert);
372 	} else {
373 	    int wleft = w;
374 	    int rbit = 8 - sbit;
375 
376 	    for (; wleft + sbit > 8; ++data, wleft -= 8)
377 		sputc(s, ((*data << sbit) + (data[1] >> rbit)) ^ invert);
378 	    if (wleft > 0)
379 		sputc(s, ((*data << sbit) ^ invert) &
380 		      (byte) (0xff00 >> wleft));
381 	}
382     }
383     return 0;
384 }
385 
386 /* Copy the data for a colored image (device pixels). */
387 int
pdf_copy_color_bits(stream * s,const byte * base,int sourcex,int raster,int w,int h,int bytes_per_pixel)388 pdf_copy_color_bits(stream *s, const byte *base, int sourcex, int raster,
389 		    int w, int h, int bytes_per_pixel)
390 {
391     int yi;
392 
393     for (yi = 0; yi < h; ++yi) {
394 	uint ignore;
395 
396 	sputs(s, base + sourcex * bytes_per_pixel + yi * raster,
397 	      w * bytes_per_pixel, &ignore);
398     }
399     return 0;
400 }
401