2 |
- |
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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
|
|
|
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 */
|
|
|
570 |
private inline bool much_bigger__DL(long l1, long l2)
|
|
|
571 |
{
|
|
|
572 |
return l1 > 1024*1024 && l2 < l1 / 3;
|
|
|
573 |
}
|
|
|
574 |
private void
|
|
|
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
|
|
|
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 |
}
|