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