xref: /plan9/sys/src/cmd/gs/src/gdevpdfo.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1997, 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: gdevpdfo.c,v 1.35 2005/10/18 09:05:58 leonardo Exp $ */
18 /* Cos object support */
19 #include "memory_.h"
20 #include "string_.h"
21 #include "gx.h"
22 #include "gserrors.h"
23 #include "gsparam.h"
24 #include "gsutil.h"		/* for bytes_compare */
25 #include "gdevpdfx.h"
26 #include "gdevpdfo.h"
27 #include "strimpl.h"
28 #include "sa85x.h"
29 #include "slzwx.h"
30 #include "sarc4.h"
31 #include "sstring.h"
32 #include "szlibx.h"
33 
34 #define CHECK(expr)\
35   BEGIN if ((code = (expr)) < 0) return code; END
36 
37 /* ---------------- Structure definitions ---------------- */
38 
39 /*
40  * Define the generic structure for elements of arrays and
41  * dictionaries/streams.
42  */
43 #define cos_element_common(etype)\
44     etype *next
45 struct cos_element_s {
46     cos_element_common(cos_element_t);
47 };
48 #define private_st_cos_element()	/* in gdevpdfo.c */\
49   gs_private_st_ptrs1(st_cos_element, cos_element_t, "cos_element_t",\
50     cos_element_enum_ptrs, cos_element_reloc_ptrs, next)
51 #define cos_element_num_ptrs 1
52 
53 /*
54  * Define the structure for a piece of stream contents.
55  */
56 struct cos_stream_piece_s {
57     cos_element_common(cos_stream_piece_t);
58     long position;		/* in streams file */
59     uint size;
60 };
61 #define private_st_cos_stream_piece()	/* in gdevpdfo.c */\
62   gs_private_st_suffix_add0_local(st_cos_stream_piece, cos_stream_piece_t,\
63     "cos_stream_piece_t", cos_element_enum_ptrs, cos_element_reloc_ptrs,\
64     st_cos_element)
65 
66 /*
67  * Define Cos arrays, dictionaries, and streams.
68  */
69      /* array */
70 struct cos_array_element_s {
71     cos_element_common(cos_array_element_t);
72     long index;
73     cos_value_t value;
74 };
75 #define private_st_cos_array_element()	/* in gdevpdfo.c */\
76   gs_private_st_composite(st_cos_array_element, cos_array_element_t,\
77     "cos_array_element_t", cos_array_element_enum_ptrs, cos_array_element_reloc_ptrs)
78     /* dict */
79 struct cos_dict_element_s {
80     cos_element_common(cos_dict_element_t);
81     gs_string key;
82     bool owns_key;	/* if false, key is shared, do not trace or free */
83     cos_value_t value;
84 };
85 #define private_st_cos_dict_element()	/* in gdevpdfo.c */\
86   gs_private_st_composite(st_cos_dict_element, cos_dict_element_t,\
87     "cos_dict_element_t", cos_dict_element_enum_ptrs, cos_dict_element_reloc_ptrs)
88 
89 /* GC descriptors */
90 private_st_cos_element();
91 private_st_cos_stream_piece();
92 private_st_cos_object();
93 private_st_cos_value();
94 private_st_cos_array_element();
95 private_st_cos_dict_element();
96 
97 /* GC procedures */
98 private
99 ENUM_PTRS_WITH(cos_value_enum_ptrs, cos_value_t *pcv) return 0;
100  case 0:
101     switch (pcv->value_type) {
102     case COS_VALUE_SCALAR:
103 	return ENUM_STRING(&pcv->contents.chars);
104     case COS_VALUE_CONST:
105 	break;
106     case COS_VALUE_OBJECT:
107     case COS_VALUE_RESOURCE:
108 	return ENUM_OBJ(pcv->contents.object);
109     }
110     return 0;
111 ENUM_PTRS_END
112 private
RELOC_PTRS_WITH(cos_value_reloc_ptrs,cos_value_t * pcv)113 RELOC_PTRS_WITH(cos_value_reloc_ptrs, cos_value_t *pcv)
114 {
115     switch (pcv->value_type) {
116     case COS_VALUE_SCALAR:
117 	RELOC_STRING_VAR(pcv->contents.chars);
118     case COS_VALUE_CONST:
119 	break;
120     case COS_VALUE_OBJECT:
121     case COS_VALUE_RESOURCE:
122 	RELOC_VAR(pcv->contents.object);
123 	break;
124     }
125 }
126 RELOC_PTRS_END
127 private
ENUM_PTRS_WITH(cos_array_element_enum_ptrs,cos_array_element_t * pcae)128 ENUM_PTRS_WITH(cos_array_element_enum_ptrs, cos_array_element_t *pcae)
129 {
130     return (index < cos_element_num_ptrs ?
131 	    ENUM_USING_PREFIX(st_cos_element, 0) :
132 	    ENUM_USING(st_cos_value, &pcae->value, sizeof(cos_value_t),
133 		       index - cos_element_num_ptrs));
134 }
135 ENUM_PTRS_END
136 private
RELOC_PTRS_WITH(cos_array_element_reloc_ptrs,cos_array_element_t * pcae)137 RELOC_PTRS_WITH(cos_array_element_reloc_ptrs, cos_array_element_t *pcae)
138 {
139     RELOC_PREFIX(st_cos_element);
140     RELOC_USING(st_cos_value, &pcae->value, sizeof(cos_value_t));
141 }
142 RELOC_PTRS_END
143 private
ENUM_PTRS_WITH(cos_dict_element_enum_ptrs,cos_dict_element_t * pcde)144 ENUM_PTRS_WITH(cos_dict_element_enum_ptrs, cos_dict_element_t *pcde)
145 {
146     return (index < cos_element_num_ptrs ?
147 	    ENUM_USING_PREFIX(st_cos_element, 0) :
148 	    (index -= cos_element_num_ptrs) > 0 ?
149 	    ENUM_USING(st_cos_value, &pcde->value, sizeof(cos_value_t),
150 		       index - 1) :
151 	    pcde->owns_key ? ENUM_STRING(&pcde->key) : ENUM_OBJ(NULL));
152 }
153 ENUM_PTRS_END
154 private
RELOC_PTRS_WITH(cos_dict_element_reloc_ptrs,cos_dict_element_t * pcde)155 RELOC_PTRS_WITH(cos_dict_element_reloc_ptrs, cos_dict_element_t *pcde)
156 {
157     RELOC_PREFIX(st_cos_element);
158     if (pcde->owns_key)
159 	RELOC_STRING_VAR(pcde->key);
160     RELOC_USING(st_cos_value, &pcde->value, sizeof(cos_value_t));
161 }
162 RELOC_PTRS_END
163 
164 /* ---------------- Generic support ---------------- */
165 
166 /* Initialize a just-allocated cos object. */
167 private void
cos_object_init(cos_object_t * pco,gx_device_pdf * pdev,const cos_object_procs_t * procs)168 cos_object_init(cos_object_t *pco, gx_device_pdf *pdev,
169 		const cos_object_procs_t *procs)
170 {
171     if (pco) {
172 	pco->cos_procs = procs;
173 	pco->id = 0;
174 	pco->elements = 0;
175 	pco->pieces = 0;
176 	pco->pdev = pdev;
177 	pco->pres = 0;
178 	pco->is_open = true;
179 	pco->is_graphics = false;
180 	pco->written = false;
181  	pco->length = 0;
182  	pco->input_strm = 0;
183     }
184 }
185 
186 /* Get the allocator for a Cos object. */
187 gs_memory_t *
cos_object_memory(const cos_object_t * pco)188 cos_object_memory(const cos_object_t *pco)
189 {
190     return pco->pdev->pdf_memory;
191 }
192 
193 /* Change a generic cos object into one of a specific type. */
194 int
cos_become(cos_object_t * pco,cos_type_t cotype)195 cos_become(cos_object_t *pco, cos_type_t cotype)
196 {
197     if (cos_type(pco) != cos_type_generic)
198 	return_error(gs_error_typecheck);
199     cos_type(pco) = cotype;
200     return 0;
201 }
202 
203 /* Release a cos object. */
204 cos_proc_release(cos_release);	/* check prototype */
205 void
cos_release(cos_object_t * pco,client_name_t cname)206 cos_release(cos_object_t *pco, client_name_t cname)
207 {
208     pco->cos_procs->release(pco, cname);
209 }
210 
211 /* Free a cos object. */
212 void
cos_free(cos_object_t * pco,client_name_t cname)213 cos_free(cos_object_t *pco, client_name_t cname)
214 {
215     cos_release(pco, cname);
216     gs_free_object(cos_object_memory(pco), pco, cname);
217 }
218 
219 /* Write a cos object on the output. */
220 cos_proc_write(cos_write);	/* check prototype */
221 int
cos_write(const cos_object_t * pco,gx_device_pdf * pdev,gs_id object_id)222 cos_write(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
223 {
224     return pco->cos_procs->write(pco, pdev, object_id);
225 }
226 
227 /* Write a cos object as a PDF object. */
228 int
cos_write_object(cos_object_t * pco,gx_device_pdf * pdev)229 cos_write_object(cos_object_t *pco, gx_device_pdf *pdev)
230 {
231     int code;
232 
233     if (pco->id == 0 || pco->written)
234 	return_error(gs_error_Fatal);
235     pdf_open_separate(pdev, pco->id);
236     code = cos_write(pco, pdev, pco->id);
237     pdf_end_separate(pdev);
238     pco->written = true;
239     return code;
240 }
241 
242 /* Make a value to store into a composite object. */
243 const cos_value_t *
cos_string_value(cos_value_t * pcv,const byte * data,uint size)244 cos_string_value(cos_value_t *pcv, const byte *data, uint size)
245 {
246     /*
247      * It's OK to break const here, because the value will be copied
248      * before being stored in the collection.
249      */
250     pcv->contents.chars.data = (byte *)data;
251     pcv->contents.chars.size = size;
252     pcv->value_type = COS_VALUE_SCALAR;
253     return pcv;
254 }
255 const cos_value_t *
cos_c_string_value(cos_value_t * pcv,const char * str)256 cos_c_string_value(cos_value_t *pcv, const char *str)
257 {
258     /*
259      * We shouldn't break const here, because the value will not be copied
260      * or freed (or traced), but that would require a lot of bothersome
261      * casting elsewhere.
262      */
263     pcv->contents.chars.data = (byte *)str;
264     pcv->contents.chars.size = strlen(str);
265     pcv->value_type = COS_VALUE_CONST;
266     return pcv;
267 }
268 const cos_value_t *
cos_object_value(cos_value_t * pcv,cos_object_t * pco)269 cos_object_value(cos_value_t *pcv, cos_object_t *pco)
270 {
271     pcv->contents.object = pco;
272     pcv->value_type = COS_VALUE_OBJECT;
273     return pcv;
274 }
275 const cos_value_t *
cos_resource_value(cos_value_t * pcv,cos_object_t * pco)276 cos_resource_value(cos_value_t *pcv, cos_object_t *pco)
277 {
278     pcv->contents.object = pco;
279     pcv->value_type = COS_VALUE_RESOURCE;
280     return pcv;
281 }
282 
283 /* Free a value. */
284 void
cos_value_free(const cos_value_t * pcv,const cos_object_t * pco,client_name_t cname)285 cos_value_free(const cos_value_t *pcv, const cos_object_t *pco,
286 	       client_name_t cname)
287 {
288     switch (pcv->value_type) {
289     case COS_VALUE_SCALAR:
290 	gs_free_string(cos_object_memory(pco), pcv->contents.chars.data,
291 		       pcv->contents.chars.size, cname);
292     case COS_VALUE_CONST:
293 	break;
294     case COS_VALUE_OBJECT:
295 	/* Free the object if this is the only reference to it. */
296 	if (!pcv->contents.object->id)
297 	    cos_free(pcv->contents.object, cname);
298     case COS_VALUE_RESOURCE:
299 	break;
300     }
301 }
302 
303 /* Write a value on the output. */
304 private int
cos_value_write_spaced(const cos_value_t * pcv,gx_device_pdf * pdev,bool do_space,gs_id object_id)305 cos_value_write_spaced(const cos_value_t *pcv, gx_device_pdf *pdev,
306 		       bool do_space, gs_id object_id)
307 {
308     stream *s = pdev->strm;
309 
310     switch (pcv->value_type) {
311     case COS_VALUE_SCALAR:
312     case COS_VALUE_CONST:
313 	if (do_space)
314 	    switch (pcv->contents.chars.data[0]) {
315 	    case '/': case '(': case '<': break;
316 	    default: stream_putc(s, ' ');
317 	    }
318 	return pdf_write_value(pdev, pcv->contents.chars.data,
319 			pcv->contents.chars.size, object_id);
320     case COS_VALUE_RESOURCE:
321 	pprintld1(s, "/R%ld", pcv->contents.object->id);
322 	break;
323     case COS_VALUE_OBJECT: {
324 	const cos_object_t *pco = pcv->contents.object;
325 
326 	if (!pco->id) {
327 	    if (do_space &&
328 		!(pco->cos_procs == cos_type_array ||
329 		  pco->cos_procs == cos_type_dict)
330 		) {
331 		/* Arrays and dictionaries (only) are self-delimiting. */
332 		stream_putc(s, ' ');
333 	    }
334 	    return cos_write(pco, pdev, object_id);
335 	}
336 	if (do_space)
337 	    stream_putc(s, ' ');
338 	pprintld1(s, "%ld 0 R", pco->id);
339 	break;
340     }
341     default:			/* can't happen */
342 	return_error(gs_error_Fatal);
343     }
344     return 0;
345 }
346 int
cos_value_write(const cos_value_t * pcv,gx_device_pdf * pdev)347 cos_value_write(const cos_value_t *pcv, gx_device_pdf *pdev)
348 {
349     return cos_value_write_spaced(pcv, pdev, false, 0);
350 }
351 
352 /* Copy a value if necessary for putting into an array or dictionary. */
353 private int
cos_copy_element_value(cos_value_t * pcv,gs_memory_t * mem,const cos_value_t * pvalue,bool copy)354 cos_copy_element_value(cos_value_t *pcv, gs_memory_t *mem,
355 		       const cos_value_t *pvalue, bool copy)
356 {
357     *pcv = *pvalue;
358     if (pvalue->value_type == COS_VALUE_SCALAR && copy) {
359 	byte *value_data = gs_alloc_string(mem, pvalue->contents.chars.size,
360 					   "cos_copy_element_value");
361 
362 	if (value_data == 0)
363 	    return_error(gs_error_VMerror);
364 	memcpy(value_data, pvalue->contents.chars.data,
365 	       pvalue->contents.chars.size);
366 	pcv->contents.chars.data = value_data;
367     }
368     return 0;
369 }
370 
371 /* Release a value copied for putting, if the operation fails. */
372 private void
cos_uncopy_element_value(cos_value_t * pcv,gs_memory_t * mem,bool copy)373 cos_uncopy_element_value(cos_value_t *pcv, gs_memory_t *mem, bool copy)
374 {
375     if (pcv->value_type == COS_VALUE_SCALAR && copy)
376 	gs_free_string(mem, pcv->contents.chars.data, pcv->contents.chars.size,
377 		       "cos_uncopy_element_value");
378 }
379 
380 /* Compare 2 cos values for equality. */
381 private int
cos_value_equal(const cos_value_t * pcv0,const cos_value_t * pcv1,gx_device_pdf * pdev)382 cos_value_equal(const cos_value_t *pcv0, const cos_value_t *pcv1, gx_device_pdf *pdev)
383 {
384     if (pcv0->value_type != pcv1->value_type)
385 	return false;
386     switch (pcv0->value_type) {
387 	case COS_VALUE_SCALAR:
388 	case COS_VALUE_CONST:
389 	    if (bytes_compare(pcv0->contents.chars.data, pcv0->contents.chars.size,
390 			      pcv1->contents.chars.data, pcv1->contents.chars.size))
391 		return false;
392 	    break;
393 	case COS_VALUE_OBJECT:
394 	    if (pcv0->contents.object != pcv1->contents.object) {
395 		int code = pcv0->contents.object->cos_procs->equal(
396 			pcv0->contents.object, pcv1->contents.object, pdev);
397 
398 		if (code < 0)
399 		    return code;
400 		if (!code)
401 		    return false;
402 	    }
403 	    break;
404 	case COS_VALUE_RESOURCE:
405 	    if (pcv0->contents.object != pcv1->contents.object)
406 		return false;
407 	    break;
408     }
409     return true;
410 }
411 
412 /* ---------------- Specific object types ---------------- */
413 
414 /* ------ Generic objects ------ */
415 
416 private cos_proc_release(cos_generic_release);
417 private cos_proc_write(cos_generic_write);
418 private cos_proc_equal(cos_generic_equal);
419 const cos_object_procs_t cos_generic_procs = {
420     cos_generic_release, cos_generic_write, cos_generic_equal
421 };
422 
423 cos_object_t *
cos_object_alloc(gx_device_pdf * pdev,client_name_t cname)424 cos_object_alloc(gx_device_pdf *pdev, client_name_t cname)
425 {
426     gs_memory_t *mem = pdev->pdf_memory;
427     cos_object_t *pco =
428 	gs_alloc_struct(mem, cos_object_t, &st_cos_object, cname);
429 
430     cos_object_init(pco, pdev, &cos_generic_procs);
431     return pco;
432 }
433 
434 private void
cos_generic_release(cos_object_t * pco,client_name_t cname)435 cos_generic_release(cos_object_t *pco, client_name_t cname)
436 {
437     /* Do nothing. */
438 }
439 
440 private int
cos_generic_write(const cos_object_t * pco,gx_device_pdf * pdev,gs_id object_id)441 cos_generic_write(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
442 {
443     return_error(gs_error_Fatal);
444 }
445 
446 private int
cos_generic_equal(const cos_object_t * pco0,const cos_object_t * pco1,gx_device_pdf * pdev)447 cos_generic_equal(const cos_object_t *pco0, const cos_object_t *pco1, gx_device_pdf *pdev)
448 {
449     return_error(gs_error_Fatal);
450 }
451 
452 /* ------ Arrays ------ */
453 
454 private cos_proc_release(cos_array_release);
455 private cos_proc_write(cos_array_write);
456 private cos_proc_equal(cos_array_equal);
457 const cos_object_procs_t cos_array_procs = {
458     cos_array_release, cos_array_write, cos_array_equal
459 };
460 
461 cos_array_t *
cos_array_alloc(gx_device_pdf * pdev,client_name_t cname)462 cos_array_alloc(gx_device_pdf *pdev, client_name_t cname)
463 {
464     gs_memory_t *mem = pdev->pdf_memory;
465     cos_array_t *pca =
466 	gs_alloc_struct(mem, cos_array_t, &st_cos_object, cname);
467 
468     cos_object_init((cos_object_t *)pca, pdev, &cos_array_procs);
469     return pca;
470 }
471 
472 cos_array_t *
cos_array_from_floats(gx_device_pdf * pdev,const float * pf,uint size,client_name_t cname)473 cos_array_from_floats(gx_device_pdf *pdev, const float *pf, uint size,
474 		      client_name_t cname)
475 {
476     cos_array_t *pca = cos_array_alloc(pdev, cname);
477     uint i;
478 
479     if (pca == 0)
480 	return 0;
481     for (i = 0; i < size; ++i) {
482 	int code = cos_array_add_real(pca, pf[i]);
483 
484 	if (code < 0) {
485 	    COS_FREE(pca, cname);
486 	    return 0;
487 	}
488     }
489     return pca;
490 }
491 
492 private void
cos_array_release(cos_object_t * pco,client_name_t cname)493 cos_array_release(cos_object_t *pco, client_name_t cname)
494 {
495     gs_memory_t *mem = cos_object_memory(pco);
496     cos_array_t *const pca = (cos_array_t *)pco;
497     cos_array_element_t *cur;
498     cos_array_element_t *next;
499 
500     for (cur = pca->elements; cur; cur = next) {
501 	next = cur->next;
502 	cos_value_free(&cur->value, pco, cname);
503 	gs_free_object(mem, cur, cname);
504     }
505     pca->elements = 0;
506 }
507 
508 private cos_array_element_t *cos_array_reorder(const cos_array_t *pca,
509 					       cos_array_element_t *first);
510 private int
cos_array_write(const cos_object_t * pco,gx_device_pdf * pdev,gs_id object_id)511 cos_array_write(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
512 {
513     stream *s = pdev->strm;
514     const cos_array_t *const pca = (const cos_array_t *)pco;
515     cos_array_element_t *first = cos_array_reorder(pca, NULL);
516     cos_array_element_t *pcae;
517     uint last_index = 0;
518 
519     stream_puts(s, "[");
520     for (pcae = first; pcae; ++last_index, pcae = pcae->next) {
521 	if (pcae != first)
522 	    stream_putc(s, '\n');
523 	for (; pcae->index > last_index; ++last_index)
524 	    stream_puts(s, "null\n");
525 	cos_value_write_spaced(&pcae->value, pdev, false, object_id);
526     }
527     DISCARD(cos_array_reorder(pca, first));
528     stream_puts(s, "]");
529     return 0;
530 }
531 
532 private int
cos_array_equal(const cos_object_t * pco0,const cos_object_t * pco1,gx_device_pdf * pdev)533 cos_array_equal(const cos_object_t *pco0, const cos_object_t *pco1, gx_device_pdf *pdev)
534 {
535     const cos_array_t *const pca0 = (const cos_array_t *)pco0;
536     const cos_array_t *const pca1 = (const cos_array_t *)pco1;
537     cos_array_element_t *first0 = pca0->elements;
538     cos_array_element_t *first1 = pca1->elements;
539     cos_array_element_t *pcae0, *pcae1;
540     int code;
541 
542     for (pcae0 = first0, pcae1 = first1; pcae0 && pcae1;
543 	    pcae0 = pcae0->next, pcae1 = pcae1->next) {
544 	if (pcae0->index != pcae1->index)
545 	    return false;
546 	code = cos_value_equal(&pcae0->value, &pcae1->value, pdev);
547 	if (code < 0)
548 	    return code;
549 	if (!code)
550 	    return false;
551     }
552     if (pcae0 || pcae1)
553 	return false;
554     return true;
555 }
556 
557 
558 /* Put/add an element in/to an array. */
559 int
cos_array_put(cos_array_t * pca,long index,const cos_value_t * pvalue)560 cos_array_put(cos_array_t *pca, long index, const cos_value_t *pvalue)
561 {
562     gs_memory_t *mem = COS_OBJECT_MEMORY(pca);
563     cos_value_t value;
564     int code = cos_copy_element_value(&value, mem, pvalue, true);
565 
566     if (code >= 0) {
567 	code = cos_array_put_no_copy(pca, index, &value);
568 	if (code < 0)
569 	    cos_uncopy_element_value(&value, mem, true);
570     }
571     return code;
572 }
573 int
cos_array_put_no_copy(cos_array_t * pca,long index,const cos_value_t * pvalue)574 cos_array_put_no_copy(cos_array_t *pca, long index, const cos_value_t *pvalue)
575 {
576     gs_memory_t *mem = COS_OBJECT_MEMORY(pca);
577     cos_array_element_t **ppcae = &pca->elements;
578     cos_array_element_t *pcae;
579     cos_array_element_t *next;
580 
581     while ((next = *ppcae) != 0 && next->index > index)
582 	ppcae = &next->next;
583     if (next && next->index == index) {
584 	/* We're replacing an existing element. */
585 	cos_value_free(&next->value, COS_OBJECT(pca),
586 		       "cos_array_put(old value)");
587 	pcae = next;
588     } else {
589 	/* Create a new element. */
590 	pcae = gs_alloc_struct(mem, cos_array_element_t, &st_cos_array_element,
591 			       "cos_array_put(element)");
592 	if (pcae == 0)
593 	    return_error(gs_error_VMerror);
594 	pcae->index = index;
595 	pcae->next = next;
596 	*ppcae = pcae;
597     }
598     pcae->value = *pvalue;
599     return 0;
600 }
601 private long
cos_array_next_index(const cos_array_t * pca)602 cos_array_next_index(const cos_array_t *pca)
603 {
604     return (pca->elements ? pca->elements->index + 1 : 0L);
605 }
606 int
cos_array_add(cos_array_t * pca,const cos_value_t * pvalue)607 cos_array_add(cos_array_t *pca, const cos_value_t *pvalue)
608 {
609     return cos_array_put(pca, cos_array_next_index(pca), pvalue);
610 }
611 int
cos_array_add_no_copy(cos_array_t * pca,const cos_value_t * pvalue)612 cos_array_add_no_copy(cos_array_t *pca, const cos_value_t *pvalue)
613 {
614     return cos_array_put_no_copy(pca, cos_array_next_index(pca), pvalue);
615 }
616 int
cos_array_add_c_string(cos_array_t * pca,const char * str)617 cos_array_add_c_string(cos_array_t *pca, const char *str)
618 {
619     cos_value_t value;
620 
621     return cos_array_add(pca, cos_c_string_value(&value, str));
622 }
623 int
cos_array_add_int(cos_array_t * pca,int i)624 cos_array_add_int(cos_array_t *pca, int i)
625 {
626     char str[sizeof(int) * 8 / 3 + 3]; /* sign, rounding, 0 terminator */
627     cos_value_t v;
628 
629     sprintf(str, "%d", i);
630     return cos_array_add(pca, cos_string_value(&v, (byte *)str, strlen(str)));
631 }
632 int
cos_array_add_real(cos_array_t * pca,floatp r)633 cos_array_add_real(cos_array_t *pca, floatp r)
634 {
635     byte str[50];		/****** ADHOC ******/
636     stream s;
637     cos_value_t v;
638 
639     swrite_string(&s, str, sizeof(str));
640     pprintg1(&s, "%g", r);
641     return cos_array_add(pca, cos_string_value(&v, str, stell(&s)));
642 }
643 int
cos_array_add_object(cos_array_t * pca,cos_object_t * pco)644 cos_array_add_object(cos_array_t *pca, cos_object_t *pco)
645 {
646     cos_value_t value;
647 
648     value.contents.chars.size = 0; /* Quiet a warning appeared with MSVC6 inline optimization. */
649     return cos_array_add(pca, cos_object_value(&value, pco));
650 }
651 
652 /*
653  * Remove and return the last element of an array.  Since this is intended
654  * specifically for arrays used as stacks, it gives an error if there is a
655  * gap in indices between the last element and the element before it.
656  */
657 int
cos_array_unadd(cos_array_t * pca,cos_value_t * pvalue)658 cos_array_unadd(cos_array_t *pca, cos_value_t *pvalue)
659 {
660     cos_array_element_t *pcae = pca->elements;
661 
662     if (pcae == 0 ||
663 	pcae->index != (pcae->next == 0 ? 0 : pcae->next->index + 1)
664 	)
665 	return_error(gs_error_rangecheck);
666     *pvalue = pcae->value;
667     pca->elements = pcae->next;
668     gs_free_object(COS_OBJECT_MEMORY(pca), pcae, "cos_array_unadd");
669     return 0;
670 }
671 
672 /* Get the first / next element for enumerating an array. */
673 const cos_array_element_t *
cos_array_element_first(const cos_array_t * pca)674 cos_array_element_first(const cos_array_t *pca)
675 {
676     return pca->elements;
677 }
678 const cos_array_element_t *
cos_array_element_next(const cos_array_element_t * pca,long * pindex,const cos_value_t ** ppvalue)679 cos_array_element_next(const cos_array_element_t *pca, long *pindex,
680 		       const cos_value_t **ppvalue)
681 {
682     *pindex = pca->index;
683     *ppvalue = &pca->value;
684     return pca->next;
685 }
686 
687 /*
688  * Reorder the elements of an array for writing or after writing.  Usage:
689  *	first_element = cos_array_reorder(pca, NULL);
690  *	...
691  *	cos_array_reorder(pca, first_element);
692  */
693 private cos_array_element_t *
cos_array_reorder(const cos_array_t * pca,cos_array_element_t * first)694 cos_array_reorder(const cos_array_t *pca, cos_array_element_t *first)
695 {
696     cos_array_element_t *last;
697     cos_array_element_t *next;
698     cos_array_element_t *pcae;
699 
700     for (pcae = (first ? first : pca->elements), last = NULL; pcae;
701 	 pcae = next)
702 	next = pcae->next, pcae->next = last, last = pcae;
703     return last;
704 }
705 
706 /* ------ Dictionaries ------ */
707 
708 private cos_proc_release(cos_dict_release);
709 private cos_proc_write(cos_dict_write);
710 private cos_proc_equal(cos_dict_equal);
711 const cos_object_procs_t cos_dict_procs = {
712     cos_dict_release, cos_dict_write, cos_dict_equal
713 };
714 
715 cos_dict_t *
cos_dict_alloc(gx_device_pdf * pdev,client_name_t cname)716 cos_dict_alloc(gx_device_pdf *pdev, client_name_t cname)
717 {
718     gs_memory_t *mem = pdev->pdf_memory;
719     cos_dict_t *pcd =
720 	gs_alloc_struct(mem, cos_dict_t, &st_cos_object, cname);
721 
722     cos_object_init((cos_object_t *)pcd, pdev, &cos_dict_procs);
723     return pcd;
724 }
725 
726 private void
cos_dict_element_free(cos_dict_t * pcd,cos_dict_element_t * pcde,client_name_t cname)727 cos_dict_element_free(cos_dict_t *pcd, cos_dict_element_t *pcde,
728 		      client_name_t cname)
729 {
730     gs_memory_t *mem = COS_OBJECT_MEMORY(pcd);
731 
732     cos_value_free(&pcde->value, COS_OBJECT(pcd), cname);
733     if (pcde->owns_key)
734 	gs_free_string(mem, pcde->key.data, pcde->key.size, cname);
735     gs_free_object(mem, pcde, cname);
736 }
737 
738 private void
cos_dict_release(cos_object_t * pco,client_name_t cname)739 cos_dict_release(cos_object_t *pco, client_name_t cname)
740 {
741     cos_dict_t *const pcd = (cos_dict_t *)pco;
742     cos_dict_element_t *cur;
743     cos_dict_element_t *next;
744 
745     for (cur = pcd->elements; cur; cur = next) {
746 	next = cur->next;
747 	cos_dict_element_free(pcd, cur, cname);
748     }
749     pcd->elements = 0;
750 }
751 
752 /* Write the elements of a dictionary. */
753 private int
cos_elements_write(stream * s,const cos_dict_element_t * pcde,gx_device_pdf * pdev,bool do_space,gs_id object_id)754 cos_elements_write(stream *s, const cos_dict_element_t *pcde,
755 		   gx_device_pdf *pdev, bool do_space, gs_id object_id)
756 {
757     if (pcde) {
758 	/* Temporarily replace the output stream in pdev. */
759 	stream *save = pdev->strm;
760 
761 	pdev->strm = s;
762 	for (;;) {
763 	    gs_id object_id1 = (pdev->NoEncrypt.size == 0 ||
764 				bytes_compare(pdev->NoEncrypt.data, pdev->NoEncrypt.size,
765 				    pcde->key.data, pcde->key.size)
766 				? object_id : (gs_id)-1);
767 
768 	    pdf_write_value(pdev, pcde->key.data, pcde->key.size, object_id1);
769 	    cos_value_write_spaced(&pcde->value, pdev, true, object_id1);
770 	    pcde = pcde->next;
771 	    if (pcde || do_space)
772 		stream_putc(s, '\n');
773 	    if (!pcde)
774 		break;
775 	}
776 	pdev->strm = save;
777     }
778     return 0;
779 }
780 int
cos_dict_elements_write(const cos_dict_t * pcd,gx_device_pdf * pdev)781 cos_dict_elements_write(const cos_dict_t *pcd, gx_device_pdf *pdev)
782 {
783     return cos_elements_write(pdev->strm, pcd->elements, pdev, true, pcd->id);
784 }
785 
786 private int
cos_dict_write(const cos_object_t * pco,gx_device_pdf * pdev,gs_id object_id)787 cos_dict_write(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
788 {
789     stream *s = pdev->strm;
790 
791     stream_puts(s, "<<");
792     cos_elements_write(s, ((const cos_dict_t *)pco)->elements, pdev, false, object_id);
793     stream_puts(s, ">>");
794     return 0;
795 }
796 
797 /* Write/delete definitions of named objects. */
798 /* This is a special-purpose facility for pdf_close. */
799 int
cos_dict_objects_write(const cos_dict_t * pcd,gx_device_pdf * pdev)800 cos_dict_objects_write(const cos_dict_t *pcd, gx_device_pdf *pdev)
801 {
802     const cos_dict_element_t *pcde = pcd->elements;
803 
804     for (; pcde; pcde = pcde->next)
805 	if (COS_VALUE_IS_OBJECT(&pcde->value) &&
806 	    pcde->value.contents.object->id  &&
807 	    !pcde->value.contents.object->written /* ForOPDFRead only. */)
808 	    cos_write_object(pcde->value.contents.object, pdev);
809     return 0;
810 }
811 int
cos_dict_objects_delete(cos_dict_t * pcd)812 cos_dict_objects_delete(cos_dict_t *pcd)
813 {
814     cos_dict_element_t *pcde = pcd->elements;
815 
816     /*
817      * Delete the objects' IDs so that freeing the dictionary will
818      * free them.
819      */
820     for (; pcde; pcde = pcde->next)
821 	pcde->value.contents.object->id = 0;
822     return 0;
823 }
824 
825 /* Put an element in a dictionary. */
826 #define DICT_COPY_KEY 1
827 #define DICT_COPY_VALUE 2
828 #define DICT_FREE_KEY 4
829 #define DICT_COPY_ALL (DICT_COPY_KEY | DICT_COPY_VALUE | DICT_FREE_KEY)
830 private int
cos_dict_put_copy(cos_dict_t * pcd,const byte * key_data,uint key_size,const cos_value_t * pvalue,int flags)831 cos_dict_put_copy(cos_dict_t *pcd, const byte *key_data, uint key_size,
832 		  const cos_value_t *pvalue, int flags)
833 {
834     gs_memory_t *mem = COS_OBJECT_MEMORY(pcd);
835     cos_dict_element_t **ppcde = &pcd->elements;
836     cos_dict_element_t *pcde;
837     cos_dict_element_t *next;
838     cos_value_t value;
839     int code;
840 
841     while ((next = *ppcde) != 0 &&
842 	   bytes_compare(next->key.data, next->key.size, key_data, key_size)
843 	   )
844 	ppcde = &next->next;
845     if (next) {
846 	/* We're replacing an existing element. */
847 	code = cos_copy_element_value(&value, mem, pvalue,
848 				      (flags & DICT_COPY_VALUE) != 0);
849 	if (code < 0)
850 	    return code;
851 	if (flags & DICT_FREE_KEY)
852 	    gs_free_const_string(mem, key_data, key_size,
853 				 "cos_dict_put(new key)");
854 	cos_value_free(&next->value, COS_OBJECT(pcd),
855 		       "cos_dict_put(old value)");
856 	pcde = next;
857     } else {
858 	/* Create a new element. */
859 	byte *copied_key_data;
860 
861 	if (flags & DICT_COPY_KEY) {
862 	    copied_key_data = gs_alloc_string(mem, key_size,
863 					      "cos_dict_put(key)");
864 	    if (copied_key_data == 0)
865 		return_error(gs_error_VMerror);
866 	    memcpy(copied_key_data, key_data, key_size);
867 	} else
868 	    copied_key_data = (byte *)key_data;	/* OK to break const */
869 	pcde = gs_alloc_struct(mem, cos_dict_element_t, &st_cos_dict_element,
870 			       "cos_dict_put(element)");
871 	code = cos_copy_element_value(&value, mem, pvalue,
872 				      (flags & DICT_COPY_VALUE) != 0);
873 	if (pcde == 0 || code < 0) {
874 	    if (code >= 0)
875 		cos_uncopy_element_value(&value, mem,
876 					 (flags & DICT_COPY_VALUE) != 0);
877 	    gs_free_object(mem, pcde, "cos_dict_put(element)");
878 	    if (flags & DICT_COPY_KEY)
879 		gs_free_string(mem, copied_key_data, key_size,
880 			       "cos_dict_put(key)");
881 	    return (code < 0 ? code : gs_note_error(gs_error_VMerror));
882 	}
883 	pcde->key.data = copied_key_data;
884 	pcde->key.size = key_size;
885 	pcde->owns_key = (flags & DICT_FREE_KEY) != 0;
886 	pcde->next = next;
887 	*ppcde = pcde;
888     }
889     pcde->value = value;
890     return 0;
891 }
892 int
cos_dict_put(cos_dict_t * pcd,const byte * key_data,uint key_size,const cos_value_t * pvalue)893 cos_dict_put(cos_dict_t *pcd, const byte *key_data, uint key_size,
894 	     const cos_value_t *pvalue)
895 {
896     return cos_dict_put_copy(pcd, key_data, key_size, pvalue, DICT_COPY_ALL);
897 }
898 int
cos_dict_put_no_copy(cos_dict_t * pcd,const byte * key_data,uint key_size,const cos_value_t * pvalue)899 cos_dict_put_no_copy(cos_dict_t *pcd, const byte *key_data, uint key_size,
900 		     const cos_value_t *pvalue)
901 {
902     return cos_dict_put_copy(pcd, key_data, key_size, pvalue,
903 			     DICT_COPY_KEY | DICT_FREE_KEY);
904 }
905 int
cos_dict_put_c_key(cos_dict_t * pcd,const char * key,const cos_value_t * pvalue)906 cos_dict_put_c_key(cos_dict_t *pcd, const char *key, const cos_value_t *pvalue)
907 {
908     return cos_dict_put_copy(pcd, (const byte *)key, strlen(key), pvalue,
909 			     DICT_COPY_VALUE);
910 }
911 int
cos_dict_put_c_key_string(cos_dict_t * pcd,const char * key,const byte * data,uint size)912 cos_dict_put_c_key_string(cos_dict_t *pcd, const char *key,
913 			  const byte *data, uint size)
914 {
915     cos_value_t value;
916 
917     cos_string_value(&value, data, size);
918     return cos_dict_put_c_key(pcd, key, &value);
919 }
920 int
cos_dict_put_c_key_int(cos_dict_t * pcd,const char * key,int value)921 cos_dict_put_c_key_int(cos_dict_t *pcd, const char *key, int value)
922 {
923     char str[sizeof(int) * 8 / 3 + 3]; /* sign, rounding, 0 terminator */
924 
925     sprintf(str, "%d", value);
926     return cos_dict_put_c_key_string(pcd, key, (byte *)str, strlen(str));
927 }
928 int
cos_dict_put_c_key_bool(cos_dict_t * pcd,const char * key,bool value)929 cos_dict_put_c_key_bool(cos_dict_t *pcd, const char *key, bool value)
930 {
931     return cos_dict_put_c_key_string(pcd, key,
932 		(const byte *)(value ? "true" : "false"),
933 			      (value ? 4 : 5));
934 }
935 int
cos_dict_put_c_key_real(cos_dict_t * pcd,const char * key,floatp value)936 cos_dict_put_c_key_real(cos_dict_t *pcd, const char *key, floatp value)
937 {
938     byte str[50];		/****** ADHOC ******/
939     stream s;
940 
941     swrite_string(&s, str, sizeof(str));
942     pprintg1(&s, "%g", value);
943     return cos_dict_put_c_key_string(pcd, key, str, stell(&s));
944 }
945 int
cos_dict_put_c_key_floats(cos_dict_t * pcd,const char * key,const float * pf,uint size)946 cos_dict_put_c_key_floats(cos_dict_t *pcd, const char *key, const float *pf,
947 			  uint size)
948 {
949     cos_array_t *pca = cos_array_from_floats(pcd->pdev, pf, size,
950 					     "cos_dict_put_c_key_floats");
951     int code;
952 
953     if (pca == 0)
954 	return_error(gs_error_VMerror);
955     code = cos_dict_put_c_key_object(pcd, key, COS_OBJECT(pca));
956     if (code < 0)
957 	COS_FREE(pca, "cos_dict_put_c_key_floats");
958     return code;
959 }
960 int
cos_dict_put_c_key_object(cos_dict_t * pcd,const char * key,cos_object_t * pco)961 cos_dict_put_c_key_object(cos_dict_t *pcd, const char *key, cos_object_t *pco)
962 {
963     cos_value_t value;
964 
965     return cos_dict_put_c_key(pcd, key, cos_object_value(&value, pco));
966 }
967 int
cos_dict_put_string(cos_dict_t * pcd,const byte * key_data,uint key_size,const byte * value_data,uint value_size)968 cos_dict_put_string(cos_dict_t *pcd, const byte *key_data, uint key_size,
969 		    const byte *value_data, uint value_size)
970 {
971     cos_value_t cvalue;
972 
973     return cos_dict_put(pcd, key_data, key_size,
974 			cos_string_value(&cvalue, value_data, value_size));
975 }
976 int
cos_dict_put_string_copy(cos_dict_t * pcd,const char * key,const char * value)977 cos_dict_put_string_copy(cos_dict_t *pcd, const char *key, const char *value)
978 {
979     return cos_dict_put_c_key_string(pcd, key, (byte *)value, strlen(value));
980 }
981 int
cos_dict_put_c_strings(cos_dict_t * pcd,const char * key,const char * value)982 cos_dict_put_c_strings(cos_dict_t *pcd, const char *key, const char *value)
983 {
984     cos_value_t cvalue;
985 
986     return cos_dict_put_c_key(pcd, key, cos_c_string_value(&cvalue, value));
987 }
988 
989 /* Move all the elements from one dict to another. */
990 int
cos_dict_move_all(cos_dict_t * pcdto,cos_dict_t * pcdfrom)991 cos_dict_move_all(cos_dict_t *pcdto, cos_dict_t *pcdfrom)
992 {
993     cos_dict_element_t *pcde = pcdfrom->elements;
994     cos_dict_element_t *head = pcdto->elements;
995 
996     while (pcde) {
997 	cos_dict_element_t *next = pcde->next;
998 
999 	if (cos_dict_find(pcdto, pcde->key.data, pcde->key.size)) {
1000 	    /* Free the element, which has been superseded. */
1001 	    cos_dict_element_free(pcdfrom, pcde, "cos_dict_move_all_from");
1002 	} else {
1003 	    /* Move the element. */
1004 	    pcde->next = head;
1005 	    head = pcde;
1006 	}
1007 	pcde = next;
1008     }
1009     pcdto->elements = head;
1010     pcdfrom->elements = 0;
1011     return 0;
1012 }
1013 
1014 /* Look up a key in a dictionary. */
1015 const cos_value_t *
cos_dict_find(const cos_dict_t * pcd,const byte * key_data,uint key_size)1016 cos_dict_find(const cos_dict_t *pcd, const byte *key_data, uint key_size)
1017 {
1018     cos_dict_element_t *pcde = pcd->elements;
1019 
1020     for (; pcde; pcde = pcde->next)
1021 	if (!bytes_compare(key_data, key_size, pcde->key.data, pcde->key.size))
1022 	    return &pcde->value;
1023     return 0;
1024 }
1025 const cos_value_t *
cos_dict_find_c_key(const cos_dict_t * pcd,const char * key)1026 cos_dict_find_c_key(const cos_dict_t *pcd, const char *key)
1027 {
1028     return cos_dict_find(pcd, (const byte *)key, strlen(key));
1029 }
1030 
1031 /* Compare two dictionaries. */
1032 int
cos_dict_equal(const cos_object_t * pco0,const cos_object_t * pco1,gx_device_pdf * pdev)1033 cos_dict_equal(const cos_object_t *pco0, const cos_object_t *pco1, gx_device_pdf *pdev)
1034 {
1035     const cos_dict_t *pcd0 = (const cos_dict_t *)pco0;
1036     const cos_dict_t *pcd1 = (const cos_dict_t *)pco1;
1037     cos_dict_element_t *pcde0 = pcd0->elements;
1038     cos_dict_element_t *pcde1 = pcd1->elements;
1039 
1040     for (; pcde1; pcde1 = pcde1->next) {
1041 	if (cos_dict_find(pcd0, pcde1->key.data, pcde1->key.size) == NULL)
1042 	    return false;
1043     }
1044     for (; pcde0; pcde0 = pcde0->next) {
1045 	const cos_value_t *v = cos_dict_find(pcd1, pcde0->key.data, pcde0->key.size);
1046 	int code;
1047 
1048 	if (v == NULL)
1049 	    return false;
1050 	code = cos_value_equal(&pcde0->value, v, pdev);
1051 	if (code < 0)
1052 	    return code;
1053 	if (!code)
1054 	    return false;
1055     }
1056     return true;
1057 }
1058 
1059 /* Set up a parameter list that writes into a Cos dictionary. */
1060 
1061 /* We'll implement the other printers later if we have to. */
1062 private param_proc_xmit_typed(cos_param_put_typed);
1063 private const gs_param_list_procs cos_param_list_writer_procs = {
1064     cos_param_put_typed,
1065     NULL /* begin_collection */ ,
1066     NULL /* end_collection */ ,
1067     NULL /* get_next_key */ ,
1068     gs_param_request_default,
1069     gs_param_requested_default
1070 };
1071 private int
cos_param_put_typed(gs_param_list * plist,gs_param_name pkey,gs_param_typed_value * pvalue)1072 cos_param_put_typed(gs_param_list * plist, gs_param_name pkey,
1073 		    gs_param_typed_value * pvalue)
1074 {
1075     cos_param_list_writer_t *const pclist =
1076 	(cos_param_list_writer_t *)plist;
1077     gx_device_pdf *pdev = pclist->pcd->pdev;
1078     gs_memory_t *mem = pclist->memory;
1079     cos_value_t value;
1080     cos_array_t *pca;
1081     int key_len = strlen(pkey);
1082     byte key_chars[100];		/****** ADHOC ******/
1083     int code;
1084 
1085     if (key_len > sizeof(key_chars) - 1)
1086 	return_error(gs_error_limitcheck);
1087     switch (pvalue->type) {
1088     default: {
1089 	param_printer_params_t ppp;
1090 	printer_param_list_t pplist;
1091 	stream s;
1092 	int len, skip;
1093 	byte *str;
1094 
1095 	ppp = param_printer_params_default;
1096 	ppp.prefix = ppp.suffix = ppp.item_prefix = ppp.item_suffix = 0;
1097 	ppp.print_ok = pclist->print_ok;
1098 	s_init_param_printer(&pplist, &ppp, &s);
1099 	swrite_position_only(&s);
1100 	param_write_typed((gs_param_list *)&pplist, "", pvalue);
1101 	len = stell(&s);
1102 	str = gs_alloc_string(mem, len, "cos_param_put(string)");
1103 	if (str == 0)
1104 	    return_error(gs_error_VMerror);
1105 	swrite_string(&s, str, len);
1106 	param_write_typed((gs_param_list *)&pplist, "", pvalue);
1107 	/*
1108 	 * The string starts with an initial / or /<space>, which
1109 	 * we need to remove.
1110 	 */
1111 	skip = (str[1] == ' ' ? 2 : 1);
1112 	memmove(str, str + skip, len - skip);
1113 	str = gs_resize_string(mem, str, len, len - skip,
1114 			       "cos_param_put(string)");
1115 	cos_string_value(&value, str, len - skip);
1116     }
1117 	break;
1118     case gs_param_type_int_array: {
1119 	uint i;
1120 
1121 	pca = cos_array_alloc(pdev, "cos_param_put(array)");
1122 	if (pca == 0)
1123 	    return_error(gs_error_VMerror);
1124 	for (i = 0; i < pvalue->value.ia.size; ++i)
1125 	    CHECK(cos_array_add_int(pca, pvalue->value.ia.data[i]));
1126     }
1127     av:
1128 	cos_object_value(&value, COS_OBJECT(pca));
1129 	break;
1130     case gs_param_type_float_array: {
1131 	uint i;
1132 
1133 	pca = cos_array_alloc(pdev, "cos_param_put(array)");
1134 	if (pca == 0)
1135 	    return_error(gs_error_VMerror);
1136 	for (i = 0; i < pvalue->value.ia.size; ++i)
1137 	    CHECK(cos_array_add_real(pca, pvalue->value.fa.data[i]));
1138     }
1139 	goto av;
1140     case gs_param_type_string_array:
1141     case gs_param_type_name_array:
1142 	/****** NYI ******/
1143 	return_error(gs_error_typecheck);
1144     }
1145     memcpy(key_chars + 1, pkey, key_len);
1146     key_chars[0] = '/';
1147     return cos_dict_put_no_copy(pclist->pcd, key_chars, key_len + 1, &value);
1148 }
1149 
1150 int
cos_param_list_writer_init(cos_param_list_writer_t * pclist,cos_dict_t * pcd,int print_ok)1151 cos_param_list_writer_init(cos_param_list_writer_t *pclist, cos_dict_t *pcd,
1152 			   int print_ok)
1153 {
1154     gs_param_list_init((gs_param_list *)pclist, &cos_param_list_writer_procs,
1155 		       COS_OBJECT_MEMORY(pcd));
1156     pclist->pcd = pcd;
1157     pclist->print_ok = print_ok;
1158     return 0;
1159 }
1160 
1161 /* ------ Streams ------ */
1162 
1163 private cos_proc_release(cos_stream_release);
1164 private cos_proc_write(cos_stream_write);
1165 private cos_proc_equal(cos_stream_equal);
1166 const cos_object_procs_t cos_stream_procs = {
1167     cos_stream_release, cos_stream_write, cos_stream_equal
1168 };
1169 
1170 cos_stream_t *
cos_stream_alloc(gx_device_pdf * pdev,client_name_t cname)1171 cos_stream_alloc(gx_device_pdf *pdev, client_name_t cname)
1172 {
1173     gs_memory_t *mem = pdev->pdf_memory;
1174     cos_stream_t *pcs =
1175 	gs_alloc_struct(mem, cos_stream_t, &st_cos_object, cname);
1176 
1177     cos_object_init((cos_object_t *)pcs, pdev, &cos_stream_procs);
1178     return pcs;
1179 }
1180 
1181 private void
cos_stream_release(cos_object_t * pco,client_name_t cname)1182 cos_stream_release(cos_object_t *pco, client_name_t cname)
1183 {
1184     gs_memory_t *mem = cos_object_memory(pco);
1185     cos_stream_t *const pcs = (cos_stream_t *)pco;
1186     cos_stream_piece_t *cur;
1187     cos_stream_piece_t *next;
1188 
1189     for (cur = pcs->pieces; cur; cur = next) {
1190 	next = cur->next;
1191 	gs_free_object(mem, cur, cname);
1192     }
1193     pcs->pieces = 0;
1194     cos_dict_release(pco, cname);
1195 }
1196 
1197 private int
cos_stream_equal(const cos_object_t * pco0,const cos_object_t * pco1,gx_device_pdf * pdev)1198 cos_stream_equal(const cos_object_t *pco0, const cos_object_t *pco1, gx_device_pdf *pdev)
1199 {
1200     const cos_stream_t *pcs0 = (const cos_stream_t *)pco0;
1201     const cos_stream_t *pcs1 = (const cos_stream_t *)pco1;
1202     bool result = false;
1203     int code;
1204 
1205     code = cos_dict_equal(pco0, pco1, pdev);
1206     if (code < 0)
1207 	return code;
1208     if (!code)
1209 	return false;
1210     {
1211 	/* fixme : this assumes same segmentation for both streams.
1212 	   In general it is not true. */
1213 	FILE *sfile = pdev->streams.file;
1214 	cos_stream_piece_t *pcsp0 = pcs0->pieces, *pcsp1 = pcs1->pieces;
1215 	long position_save = ftell(sfile);
1216 
1217 	for (; pcsp0 && pcsp1; pcsp0 = pcsp0->next, pcsp1 = pcsp1->next) {
1218 	    long position0 = pcsp0->position;
1219 	    long position1 = pcsp1->position;
1220 	    uint size0 = pcsp0->size;
1221 	    uint size1 = pcsp1->size;
1222 	    byte buf0[512], buf1[sizeof(buf0)];
1223 
1224 	    if (size0 != size1)
1225 		goto notequal;
1226 	    for(; size0; position0 += size1, position1 += size1, size0 -= size1) {
1227 		size1 = min(sizeof(buf0), size0);
1228 		fseek(sfile, position0, SEEK_SET);
1229 		if (fread(buf0, 1, size1, sfile) != size1) {
1230 		    result = gs_note_error(gs_error_ioerror);
1231 		    goto notequal;
1232 		}
1233 		fseek(sfile, position1, SEEK_SET);
1234 		if (fread(buf1, 1, size1, sfile) != size1) {
1235 		    result = gs_note_error(gs_error_ioerror);
1236 		    goto notequal;
1237 		}
1238 		if (memcmp(buf0, buf1, size1))
1239 		    goto notequal;
1240 	    }
1241 	}
1242 	if (pcsp0 || pcsp1)
1243 	    goto notequal;
1244 	result = true;
1245 notequal:
1246 	fseek(sfile, position_save, SEEK_SET);
1247 	return result;
1248     }
1249 }
1250 
1251 /* Find the total length of a stream. */
1252 long
cos_stream_length(const cos_stream_t * pcs)1253 cos_stream_length(const cos_stream_t *pcs)
1254 {
1255     return pcs->length;
1256 }
1257 
1258 /* Write the (dictionary) elements of a stream. */
1259 /* (This procedure is exported.) */
1260 int
cos_stream_elements_write(const cos_stream_t * pcs,gx_device_pdf * pdev)1261 cos_stream_elements_write(const cos_stream_t *pcs, gx_device_pdf *pdev)
1262 {
1263     return cos_elements_write(pdev->strm, pcs->elements, pdev, true, pcs->id);
1264 }
1265 
1266 /* Write the contents of a stream.  (This procedure is exported.) */
1267 int
cos_stream_contents_write(const cos_stream_t * pcs,gx_device_pdf * pdev)1268 cos_stream_contents_write(const cos_stream_t *pcs, gx_device_pdf *pdev)
1269 {
1270     stream *s = pdev->strm;
1271     cos_stream_piece_t *pcsp;
1272     cos_stream_piece_t *last;
1273     cos_stream_piece_t *next;
1274     FILE *sfile = pdev->streams.file;
1275     long end_pos;
1276     bool same_file = (pdev->sbstack_depth > 0);
1277     int code;
1278     stream_arcfour_state sarc4, *ss = NULL;
1279 
1280     if (pdev->KeyLength) {
1281 	code = pdf_encrypt_init(pdev, pcs->id, &sarc4);
1282 	if (code < 0)
1283 	    return code;
1284 	ss = &sarc4;
1285     }
1286     sflush(s);
1287     sflush(pdev->streams.strm);
1288 
1289     /* Reverse the elements temporarily. */
1290     for (pcsp = pcs->pieces, last = NULL; pcsp; pcsp = next)
1291 	next = pcsp->next, pcsp->next = last, last = pcsp;
1292     for (pcsp = last, code = 0; pcsp && code >= 0; pcsp = pcsp->next) {
1293 	if (same_file)
1294 	    pdf_copy_data_safe(s, sfile, pcsp->position, pcsp->size);
1295 	else {
1296 	    end_pos = ftell(sfile);
1297 	    fseek(sfile, pcsp->position, SEEK_SET);
1298 	    pdf_copy_data(s, sfile, pcsp->size, ss);
1299 	    fseek(sfile, end_pos, SEEK_SET);
1300 	}
1301     }
1302     /* Reverse the elements back. */
1303     for (pcsp = last, last = NULL; pcsp; pcsp = next)
1304 	next = pcsp->next, pcsp->next = last, last = pcsp;
1305 
1306     return code;
1307 }
1308 
1309 private int
cos_stream_write(const cos_object_t * pco,gx_device_pdf * pdev,gs_id object_id)1310 cos_stream_write(const cos_object_t *pco, gx_device_pdf *pdev, gs_id object_id)
1311 {
1312     stream *s = pdev->strm;
1313     const cos_stream_t *const pcs = (const cos_stream_t *)pco;
1314     int code;
1315 
1316     if (pcs->input_strm != NULL) {
1317 	stream *s = pco->input_strm;
1318 	int status = s_close_filters(&s, NULL);
1319 
1320 	if (status < 0)
1321 	    return_error(gs_error_ioerror);
1322 	/* We have to break const here to clear the input_strm. */
1323 	((cos_object_t *)pco)->input_strm = 0;
1324     }
1325     stream_puts(s, "<<");
1326     cos_elements_write(s, pcs->elements, pdev, false, object_id);
1327     pprintld1(s, "/Length %ld>>stream\n", cos_stream_length(pcs));
1328     code = cos_stream_contents_write(pcs, pdev);
1329     stream_puts(s, "\nendstream\n");
1330 
1331     return code;
1332 }
1333 
1334 /* Return a stream's dictionary (just a cast). */
1335 cos_dict_t *
cos_stream_dict(cos_stream_t * pcs)1336 cos_stream_dict(cos_stream_t *pcs)
1337 {
1338     return (cos_dict_t *)pcs;
1339 }
1340 
1341 /* Add a contents piece to a stream object: size bytes just written on */
1342 /* streams.strm. */
1343 int
cos_stream_add(cos_stream_t * pcs,uint size)1344 cos_stream_add(cos_stream_t *pcs, uint size)
1345 {
1346     gx_device_pdf *pdev = pcs->pdev;
1347     stream *s = pdev->streams.strm;
1348     long position = stell(s);
1349     cos_stream_piece_t *prev = pcs->pieces;
1350 
1351     /* Check for consecutive writing -- just an optimization. */
1352     if (prev != 0 && prev->position + prev->size + size == position) {
1353 	prev->size += size;
1354     } else {
1355 	gs_memory_t *mem = pdev->pdf_memory;
1356 	cos_stream_piece_t *pcsp =
1357 	    gs_alloc_struct(mem, cos_stream_piece_t, &st_cos_stream_piece,
1358 			    "cos_stream_add");
1359 
1360 	if (pcsp == 0)
1361 	    return_error(gs_error_VMerror);
1362 	pcsp->position = position - size;
1363 	pcsp->size = size;
1364 	pcsp->next = pcs->pieces;
1365 	pcs->pieces = pcsp;
1366     }
1367     pcs->length += size;
1368     return 0;
1369 }
1370 
1371 /* Add bytes to a stream object. */
1372 int
cos_stream_add_bytes(cos_stream_t * pcs,const byte * data,uint size)1373 cos_stream_add_bytes(cos_stream_t *pcs, const byte *data, uint size)
1374 {
1375     stream_write(pcs->pdev->streams.strm, data, size);
1376     return cos_stream_add(pcs, size);
1377 }
1378 
1379 /* Add the contents of a stream to a stream object. */
1380 int
cos_stream_add_stream_contents(cos_stream_t * pcs,stream * s)1381 cos_stream_add_stream_contents(cos_stream_t *pcs, stream *s)
1382 {
1383     int code = 0;
1384     byte sbuff[200];	/* arbitrary */
1385     uint cnt;
1386     int status = sseek(s, 0);
1387 
1388     if (status < 0)
1389 	return_error(gs_error_ioerror);
1390     do {
1391 	status = sgets(s, sbuff, sizeof(sbuff), &cnt);
1392 
1393 	if (cnt == 0) {
1394 	    if (status == EOFC)
1395 		break;
1396 	    return_error(gs_error_ioerror);
1397 	}
1398     } while ((code = cos_stream_add_bytes(pcs, sbuff, cnt)) >= 0);
1399     return code;
1400 }
1401 
1402 /* Release the last contents piece of a stream object. */
1403 /* Warning : this function can't release pieces if another stream is written after them. */
1404 int
cos_stream_release_pieces(cos_stream_t * pcs)1405 cos_stream_release_pieces(cos_stream_t *pcs)
1406 {
1407     gx_device_pdf *pdev = pcs->pdev;
1408     stream *s = pdev->streams.strm;
1409     long position = stell(s), position0 = position;
1410     gs_memory_t *mem = cos_object_memory((cos_object_t *)pcs);
1411 
1412     while (pcs->pieces != NULL &&
1413 		position == pcs->pieces->position + pcs->pieces->size) {
1414 	cos_stream_piece_t *p = pcs->pieces;
1415 
1416 	position -= p->size;
1417 	pcs->pieces = p->next;
1418 	gs_free_object(mem, p, "cos_stream_release_pieces");
1419     }
1420     if (position0 != position)
1421 	if (sseek(s, position) < 0)
1422 	    return_error(gs_error_ioerror);
1423     return 0;
1424 }
1425 
1426 /* Create a stream that writes into a Cos stream. */
1427 /* Closing the stream will free it. */
1428 /* Note that this is not a filter. */
1429 typedef struct cos_write_stream_state_s {
1430     stream_state_common;
1431     cos_stream_t *pcs;
1432     gx_device_pdf *pdev;
1433     stream *s;			/* pointer back to stream */
1434     stream *target;		/* use this instead of strm */
1435 } cos_write_stream_state_t;
1436 gs_private_st_suffix_add4(st_cos_write_stream_state, cos_write_stream_state_t,
1437 			  "cos_write_stream_state_t",
1438 			  cos_ws_state_enum_ptrs, cos_ws_state_reloc_ptrs,
1439 			  st_stream_state, pcs, pdev, s, target);
1440 
1441 private int
cos_write_stream_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * ignore_pw,bool last)1442 cos_write_stream_process(stream_state * st, stream_cursor_read * pr,
1443 			 stream_cursor_write * ignore_pw, bool last)
1444 {
1445     uint count = pr->limit - pr->ptr;
1446     cos_write_stream_state_t *ss = (cos_write_stream_state_t *)st;
1447     gx_device_pdf *pdev = ss->pdev;
1448     stream *target = ss->target;
1449     long start_pos = stell(pdev->streams.strm);
1450     int code;
1451 
1452     stream_write(target, pr->ptr + 1, count);
1453     pr->ptr = pr->limit;
1454     sflush(target);
1455     code = cos_stream_add(ss->pcs, (uint)(stell(pdev->streams.strm) - start_pos));
1456     return (code < 0 ? ERRC : 0);
1457 }
1458 private int
cos_write_stream_close(stream * s)1459 cos_write_stream_close(stream *s)
1460 {
1461     cos_write_stream_state_t *ss = (cos_write_stream_state_t *)s->state;
1462     int status;
1463 
1464     sflush(s);
1465     status = s_close_filters(&ss->target, ss->pdev->streams.strm);
1466     return (status < 0 ? status : s_std_close(s));
1467 }
1468 
1469 private const stream_procs cos_s_procs = {
1470     s_std_noavailable, s_std_noseek, s_std_write_reset,
1471     s_std_write_flush, cos_write_stream_close, cos_write_stream_process
1472 };
1473 private const stream_template cos_write_stream_template = {
1474     &st_cos_write_stream_state, 0, cos_write_stream_process, 1, 1
1475 };
1476 stream *
cos_write_stream_alloc(cos_stream_t * pcs,gx_device_pdf * pdev,client_name_t cname)1477 cos_write_stream_alloc(cos_stream_t *pcs, gx_device_pdf *pdev,
1478 		       client_name_t cname)
1479 {
1480     gs_memory_t *mem = pdev->pdf_memory;
1481     stream *s = s_alloc(mem, cname);
1482     cos_write_stream_state_t *ss = (cos_write_stream_state_t *)
1483 	s_alloc_state(mem, &st_cos_write_stream_state, cname);
1484 #define CWS_BUF_SIZE 512	/* arbitrary */
1485     byte *buf = gs_alloc_bytes(mem, CWS_BUF_SIZE, cname);
1486 
1487     if (s == 0 || ss == 0 || buf == 0)
1488 	goto fail;
1489     ss->template = &cos_write_stream_template;
1490     ss->pcs = pcs;
1491     ss->pdev = pdev;
1492     ss->s = s;
1493     ss->target = pdev->streams.strm; /* not s->strm */
1494     s_std_init(s, buf, CWS_BUF_SIZE, &cos_s_procs, s_mode_write);
1495     s->state = (stream_state *)ss;
1496     return s;
1497 #undef CWS_BUF_SIZE
1498  fail:
1499     gs_free_object(mem, buf, cname);
1500     gs_free_object(mem, ss, cname);
1501     gs_free_object(mem, s, cname);
1502     return 0;
1503 }
1504 
1505 /* Get cos stream from pipeline. */
1506 cos_stream_t *
cos_stream_from_pipeline(stream * s)1507 cos_stream_from_pipeline(stream *s)
1508 {
1509     cos_write_stream_state_t *ss;
1510 
1511     while(s->procs.process != cos_s_procs.process)
1512 	s = s->strm;
1513     ss = (cos_write_stream_state_t *)s->state;
1514     return ss->pcs;
1515 }
1516 
1517 /* Get cos write stream from pipeline. */
1518 stream *
cos_write_stream_from_pipeline(stream * s)1519 cos_write_stream_from_pipeline(stream *s)
1520 {
1521     while(s->procs.process != cos_s_procs.process)
1522 	s = s->strm;
1523     return s;
1524 }
1525 
1526