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