xref: /plan9/sys/src/cmd/gs/src/gdevpdfm.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1996, 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: gdevpdfm.c,v 1.50 2005/10/17 19:23:44 leonardo Exp $ */
18 /* pdfmark processing for PDF-writing driver */
19 #include "math_.h"
20 #include "memory_.h"
21 #include "string_.h"
22 #include "gx.h"
23 #include "gserrors.h"
24 #include "gsutil.h"		/* for bytes_compare */
25 #include "gdevpdfx.h"
26 #include "gdevpdfo.h"
27 #include "scanchar.h"
28 #include "szlibx.h"
29 #include "slzwx.h"
30 
31 /* GC descriptors */
32 private_st_pdf_article();
33 
34 /*
35  * The pdfmark pseudo-parameter indicates the occurrence of a pdfmark
36  * operator in the input file.  Its "value" is the arguments of the operator,
37  * passed through essentially unchanged:
38  *      (key, value)*, CTM, type
39  */
40 
41 /*
42  * Define an entry in a table of pdfmark-processing procedures.
43  * (The actual table is at the end of this file, to avoid the need for
44  * forward declarations for the procedures.)
45  */
46 #define PDFMARK_NAMEABLE 1	/* allows _objdef */
47 #define PDFMARK_ODD_OK 2	/* OK if odd # of parameters */
48 #define PDFMARK_KEEP_NAME 4	/* don't substitute reference for name */
49 				/* in 1st argument */
50 #define PDFMARK_NO_REFS 8	/* don't substitute references for names */
51 				/* anywhere */
52 #define PDFMARK_TRUECTM 16	/* pass the true CTM to the procedure, */
53 				/* not the one transformed to reflect the default user space */
54 typedef struct pdfmark_name_s {
55     const char *mname;
56     pdfmark_proc((*proc));
57     byte options;
58 } pdfmark_name;
59 
60 /* ---------------- Public utilities ---------------- */
61 
62 /* Compare a C string and a gs_param_string. */
63 bool
pdf_key_eq(const gs_param_string * pcs,const char * str)64 pdf_key_eq(const gs_param_string * pcs, const char *str)
65 {
66     return (strlen(str) == pcs->size &&
67 	    !strncmp(str, (const char *)pcs->data, pcs->size));
68 }
69 
70 /* Scan an integer out of a parameter string. */
71 int
pdfmark_scan_int(const gs_param_string * pstr,int * pvalue)72 pdfmark_scan_int(const gs_param_string * pstr, int *pvalue)
73 {
74 #define MAX_INT_STR 20
75     uint size = pstr->size;
76     char str[MAX_INT_STR + 1];
77 
78     if (size > MAX_INT_STR)
79 	return_error(gs_error_limitcheck);
80     memcpy(str, pstr->data, size);
81     str[size] = 0;
82     return (sscanf(str, "%d", pvalue) == 1 ? 0 :
83 	    gs_note_error(gs_error_rangecheck));
84 #undef MAX_INT_STR
85 }
86 
87 /* ---------------- Private utilities ---------------- */
88 
89 /* Find a key in a dictionary. */
90 private bool
pdfmark_find_key(const char * key,const gs_param_string * pairs,uint count,gs_param_string * pstr)91 pdfmark_find_key(const char *key, const gs_param_string * pairs, uint count,
92 		 gs_param_string * pstr)
93 {
94     uint i;
95 
96     for (i = 0; i < count; i += 2)
97 	if (pdf_key_eq(&pairs[i], key)) {
98 	    *pstr = pairs[i + 1];
99 	    return true;
100 	}
101     pstr->data = 0;
102     pstr->size = 0;
103     return false;
104 }
105 
106 /*
107  * Get the page number for a page referenced by number or as /Next or /Prev.
108  * The result may be 0 if the page number is 0 or invalid.
109  */
110 private int
pdfmark_page_number(gx_device_pdf * pdev,const gs_param_string * pnstr)111 pdfmark_page_number(gx_device_pdf * pdev, const gs_param_string * pnstr)
112 {
113     int page = pdev->next_page + 1;
114 
115     if (pnstr->data == 0);
116     else if (pdf_key_eq(pnstr, "/Next"))
117 	++page;
118     else if (pdf_key_eq(pnstr, "/Prev"))
119 	--page;
120     else if (pdfmark_scan_int(pnstr, &page) < 0)
121 	page = 0;
122     if (pdev->max_referred_page < page)
123 	pdev->max_referred_page = page;
124     return page;
125 }
126 
127 /* Construct a destination string specified by /Page and/or /View. */
128 /* Return 0 if none (but still fill in a default), 1 or 2 if present */
129 /* (1 if only one of /Page or /View, 2 if both), <0 if error. */
130 private int
pdfmark_make_dest(char dstr[MAX_DEST_STRING],gx_device_pdf * pdev,const char * Page_key,const char * View_key,const gs_param_string * pairs,uint count)131 pdfmark_make_dest(char dstr[MAX_DEST_STRING], gx_device_pdf * pdev,
132 		  const char *Page_key, const char *View_key,
133 		  const gs_param_string * pairs, uint count)
134 {
135     gs_param_string page_string, view_string;
136     int present =
137 	pdfmark_find_key(Page_key, pairs, count, &page_string) +
138 	pdfmark_find_key(View_key, pairs, count, &view_string);
139     int page = pdfmark_page_number(pdev, &page_string);
140     gs_param_string action;
141     int len;
142 
143     if (view_string.size == 0)
144 	param_string_from_string(view_string, "[/XYZ null null null]");
145     if (page == 0)
146 	strcpy(dstr, "[null ");
147     else if (pdfmark_find_key("/Action", pairs, count, &action) &&
148 	     pdf_key_eq(&action, "/GoToR")
149 	)
150 	sprintf(dstr, "[%d ", page - 1);
151     else
152 	sprintf(dstr, "[%ld 0 R ", pdf_page_id(pdev, page));
153     len = strlen(dstr);
154     if (len + view_string.size > MAX_DEST_STRING)
155 	return_error(gs_error_limitcheck);
156     if (view_string.data[0] != '[' ||
157 	view_string.data[view_string.size - 1] != ']'
158 	)
159 	return_error(gs_error_rangecheck);
160     memcpy(dstr + len, view_string.data + 1, view_string.size - 1);
161     dstr[len + view_string.size - 1] = 0;
162     return present;
163 }
164 
165 /*
166  * If a named destination is specified by a string, convert it to a name,
167  * update *dstr, and return 1; otherwise return 0.
168  */
169 private int
pdfmark_coerce_dest(gs_param_string * dstr,char dest[MAX_DEST_STRING])170 pdfmark_coerce_dest(gs_param_string *dstr, char dest[MAX_DEST_STRING])
171 {
172     const byte *data = dstr->data;
173     uint size = dstr->size;
174 
175     if (size == 0 || data[0] != '(')
176 	return 0;
177     /****** HANDLE ESCAPES ******/
178     memcpy(dest, data, size - 1);
179     dest[0] = '/';
180     dest[size - 1] = 0;
181     dstr->data = (byte *)dest;
182     dstr->size = size - 1;
183     return 1;
184 }
185 
186 /* Put pairs in a dictionary. */
187 private int
pdfmark_put_c_pair(cos_dict_t * pcd,const char * key,const gs_param_string * pvalue)188 pdfmark_put_c_pair(cos_dict_t *pcd, const char *key,
189 		   const gs_param_string * pvalue)
190 {
191     return cos_dict_put_c_key_string(pcd, key, pvalue->data, pvalue->size);
192 }
193 private int
pdfmark_put_pair(cos_dict_t * pcd,const gs_param_string * pair)194 pdfmark_put_pair(cos_dict_t *pcd, const gs_param_string * pair)
195 {
196     return cos_dict_put_string(pcd, pair->data, pair->size,
197 			       pair[1].data, pair[1].size);
198 }
199 
200 /* Scan a Rect value. */
201 private int
pdfmark_scan_rect(gs_rect * prect,const gs_param_string * str,const gs_matrix * pctm)202 pdfmark_scan_rect(gs_rect * prect, const gs_param_string * str,
203 		  const gs_matrix * pctm)
204 {
205     uint size = str->size;
206     double v[4];
207 #define MAX_RECT_STRING 100
208     char chars[MAX_RECT_STRING + 3];
209     int end_check;
210 
211     if (str->size > MAX_RECT_STRING)
212 	return_error(gs_error_limitcheck);
213     memcpy(chars, str->data, size);
214     strcpy(chars + size, " 0");
215     if (sscanf(chars, "[%lg %lg %lg %lg]%d",
216 	       &v[0], &v[1], &v[2], &v[3], &end_check) != 5
217 	)
218 	return_error(gs_error_rangecheck);
219     gs_point_transform(v[0], v[1], pctm, &prect->p);
220     gs_point_transform(v[2], v[3], pctm, &prect->q);
221     return 0;
222 }
223 
224 /* Make a Rect value. */
225 private void
pdfmark_make_rect(char str[MAX_RECT_STRING],const gs_rect * prect)226 pdfmark_make_rect(char str[MAX_RECT_STRING], const gs_rect * prect)
227 {
228     /*
229      * We have to use a stream and pprintf, rather than sprintf,
230      * because printf formats can't express the PDF restrictions on
231      * the form of the output.
232      */
233     stream s;
234 
235     s_init(&s, NULL);
236     swrite_string(&s, (byte *)str, MAX_RECT_STRING - 1);
237     pprintg4(&s, "[%g %g %g %g]",
238 	     prect->p.x, prect->p.y, prect->q.x, prect->q.y);
239     str[stell(&s)] = 0;
240 }
241 
242 /* Write a transformed Border value on a stream. */
243 private int
pdfmark_write_border(stream * s,const gs_param_string * str,const gs_matrix * pctm)244 pdfmark_write_border(stream *s, const gs_param_string *str,
245 		     const gs_matrix *pctm)
246 {
247     /*
248      * We don't preserve the entire CTM in the output, and it isn't clear
249      * what CTM is applicable to annotations anyway: we only attempt to
250      * handle well-behaved CTMs here.
251      */
252     uint size = str->size;
253 #define MAX_BORDER_STRING 100
254     char chars[MAX_BORDER_STRING + 1];
255     double bx, by, c;
256     gs_point bpt, cpt;
257     const char *next;
258 
259     if (str->size > MAX_BORDER_STRING)
260 	return_error(gs_error_limitcheck);
261     memcpy(chars, str->data, size);
262     chars[size] = 0;
263     if (sscanf(chars, "[%lg %lg %lg", &bx, &by, &c) != 3)
264 	return_error(gs_error_rangecheck);
265     gs_distance_transform(bx, by, pctm, &bpt);
266     gs_distance_transform(0.0, c, pctm, &cpt);
267     pprintg3(s, "[%g %g %g", fabs(bpt.x), fabs(bpt.y), fabs(cpt.x + cpt.y));
268     /*
269      * We don't attempt to do 100% reliable syntax checking here --
270      * it's just not worth the trouble.
271      */
272     next = strchr(chars + 1, ']');
273     if (next == 0)
274 	return_error(gs_error_rangecheck);
275     if (next[1] != 0) {
276 	/* Handle a dash array.  This is tiresome. */
277 	double v;
278 
279 	stream_putc(s, '[');
280 	while (next != 0 && sscanf(++next, "%lg", &v) == 1) {
281 	    gs_point vpt;
282 
283 	    gs_distance_transform(0.0, v, pctm, &vpt);
284 	    pprintg1(s, "%g ", fabs(vpt.x + vpt.y));
285 	    next = strchr(next, ' ');
286 	}
287 	stream_putc(s, ']');
288     }
289     stream_putc(s, ']');
290     return 0;
291 }
292 
293 /* Put an element in a stream's dictionary. */
294 private int
cos_stream_put_c_strings(cos_stream_t * pcs,const char * key,const char * value)295 cos_stream_put_c_strings(cos_stream_t *pcs, const char *key, const char *value)
296 {
297     return cos_dict_put_c_strings(cos_stream_dict(pcs), key, value);
298 }
299 
300 /* Setup pdfmak stream compression. */
301 private int
setup_pdfmark_stream_compression(gx_device_psdf * pdev0,cos_stream_t * pco)302 setup_pdfmark_stream_compression(gx_device_psdf *pdev0,
303 			cos_stream_t *pco)
304 {
305     /* This function is for pdfwrite only. */
306     gx_device_pdf *pdev = (gx_device_pdf *)pdev0;
307     gs_memory_t *mem = pdev->pdf_memory;
308     static const pdf_filter_names_t fnames = {
309 	PDF_FILTER_NAMES
310     };
311     const stream_template *template =
312 	(pdev->params.UseFlateCompression &&
313 	 pdev->version >= psdf_version_ll3 ?
314 	 &s_zlibE_template : &s_LZWE_template);
315     stream_state *st;
316 
317     pco->input_strm = cos_write_stream_alloc(pco, pdev,
318 				  "setup_pdfmark_stream_compression");
319     if (pco->input_strm == 0)
320 	return_error(gs_error_VMerror);
321     if (!pdev->binary_ok) {
322 	stream_state *ss = s_alloc_state(mem, s_A85E_template.stype,
323 			  "setup_pdfmark_stream_compression");
324 	if (ss == 0)
325 	    return_error(gs_error_VMerror);
326 	if (s_add_filter(&pco->input_strm, &s_A85E_template, ss, mem) == 0) {
327 	    gs_free_object(mem, ss, "setup_image_compression");
328 	    return_error(gs_error_VMerror);
329 	}
330     }
331     st = s_alloc_state(mem, template->stype,
332 			    "setup_pdfmark_stream_compression");
333     if (st == 0)
334 	return_error(gs_error_VMerror);
335     if (template->set_defaults)
336 	(*template->set_defaults) (st);
337     if (s_add_filter(&pco->input_strm, template, st, mem) == 0) {
338         gs_free_object(mem, st, "setup_image_compression");
339         return_error(gs_error_VMerror);
340     }
341     return pdf_put_filters(cos_stream_dict(pco), pdev, pco->input_strm, &fnames);
342 }
343 
344 /* ---------------- Miscellaneous pdfmarks ---------------- */
345 
346 /*
347  * Create the dictionary for an annotation or outline.  For some
348  * unfathomable reason, PDF requires the following key substitutions
349  * relative to pdfmarks:
350  *   In annotation and link dictionaries:
351  *     /Action => /A, /Color => /C, /Title => /T
352  *   In outline directionaries:
353  *     /Action => /A, but *not* /Color or /Title
354  *   In Action subdictionaries:
355  *     /Dest => /D, /File => /F, /Subtype => /S
356  * and also the following substitutions:
357  *     /Action /Launch /File xxx =>
358  *       /A << /S /Launch /F xxx >>
359  *     /Action /GoToR /File xxx /Dest yyy =>
360  *       /A << /S /GoToR /F xxx /D yyy' >>
361  *     /Action /Article /Dest yyy =>
362  *       /A << /S /Thread /D yyy' >>
363  *     /Action /GoTo => drop the Action key
364  * Also, \n in Contents strings must be replaced with \r.
365  * Also, an outline dictionary with no action, Dest, Page, or View has an
366  * implied GoTo action with Dest = [{ThisPage} /XYZ null null null].
367  * Note that for Thread actions, the Dest is not a real destination,
368  * and must not be processed as one.
369  *
370  * We always treat /A and /F as equivalent to /Action and /File
371  * respectively.  The pdfmark and PDF documentation is so confused on the
372  * issue of when the long and short names should be used that we only give
373  * this a 50-50 chance of being right.
374  *
375  * Note that we must transform Rect and Border coordinates.
376  */
377 
378 typedef struct ao_params_s {
379     gx_device_pdf *pdev;	/* for pdfmark_make_dest */
380     const char *subtype;	/* default Subtype in top-level dictionary */
381     long src_pg;		/* set to SrcPg - 1 if any */
382 } ao_params_t;
383 private int
pdfmark_put_ao_pairs(gx_device_pdf * pdev,cos_dict_t * pcd,const gs_param_string * pairs,uint count,const gs_matrix * pctm,ao_params_t * params,bool for_outline)384 pdfmark_put_ao_pairs(gx_device_pdf * pdev, cos_dict_t *pcd,
385 		     const gs_param_string * pairs, uint count,
386 		     const gs_matrix * pctm, ao_params_t * params,
387 		     bool for_outline)
388 {
389     const gs_param_string *Action = 0;
390     const gs_param_string *File = 0;
391     gs_param_string Dest;
392     gs_param_string Subtype;
393     uint i;
394     int code;
395     char dest[MAX_DEST_STRING];
396     bool coerce_dest = false;
397 
398     Dest.data = 0;
399     if (params->subtype)
400 	param_string_from_string(Subtype, params->subtype);
401     else
402 	Subtype.data = 0;
403     for (i = 0; i < count; i += 2) {
404 	const gs_param_string *pair = &pairs[i];
405 	long src_pg;
406 
407 	if (pdf_key_eq(pair, "/SrcPg") &&
408 	    sscanf((const char *)pair[1].data, "%ld", &src_pg) == 1
409 	    )
410 	    params->src_pg = src_pg - 1;
411 	else if (!for_outline && pdf_key_eq(pair, "/Color"))
412 	    pdfmark_put_c_pair(pcd, "/C", pair + 1);
413 	else if (!for_outline && pdf_key_eq(pair, "/Title"))
414 	    pdfmark_put_c_pair(pcd, "/T", pair + 1);
415 	else if (pdf_key_eq(pair, "/Action") || pdf_key_eq(pair, "/A"))
416 	    Action = pair;
417 	else if (pdf_key_eq(pair, "/File") || pdf_key_eq(pair, "/F"))
418 	    File = pair;
419 	else if (pdf_key_eq(pair, "/Dest")) {
420 	    Dest = pair[1];
421 	    coerce_dest = true;
422 	}
423 	else if (pdf_key_eq(pair, "/Page") || pdf_key_eq(pair, "/View")) {
424 	    /* Make a destination even if this is for an outline. */
425 	    if (Dest.data == 0) {
426 		code = pdfmark_make_dest(dest, params->pdev, "/Page", "/View",
427 					 pairs, count);
428 		if (code < 0)
429 		    return code;
430 		param_string_from_string(Dest, dest);
431 		if (for_outline)
432 		    coerce_dest = false;
433 	    }
434 	} else if (pdf_key_eq(pair, "/Subtype"))
435 	    Subtype = pair[1];
436 	/*
437 	 * We also have to replace all occurrences of \n in Contents
438 	 * strings with \r.  Unfortunately, they probably have already
439 	 * been converted to \012....
440 	 */
441 	else if (pdf_key_eq(pair, "/Contents")) {
442 	    byte *cstr;
443 	    uint csize = pair[1].size;
444 	    cos_value_t *pcv;
445 	    uint i, j;
446 
447 	    /*
448 	     * Copy the string into value storage, then update it in place.
449 	     */
450 	    pdfmark_put_pair(pcd, pair);
451 	    /* Break const so we can update the (copied) string. */
452 	    pcv = (cos_value_t *)cos_dict_find_c_key(pcd, "/Contents");
453 	    cstr = pcv->contents.chars.data;
454 	    /* Loop invariant: j <= i < csize. */
455 	    for (i = j = 0; i < csize;)
456 		if (csize - i >= 2 && !memcmp(cstr + i, "\\n", 2) &&
457 		    (i == 0 || cstr[i - 1] != '\\')
458 		    ) {
459 		    cstr[j] = '\\', cstr[j + 1] = 'r';
460 		    i += 2, j += 2;
461 		} else if (csize - i >= 4 && !memcmp(cstr + i, "\\012", 4) &&
462 			   (i == 0 || cstr[i - 1] != '\\')
463 		    ) {
464 		    cstr[j] = '\\', cstr[j + 1] = 'r';
465 		    i += 4, j += 2;
466 		} else
467 		    cstr[j++] = cstr[i++];
468 	    if (j != i)
469 		pcv->contents.chars.data =
470 		    gs_resize_string(pdev->pdf_memory, cstr, csize, j,
471 				     "pdfmark_put_ao_pairs");
472 	} else if (pdf_key_eq(pair, "/Rect")) {
473 	    gs_rect rect;
474 	    char rstr[MAX_RECT_STRING];
475 	    int code = pdfmark_scan_rect(&rect, pair + 1, pctm);
476 
477 	    if (code < 0)
478 		return code;
479 	    pdfmark_make_rect(rstr, &rect);
480 	    cos_dict_put_c_key_string(pcd, "/Rect", (byte *)rstr,
481 				      strlen(rstr));
482 	} else if (pdf_key_eq(pair, "/Border")) {
483 	    stream s;
484 	    char bstr[MAX_BORDER_STRING + 1];
485 	    int code;
486 
487 	    s_init(&s, NULL);
488 	    swrite_string(&s, (byte *)bstr, MAX_BORDER_STRING + 1);
489 	    code = pdfmark_write_border(&s, pair + 1, pctm);
490 	    if (code < 0)
491 		return code;
492 	    if (stell(&s) > MAX_BORDER_STRING)
493 		return_error(gs_error_limitcheck);
494 	    bstr[stell(&s)] = 0;
495 	    cos_dict_put_c_key_string(pcd, "/Border", (byte *)bstr,
496 				      strlen(bstr));
497 	} else if (for_outline && pdf_key_eq(pair, "/Count"))
498 	    DO_NOTHING;
499 	else
500 	    pdfmark_put_pair(pcd, pair);
501     }
502     if (!for_outline && pdf_key_eq(&Subtype, "/Link")) {
503 	if (Action) {
504 	    /* Don't delete the Dest for GoTo or file-GoToR. */
505 	    if (pdf_key_eq(Action + 1, "/GoTo") ||
506 		(File && pdf_key_eq(Action + 1, "/GoToR"))
507 		)
508 		DO_NOTHING;
509 	    else
510 		Dest.data = 0;
511 	}
512     }
513 
514     /* Now handle the deferred keys. */
515     if (Action) {
516 	const byte *astr = Action[1].data;
517 	const uint asize = Action[1].size;
518 
519 	if ((File != 0 || Dest.data != 0) &&
520 	    (pdf_key_eq(Action + 1, "/Launch") ||
521 	     (pdf_key_eq(Action + 1, "/GoToR") && File) ||
522 	     pdf_key_eq(Action + 1, "/Article"))
523 	    ) {
524 	    cos_dict_t *adict = cos_dict_alloc(pdev, "action dict");
525 	    cos_value_t avalue;
526 
527 	    if (adict == 0)
528 		return_error(gs_error_VMerror);
529 	    if (!for_outline) {
530 		/* We aren't sure whether this is really needed.... */
531 		cos_dict_put_c_strings(adict, "/Type", "/Action");
532 	    }
533 	    if (pdf_key_eq(Action + 1, "/Article")) {
534 		cos_dict_put_c_strings(adict, "/S", "/Thread");
535 		coerce_dest = false; /* Dest is not a real destination */
536 	    }
537 	    else
538 		pdfmark_put_c_pair(adict, "/S", Action + 1);
539 	    if (Dest.data) {
540 		if (coerce_dest)
541 		    pdfmark_coerce_dest(&Dest, dest);
542 		pdfmark_put_c_pair(adict, "/D", &Dest);
543 		Dest.data = 0;	/* so we don't write it again */
544 	    }
545 	    if (File) {
546 		pdfmark_put_c_pair(adict, "/F", File + 1);
547 		File = 0;	/* so we don't write it again */
548 	    }
549 	    cos_dict_put(pcd, (const byte *)"/A", 2,
550 			 COS_OBJECT_VALUE(&avalue, adict));
551 	} else if (asize >= 4 && !memcmp(astr, "<<", 2)) {
552 	    /* Replace occurrences of /Dest, /File, and /Subtype. */
553 	    const byte *scan = astr + 2;
554 	    const byte *end = astr + asize;
555 	    gs_param_string key, value;
556 	    cos_dict_t *adict = cos_dict_alloc(pdev, "action dict");
557 	    cos_value_t avalue;
558 	    int code;
559 
560 	    if (adict == 0)
561 		return_error(gs_error_VMerror);
562 	    while ((code = pdf_scan_token(&scan, end, &key.data)) > 0) {
563 		key.size = scan - key.data;
564 		if (key.data[0] != '/' ||
565 		    (code = pdf_scan_token_composite(&scan, end, &value.data)) != 1)
566 		    break;
567 		value.size = scan - value.data;
568 		if (pdf_key_eq(&key, "/Dest") || pdf_key_eq(&key, "/D")) {
569 		    param_string_from_string(key, "/D");
570 		    if (value.data[0] == '(') {
571 			/****** HANDLE ESCAPES ******/
572 			pdfmark_coerce_dest(&value, dest);
573 		    }
574 		} else if (pdf_key_eq(&key, "/File"))
575 		    param_string_from_string(key, "/F");
576 		else if (pdf_key_eq(&key, "/Subtype"))
577 		    param_string_from_string(key, "/S");
578 		cos_dict_put_string(adict, key.data, key.size,
579 				    value.data, value.size);
580 	    }
581 	    if (code <= 0 || !pdf_key_eq(&key, ">>"))
582 		return_error(gs_error_rangecheck);
583 	    cos_dict_put(pcd, (const byte *)"/A", 2,
584 			 COS_OBJECT_VALUE(&avalue, adict));
585 	} else if (pdf_key_eq(Action + 1, "/GoTo"))
586 	    pdfmark_put_pair(pcd, Action);
587 	else if (Action[1].size < 30) {
588 	    /* Hack: we could substitute names in pdfmark_process,
589 	       now should recognize whether it was done.
590 	       Not a perfect method though.
591 	       Go with it for a while. */
592 	    char buf[30];
593 	    int d0, d1;
594 
595 	    memcpy(buf, Action[1].data, Action[1].size);
596 	    buf[Action[1].size] = 0;
597 	    if (sscanf(buf, "%d %d R", &d0, &d1) == 2)
598 		pdfmark_put_pair(pcd, Action);
599 	}
600     }
601     /*
602      * If we have /Dest or /File without the right kind of action,
603      * simply write it at the top level.  This doesn't seem right,
604      * but I'm not sure what else to do.
605      */
606     if (Dest.data) {
607 	if (coerce_dest)
608 	    pdfmark_coerce_dest(&Dest, dest);
609 	pdfmark_put_c_pair(pcd, "/Dest", &Dest);
610     } else if (for_outline && !Action) {
611 	/* Make an implicit destination. */
612 	char dstr[1 + (sizeof(long) * 8 / 3 + 1) + 25 + 1];
613 	long page_id = pdf_page_id(pdev, pdev->next_page + 1);
614 
615 	sprintf(dstr, "[%ld 0 R /XYZ null null null]", page_id);
616 	cos_dict_put_c_key_string(pcd, "/Dest", (const unsigned char*) dstr,
617 				  strlen(dstr));
618     }
619     if (File)
620 	pdfmark_put_pair(pcd, File);
621     if (Subtype.data)
622 	pdfmark_put_c_pair(pcd, "/Subtype", &Subtype);
623     return 0;
624 }
625 
626 /* Copy an annotation dictionary. */
627 private int
pdfmark_annot(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname,const char * subtype)628 pdfmark_annot(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
629 	      const gs_matrix * pctm, const gs_param_string *objname,
630 	      const char *subtype)
631 {
632     ao_params_t params;
633     cos_dict_t *pcd;
634     int page_index = pdev->next_page;
635     cos_array_t *annots;
636     cos_value_t value;
637     int code;
638 
639     params.pdev = pdev;
640     params.subtype = subtype;
641     params.src_pg = -1;
642     code = pdf_make_named_dict(pdev, objname, &pcd, true);
643     if (code < 0)
644 	return code;
645     code = cos_dict_put_c_strings(pcd, "/Type", "/Annot");
646     if (code < 0)
647 	return code;
648     code = pdfmark_put_ao_pairs(pdev, pcd, pairs, count, pctm, &params, false);
649     if (code < 0)
650 	return code;
651     if (params.src_pg >= 0)
652 	page_index = params.src_pg;
653     if (pdf_page_id(pdev, page_index + 1) <= 0)
654 	return_error(gs_error_rangecheck);
655     annots = pdev->pages[page_index].Annots;
656     if (annots == 0) {
657 	annots = cos_array_alloc(pdev, "pdfmark_annot");
658 	if (annots == 0)
659 	    return_error(gs_error_VMerror);
660 	pdev->pages[page_index].Annots = annots;
661     }
662     if (!objname) {
663 	/* Write the annotation now. */
664 	COS_WRITE_OBJECT(pcd, pdev);
665 	COS_RELEASE(pcd, "pdfmark_annot");
666     }
667     return cos_array_add(annots,
668 			 cos_object_value(&value, COS_OBJECT(pcd)));
669 }
670 
671 /* ANN pdfmark */
672 private int
pdfmark_ANN(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)673 pdfmark_ANN(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
674 	    const gs_matrix * pctm, const gs_param_string * objname)
675 {
676     return pdfmark_annot(pdev, pairs, count, pctm, objname, "/Text");
677 }
678 
679 /* LNK pdfmark (obsolescent) */
680 private int
pdfmark_LNK(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)681 pdfmark_LNK(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
682 	    const gs_matrix * pctm, const gs_param_string * objname)
683 {
684     return pdfmark_annot(pdev, pairs, count, pctm, objname, "/Link");
685 }
686 
687 /* Write and release one node of the outline tree. */
688 private int
pdfmark_write_outline(gx_device_pdf * pdev,pdf_outline_node_t * pnode,long next_id)689 pdfmark_write_outline(gx_device_pdf * pdev, pdf_outline_node_t * pnode,
690 		      long next_id)
691 {
692     stream *s;
693 
694     pdf_open_separate(pdev, pnode->id);
695     pnode->action->id = pnode->id;
696     s = pdev->strm;
697     stream_puts(s, "<< ");
698     cos_dict_elements_write(pnode->action, pdev);
699     if (pnode->count)
700 	pprintd1(s, "/Count %d ", pnode->count);
701     pprintld1(s, "/Parent %ld 0 R\n", pnode->parent_id);
702     if (pnode->prev_id)
703 	pprintld1(s, "/Prev %ld 0 R\n", pnode->prev_id);
704     if (next_id)
705 	pprintld1(s, "/Next %ld 0 R\n", next_id);
706     if (pnode->first_id)
707 	pprintld2(s, "/First %ld 0 R /Last %ld 0 R\n",
708 		  pnode->first_id, pnode->last_id);
709     stream_puts(s, ">>\n");
710     pdf_end_separate(pdev);
711     COS_FREE(pnode->action, "pdfmark_write_outline");
712     pnode->action = 0;
713     return 0;
714 }
715 
716 /* Adjust the parent's count when writing an outline node. */
717 private void
pdfmark_adjust_parent_count(pdf_outline_level_t * plevel)718 pdfmark_adjust_parent_count(pdf_outline_level_t * plevel)
719 {
720     pdf_outline_level_t *parent = plevel - 1;
721     int count = plevel->last.count;
722 
723     if (count > 0) {
724 	if (parent->last.count < 0)
725 	    parent->last.count -= count;
726 	else
727 	    parent->last.count += count;
728     }
729 }
730 
731 /*
732  * Close the current level of the outline tree.  Note that if we are at
733  * the end of the document, some of the levels may be incomplete if the
734  * Count values were incorrect.
735  */
736 int
pdfmark_close_outline(gx_device_pdf * pdev)737 pdfmark_close_outline(gx_device_pdf * pdev)
738 {
739     int depth = pdev->outline_depth;
740     pdf_outline_level_t *plevel = &pdev->outline_levels[depth];
741     int code;
742 
743     if (plevel->last.id) {	/* check for incomplete tree */
744 	code = pdfmark_write_outline(pdev, &plevel->last, 0);
745 	if (code < 0)
746 	    return code;
747     }
748     if (depth > 0) {
749 	plevel[-1].last.last_id = plevel->last.id;
750 	pdfmark_adjust_parent_count(plevel);
751 	--plevel;
752 	if (plevel->last.count < 0)
753 	    pdev->closed_outline_depth--;
754 	pdev->outline_depth--;
755     }
756     return 0;
757 }
758 
759 /* OUT pdfmark */
760 private int
pdfmark_OUT(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * no_objname)761 pdfmark_OUT(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
762 	    const gs_matrix * pctm, const gs_param_string * no_objname)
763 {
764     int depth = pdev->outline_depth;
765     pdf_outline_level_t *plevel = &pdev->outline_levels[depth];
766     int sub_count = 0;
767     uint i;
768     pdf_outline_node_t node;
769     ao_params_t ao;
770     int code;
771 
772     for (i = 0; i < count; i += 2) {
773 	const gs_param_string *pair = &pairs[i];
774 
775 	if (pdf_key_eq(pair, "/Count"))
776 	    pdfmark_scan_int(pair + 1, &sub_count);
777     }
778     if (sub_count != 0 && depth == MAX_OUTLINE_DEPTH - 1)
779 	return_error(gs_error_limitcheck);
780     node.action = cos_dict_alloc(pdev, "pdfmark_OUT");
781     if (node.action == 0)
782 	return_error(gs_error_VMerror);
783     ao.pdev = pdev;
784     ao.subtype = 0;
785     ao.src_pg = -1;
786     code = pdfmark_put_ao_pairs(pdev, node.action, pairs, count, pctm, &ao,
787 				true);
788     if (code < 0)
789 	return code;
790     if (pdev->outlines_id == 0)
791 	pdev->outlines_id = pdf_obj_ref(pdev);
792     node.id = pdf_obj_ref(pdev);
793     node.parent_id =
794 	(depth == 0 ? pdev->outlines_id : plevel[-1].last.id);
795     node.prev_id = plevel->last.id;
796     node.first_id = node.last_id = 0;
797     node.count = sub_count;
798     /* Add this node to the outline at the current level. */
799     if (plevel->first.id == 0) {	/* First node at this level. */
800 	if (depth > 0)
801 	    plevel[-1].last.first_id = node.id;
802 	node.prev_id = 0;
803 	plevel->first = node;
804 	plevel->first.action = 0; /* never used */
805     } else {			/* Write the previous node. */
806 	if (depth > 0)
807 	    pdfmark_adjust_parent_count(plevel);
808 	pdfmark_write_outline(pdev, &plevel->last, node.id);
809     }
810     plevel->last = node;
811     plevel->left--;
812     if (!pdev->closed_outline_depth)
813 	pdev->outlines_open++;
814     /* If this node has sub-nodes, descend one level. */
815     if (sub_count != 0) {
816 	pdev->outline_depth++;
817 	++plevel;
818 	plevel->left = (sub_count > 0 ? sub_count : -sub_count);
819 	plevel->first.id = 0;
820 	plevel->first.action = plevel->last.action = 0;	/* for GC */
821 	if (sub_count < 0)
822 	    pdev->closed_outline_depth++;
823     } else {
824 	while ((depth = pdev->outline_depth) > 0 &&
825 	       pdev->outline_levels[depth].left == 0
826 	    )
827 	    pdfmark_close_outline(pdev);
828     }
829     return 0;
830 }
831 
832 /* Write an article bead. */
833 private int
pdfmark_write_bead(gx_device_pdf * pdev,const pdf_bead_t * pbead)834 pdfmark_write_bead(gx_device_pdf * pdev, const pdf_bead_t * pbead)
835 {
836     stream *s;
837     char rstr[MAX_RECT_STRING];
838 
839     pdf_open_separate(pdev, pbead->id);
840     s = pdev->strm;
841     pprintld3(s, "<</T %ld 0 R/V %ld 0 R/N %ld 0 R",
842 	      pbead->article_id, pbead->prev_id, pbead->next_id);
843     if (pbead->page_id != 0)
844 	pprintld1(s, "/P %ld 0 R", pbead->page_id);
845     pdfmark_make_rect(rstr, &pbead->rect);
846     pprints1(s, "/R%s>>\n", rstr);
847     return pdf_end_separate(pdev);
848 }
849 
850 /* Finish writing an article, and release its data. */
851 int
pdfmark_write_article(gx_device_pdf * pdev,const pdf_article_t * part)852 pdfmark_write_article(gx_device_pdf * pdev, const pdf_article_t * part)
853 {
854     pdf_article_t art;
855     stream *s;
856 
857     art = *part;
858     if (art.last.id == 0) {
859 	/* Only one bead in the article. */
860 	art.first.prev_id = art.first.next_id = art.first.id;
861     } else {
862 	/* More than one bead in the article. */
863 	art.first.prev_id = art.last.id;
864 	art.last.next_id = art.first.id;
865 	pdfmark_write_bead(pdev, &art.last);
866     }
867     pdfmark_write_bead(pdev, &art.first);
868     pdf_open_separate(pdev, art.contents->id);
869     s = pdev->strm;
870     pprintld1(s, "<</F %ld 0 R/I<<", art.first.id);
871     cos_dict_elements_write(art.contents, pdev);
872     stream_puts(s, ">> >>\n");
873     return pdf_end_separate(pdev);
874 }
875 
876 /* ARTICLE pdfmark */
877 private int
pdfmark_ARTICLE(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * no_objname)878 pdfmark_ARTICLE(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
879 		const gs_matrix * pctm, const gs_param_string * no_objname)
880 {
881     gs_memory_t *mem = pdev->pdf_memory;
882     gs_param_string title;
883     gs_param_string rectstr;
884     gs_rect rect;
885     long bead_id;
886     pdf_article_t *part;
887     int code;
888 
889     if (!pdfmark_find_key("/Title", pairs, count, &title) ||
890 	!pdfmark_find_key("/Rect", pairs, count, &rectstr)
891 	)
892 	return_error(gs_error_rangecheck);
893     if ((code = pdfmark_scan_rect(&rect, &rectstr, pctm)) < 0)
894 	return code;
895     bead_id = pdf_obj_ref(pdev);
896 
897     /* Find the article with this title, or create one. */
898     for (part = pdev->articles; part != 0; part = part->next) {
899 	const cos_value_t *a_title =
900 	    cos_dict_find_c_key(part->contents, "/Title");
901 
902 	if (a_title != 0 && !COS_VALUE_IS_OBJECT(a_title) &&
903 	    !bytes_compare(a_title->contents.chars.data,
904 			   a_title->contents.chars.size,
905 			   title.data, title.size))
906 	    break;
907     }
908     if (part == 0) {		/* Create the article. */
909 	cos_dict_t *contents =
910 	    cos_dict_alloc(pdev, "pdfmark_ARTICLE(contents)");
911 
912 	if (contents == 0)
913 	    return_error(gs_error_VMerror);
914 	part = gs_alloc_struct(mem, pdf_article_t, &st_pdf_article,
915 			       "pdfmark_ARTICLE(article)");
916 	if (part == 0 || contents == 0) {
917 	    gs_free_object(mem, part, "pdfmark_ARTICLE(article)");
918 	    if (contents)
919 		COS_FREE(contents, "pdfmark_ARTICLE(contents)");
920 	    return_error(gs_error_VMerror);
921 	}
922 	contents->id = pdf_obj_ref(pdev);
923 	part->next = pdev->articles;
924 	pdev->articles = part;
925 	cos_dict_put_string(contents, (const byte *)"/Title", 6,
926 			    title.data, title.size);
927 	part->first.id = part->last.id = 0;
928 	part->contents = contents;
929     }
930     /*
931      * Add the bead to the article.  This is similar to what we do for
932      * outline nodes, except that articles have only a page number and
933      * not View/Dest.
934      */
935     if (part->last.id == 0) {
936 	part->first.next_id = bead_id;
937 	part->last.id = part->first.id;
938     } else {
939 	part->last.next_id = bead_id;
940 	pdfmark_write_bead(pdev, &part->last);
941     }
942     part->last.prev_id = part->last.id;
943     part->last.id = bead_id;
944     part->last.article_id = part->contents->id;
945     part->last.next_id = 0;
946     part->last.rect = rect;
947     {
948 	gs_param_string page_string;
949 	int page = 0;
950 	uint i;
951 
952 	pdfmark_find_key("/Page", pairs, count, &page_string);
953 	page = pdfmark_page_number(pdev, &page_string);
954 	part->last.page_id = pdf_page_id(pdev, page);
955 	for (i = 0; i < count; i += 2) {
956 	    if (pdf_key_eq(&pairs[i], "/Rect") || pdf_key_eq(&pairs[i], "/Page"))
957 		continue;
958 	    pdfmark_put_pair(part->contents, &pairs[i]);
959 	}
960     }
961     if (part->first.id == 0) {	/* This is the first bead of the article. */
962 	part->first = part->last;
963 	part->last.id = 0;
964     }
965     return 0;
966 }
967 
968 /* DEST pdfmark */
969 private int
pdfmark_DEST(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)970 pdfmark_DEST(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
971 	     const gs_matrix * pctm, const gs_param_string * objname)
972 {
973     int present;
974     char dest[MAX_DEST_STRING];
975     gs_param_string key;
976     cos_value_t value;
977 
978     if (!pdfmark_find_key("/Dest", pairs, count, &key) ||
979 	(present =
980 	 pdfmark_make_dest(dest, pdev, "/Page", "/View", pairs, count)) < 0
981 	)
982 	return_error(gs_error_rangecheck);
983     cos_string_value(&value, (byte *)dest, strlen(dest));
984     if (!pdev->Dests) {
985 	pdev->Dests = cos_dict_alloc(pdev, "pdfmark_DEST(Dests)");
986 	if (pdev->Dests == 0)
987 	    return_error(gs_error_VMerror);
988 	pdev->Dests->id = pdf_obj_ref(pdev);
989     }
990     if (objname || count > (present + 1) * 2) {
991 	/*
992 	 * Create the destination as a dictionary with a D key, since
993 	 * it has (or, if named, may have) additional key/value pairs.
994 	 */
995 	cos_dict_t *ddict;
996 	int i, code;
997 
998 	code = pdf_make_named_dict(pdev, objname, &ddict, false);
999 	if (code < 0)
1000 	    return code;
1001 	code = cos_dict_put_c_key_string(ddict, "/D", (byte *)dest,
1002 					 strlen(dest));
1003 	for (i = 0; code >= 0 && i < count; i += 2)
1004 	    if (!pdf_key_eq(&pairs[i], "/Dest") &&
1005 		!pdf_key_eq(&pairs[i], "/Page") &&
1006 		!pdf_key_eq(&pairs[i], "/View")
1007 		)
1008 		code = pdfmark_put_pair(ddict, &pairs[i]);
1009 	if (code < 0)
1010 	    return code;
1011 	COS_OBJECT_VALUE(&value, ddict);
1012     }
1013     return cos_dict_put(pdev->Dests, key.data, key.size, &value);
1014 }
1015 
1016 /* Check that pass-through PostScript code is a string. */
1017 private bool
ps_source_ok(const gs_param_string * psource)1018 ps_source_ok(const gs_param_string * psource)
1019 {
1020     if (psource->size >= 2 && psource->data[0] == '(' &&
1021 	psource->data[psource->size - 1] == ')'
1022 	)
1023 	return true;
1024     else {
1025 	int i;
1026 	lprintf("bad PS passthrough: ");
1027 	for (i=0; i<psource->size; i++)
1028 	    errprintf("%c", psource->data[i]);
1029 	errprintf("\n");
1030 	return false;
1031     }
1032 }
1033 
1034 /* Write the contents of pass-through PostScript code. */
1035 /* Return the size written on the file. */
1036 private uint
pdfmark_write_ps(stream * s,const gs_param_string * psource)1037 pdfmark_write_ps(stream *s, const gs_param_string * psource)
1038 {
1039     /****** REMOVE ESCAPES WITH PSSDecode, SEE gdevpdfr p. 2 ******/
1040     uint size = psource->size - 2;
1041 
1042     stream_write(s, psource->data + 1, size);
1043     stream_putc(s, '\n');
1044     return size + 1;
1045 }
1046 
1047 /* Start a XObject. */
1048 private int
start_XObject(gx_device_pdf * pdev,bool compress,cos_stream_t ** ppcs)1049 start_XObject(gx_device_pdf * pdev, bool compress, cos_stream_t **ppcs)
1050 {   pdf_resource_t *pres;
1051     cos_stream_t *pcs;
1052     int code;
1053 
1054     code = pdf_open_page(pdev, PDF_IN_STREAM);
1055     if (code < 0)
1056 	return code;
1057     code = pdf_enter_substream(pdev, resourceXObject, gs_no_id, &pres, false,
1058 		pdev->CompressFonts /* Have no better switch*/);
1059     if (code < 0)
1060 	return code;
1061     pdev->accumulating_a_global_object = true;
1062     pcs = (cos_stream_t *)pres->object;
1063     pdev->substream_Resources = cos_dict_alloc(pdev, "start_XObject");
1064     if (!pdev->substream_Resources)
1065 	return_error(gs_error_VMerror);
1066     if (pdev->ForOPDFRead) {
1067 	code = cos_dict_put_c_key_bool((cos_dict_t *)pres->object, "/.Global", true);
1068 	if (code < 0)
1069 	    return code;
1070     }
1071     pres->named = true;
1072     pres->where_used = 0;	/* initially not used */
1073     pcs->pres = pres;
1074     *ppcs = pcs;
1075     return 0;
1076 }
1077 
1078 /* PS pdfmark */
1079 #define MAX_PS_INLINE 100
1080 private int
pdfmark_PS(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1081 pdfmark_PS(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1082 	   const gs_matrix * pctm, const gs_param_string * objname)
1083 {
1084     gs_param_string source;
1085     gs_param_string level1;
1086 
1087     if (!pdfmark_find_key("/DataSource", pairs, count, &source) ||
1088 	!ps_source_ok(&source) ||
1089 	(pdfmark_find_key("/Level1", pairs, count, &level1) &&
1090 	 !ps_source_ok(&level1))
1091 	)
1092 	return_error(gs_error_rangecheck);
1093     if (level1.data == 0 && source.size <= MAX_PS_INLINE && objname == 0) {
1094 	/* Insert the PostScript code in-line */
1095 	int code = pdf_open_contents(pdev, PDF_IN_STREAM);
1096 	stream *s;
1097 
1098 	if (code < 0)
1099 	    return code;
1100 	s = pdev->strm;
1101 	stream_write(s, source.data, source.size);
1102 	stream_puts(s, " PS\n");
1103     } else {
1104 	/* Put the PostScript code in a resource. */
1105 	cos_stream_t *pcs;
1106 	int code;
1107 	gs_id level1_id = gs_no_id;
1108 	pdf_resource_t *pres;
1109 	cos_value_t value;
1110 
1111 	if (level1.data != 0) {
1112 	    pdf_resource_t *pres;
1113 
1114 	    code = pdf_enter_substream(pdev,
1115 			resourceXObject,
1116 			gs_no_id, &pres, true,
1117 			pdev->CompressFonts /* Have no better switch*/);
1118 	    if (code < 0)
1119 		return code;
1120 	    pcs = (cos_stream_t *)pres->object;
1121 	    if (pdev->ForOPDFRead && objname != 0) {
1122 		code = cos_dict_put_c_key_bool((cos_dict_t *)pres->object, "/.Global", true);
1123 		if (code < 0)
1124 		    return code;
1125 	    }
1126 	    pres->named = (objname != 0);
1127 	    pres->where_used = 0;
1128 	    pcs->pres = pres;
1129 	    DISCARD(pdfmark_write_ps(pdev->strm, &level1));
1130 	    code = pdf_exit_substream(pdev);
1131 	    if (code < 0)
1132 		return code;
1133 	    code = cos_write_object(pres->object, pdev);
1134 	    if (code < 0)
1135 		return code;
1136 	    level1_id = pres->object->id;
1137 	}
1138 	code = start_XObject(pdev, pdev->params.CompressPages, &pcs);
1139 	if (code < 0)
1140 	    return code;
1141 	pres = pdev->accumulating_substream_resource;
1142 	code = cos_stream_put_c_strings(pcs, "/Type", "/XObject");
1143 	if (code < 0)
1144 	    return code;
1145 	code = cos_stream_put_c_strings(pcs, "/Subtype", "/PS");
1146 	if (code < 0)
1147 	    return code;
1148 	if (level1_id != gs_no_id) {
1149 	    char r[MAX_DEST_STRING];
1150 
1151 	    sprintf(r, "%ld 0 R", level1_id);
1152 	    code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/Level1",
1153 					     (byte *)r, strlen(r));
1154 	    if (code < 0)
1155 		return code;
1156 	}
1157 	DISCARD(pdfmark_write_ps(pdev->strm, &source));
1158 	code = pdf_exit_substream(pdev);
1159 	if (code < 0)
1160 	    return code;
1161 	code = pdf_substitute_resource(pdev, &pres, resourceXObject, NULL, false);
1162 	if (code < 0)
1163 	    return code;
1164 	if (objname != 0) {
1165 	    code = cos_dict_put(pdev->local_named_objects, objname->data,
1166 				objname->size, cos_object_value(&value, (cos_object_t *)pcs));
1167 	    if (code < 0)
1168 		return code;
1169 	}
1170 	code = pdf_open_contents(pdev, PDF_IN_STREAM);
1171 	if (code < 0)
1172 	    return code;
1173         pcs->pres->where_used |= pdev->used_mask;
1174 	pprintld1(pdev->strm, "/R%ld Do\n", pcs->id);
1175     }
1176     return 0;
1177 }
1178 
1179 /* Common code for pdfmarks that do PUT into a specific object. */
1180 private int
pdfmark_put_pairs(cos_dict_t * pcd,gs_param_string * pairs,uint count)1181 pdfmark_put_pairs(cos_dict_t *pcd, gs_param_string * pairs, uint count)
1182 {
1183     int code = 0, i;
1184 
1185     if (count & 1)
1186 	return_error(gs_error_rangecheck);
1187     for (i = 0; code >= 0 && i < count; i += 2)
1188 	code = pdfmark_put_pair(pcd, pairs + i);
1189     return code;
1190 }
1191 
1192 /* PAGES pdfmark */
1193 private int
pdfmark_PAGES(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * no_objname)1194 pdfmark_PAGES(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1195 	      const gs_matrix * pctm, const gs_param_string * no_objname)
1196 {
1197     return pdfmark_put_pairs(pdev->Pages, pairs, count);
1198 }
1199 
1200 /* PAGE pdfmark */
1201 private int
pdfmark_PAGE(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * no_objname)1202 pdfmark_PAGE(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1203 	     const gs_matrix * pctm, const gs_param_string * no_objname)
1204 {
1205     return pdfmark_put_pairs(pdf_current_page_dict(pdev), pairs, count);
1206 }
1207 
1208 /* Add a page label for the current page. The last label on a page
1209  * overrides all previous labels for this page. Unlabeled pages will get
1210  * empty page labels. label == NULL flushes the last label */
1211 private int
pdfmark_add_pagelabel(gx_device_pdf * pdev,const gs_param_string * label)1212 pdfmark_add_pagelabel(gx_device_pdf * pdev, const gs_param_string *label)
1213 {
1214     cos_value_t value;
1215     cos_dict_t *dict = 0;
1216     int code = 0;
1217 
1218     /* create label dict (and page label array if not present yet) */
1219     if (label != 0) {
1220         if (!pdev->PageLabels) {
1221             pdev->PageLabels = cos_array_alloc(pdev,
1222                     "pdfmark_add_pagelabel(PageLabels)");
1223             if (pdev->PageLabels == 0)
1224                 return_error(gs_error_VMerror);
1225             pdev->PageLabels->id = pdf_obj_ref(pdev);
1226 
1227             /* empty label for unlabled pages before first labled page */
1228             pdev->PageLabels_current_page = 0;
1229             pdev->PageLabels_current_label = cos_dict_alloc(pdev,
1230                                            "pdfmark_add_pagelabel(first)");
1231             if (pdev->PageLabels_current_label == 0)
1232                 return_error(gs_error_VMerror);
1233         }
1234 
1235         dict = cos_dict_alloc(pdev, "pdfmark_add_pagelabel(dict)");
1236         if (dict == 0)
1237             return_error(gs_error_VMerror);
1238 
1239         code = cos_dict_put_c_key(dict, "/P", cos_string_value(&value,
1240             label->data, label->size));
1241         if (code < 0) {
1242             COS_FREE(dict, "pdfmark_add_pagelabel(dict)");
1243             return code;
1244         }
1245     }
1246 
1247     /* flush current label */
1248     if (label == 0 || pdev->next_page != pdev->PageLabels_current_page) {
1249         /* handle current label */
1250         if (pdev->PageLabels_current_label) {
1251             if (code >= 0) {
1252                 code = cos_array_add_int(pdev->PageLabels,
1253                         pdev->PageLabels_current_page);
1254                 if (code >= 0)
1255                     code = cos_array_add(pdev->PageLabels,
1256                             COS_OBJECT_VALUE(&value,
1257                                 pdev->PageLabels_current_label));
1258             }
1259             pdev->PageLabels_current_label = 0;
1260         }
1261 
1262         /* handle unlabled pages between current labeled page and
1263          * next labeled page */
1264         if (pdev->PageLabels) {
1265             if (pdev->next_page - pdev->PageLabels_current_page > 1) {
1266                 cos_dict_t *tmp = cos_dict_alloc(pdev,
1267                         "pdfmark_add_pagelabel(tmp)");
1268                 if (tmp == 0)
1269                     return_error(gs_error_VMerror);
1270 
1271                 code = cos_array_add_int(pdev->PageLabels,
1272                         pdev->PageLabels_current_page + 1);
1273                 if (code >= 0)
1274                     code = cos_array_add(pdev->PageLabels,
1275                             COS_OBJECT_VALUE(&value, tmp));
1276             }
1277         }
1278     }
1279 
1280     /* new current label */
1281     if (pdev->PageLabels_current_label)
1282         COS_FREE(pdev->PageLabels_current_label,
1283                 "pdfmark_add_pagelabel(current_label)");
1284     pdev->PageLabels_current_label = dict;
1285     pdev->PageLabels_current_page = pdev->next_page;
1286 
1287     return code;
1288 }
1289 
1290 /* Close the pagelabel numtree.*/
1291 int
pdfmark_end_pagelabels(gx_device_pdf * pdev)1292 pdfmark_end_pagelabels(gx_device_pdf * pdev)
1293 {
1294     return pdfmark_add_pagelabel(pdev, 0);
1295 }
1296 
1297 /* [ /Label string /PlateColor string pdfmark */
1298 /* FIXME: /PlateColor is ignored */
1299 private int
pdfmark_PAGELABEL(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * no_objname)1300 pdfmark_PAGELABEL(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1301 	     const gs_matrix * pctm, const gs_param_string * no_objname)
1302 {
1303     gs_param_string key;
1304 
1305     if (pdev->CompatibilityLevel >= 1.3) {
1306         if (pdfmark_find_key("/Label", pairs, count, &key)) {
1307             return pdfmark_add_pagelabel(pdev, &key);
1308         }
1309     }
1310     return 0;
1311 }
1312 
1313 /* DOCINFO pdfmark */
1314 private int
pdfmark_DOCINFO(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * no_objname)1315 pdfmark_DOCINFO(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1316 		const gs_matrix * pctm, const gs_param_string * no_objname)
1317 {
1318     /*
1319      * We could use pdfmark_put_pairs(pdev->Info, pairs, count), except
1320      * that we want to replace "Distiller" with our own name as the
1321      * Producer.
1322      */
1323     cos_dict_t *const pcd = pdev->Info;
1324     int code = 0, i;
1325     gs_memory_t *mem = pdev->pdf_memory;
1326 
1327     if (count & 1)
1328 	return_error(gs_error_rangecheck);
1329     for (i = 0; code >= 0 && i < count; i += 2) {
1330 	const gs_param_string *pair = pairs + i;
1331 	gs_param_string alt_pair[2];
1332 	const byte *vdata;	/* alt_pair[1].data */
1333 	uint vsize;		/* alt_pair[1].size */
1334 	byte *str = 0;
1335 
1336 	vsize = 0x0badf00d; /* Quiet compiler. */
1337 
1338 	if (pdf_key_eq(pairs + i, "/Producer")) {
1339 	    /*
1340 	     * If the string "Distiller" appears anywhere in the Producer,
1341 	     * replace the Producer (or the part after a " + ") with our
1342 	     * own name.
1343 	     */
1344 	    string_match_params params;
1345 
1346 	    memcpy(alt_pair, pairs + i, sizeof(alt_pair));
1347 	    vdata = alt_pair[1].data;
1348 	    vsize = alt_pair[1].size;
1349 	    params = string_match_params_default;
1350 	    params.ignore_case = true;
1351 	    if (string_match(vdata, vsize, (const byte *)"*Distiller*",
1352 			     11, &params) ||
1353 		string_match(vdata, vsize,
1354 	     (const byte *)"*\000D\000i\000s\000t\000i\000l\000l\000e\000r*",
1355 			     20, &params)
1356 		) {
1357 		uint j;
1358 		char buf[PDF_MAX_PRODUCER];
1359 		int len;
1360 
1361 		for (j = vsize; j > 0 && vdata[--j] != '+'; )
1362 		    DO_NOTHING;
1363 		if (vsize - j > 2 && vdata[j] == '+') {
1364 		    ++j;
1365 		    while (j < vsize && vdata[j] == ' ')
1366 			++j;
1367 		}
1368 		/*
1369 		 * Replace vdata[j .. vsize) with our name.  Note that both
1370 		 * vdata/vstr and the default producer string are enclosed
1371 		 * in ().
1372 		 */
1373 		pdf_store_default_Producer(buf);
1374 		len = strlen(buf) - 1;
1375 		str = gs_alloc_string(mem, j + len, "Producer");
1376 		if (str == 0)
1377 		    return_error(gs_error_VMerror);
1378 		memcpy(str, vdata, j);
1379 		memcpy(str + j, buf + 1, len);
1380 		alt_pair[1].data = vdata = str;
1381 		alt_pair[1].size = vsize = j + len;
1382 		pair = alt_pair;
1383 	    }
1384 	}
1385 	code = pdfmark_put_pair(pcd, pair);
1386 	if (str)
1387 	    gs_free_string(mem, str, vsize, "Producer");
1388     }
1389     return code;
1390 }
1391 
1392 /* DOCVIEW pdfmark */
1393 private int
pdfmark_DOCVIEW(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * no_objname)1394 pdfmark_DOCVIEW(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1395 		const gs_matrix * pctm, const gs_param_string * no_objname)
1396 {
1397     char dest[MAX_DEST_STRING];
1398     int code = 0;
1399 
1400     if (count & 1)
1401 	return_error(gs_error_rangecheck);
1402     if (pdfmark_make_dest(dest, pdev, "/Page", "/View", pairs, count)) {
1403 	int i;
1404 
1405 	code = cos_dict_put_c_key_string(pdev->Catalog, "/OpenAction",
1406 					 (byte *)dest, strlen(dest));
1407 	for (i = 0; code >= 0 && i < count; i += 2)
1408 	    if (!(pdf_key_eq(&pairs[i], "/Page") ||
1409 		  pdf_key_eq(&pairs[i], "/View"))
1410 		)
1411 		code = pdfmark_put_pair(pdev->Catalog, pairs + i);
1412 	return code;
1413     } else
1414 	return pdfmark_put_pairs(pdev->Catalog, pairs, count);
1415 }
1416 
1417 /* ---------------- Named object pdfmarks ---------------- */
1418 
1419 /* [ /BBox [llx lly urx ury] /_objdef {obj} /BP pdfmark */
1420 private int
pdfmark_BP(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1421 pdfmark_BP(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1422 	   const gs_matrix * pctm, const gs_param_string * objname)
1423 {
1424     gs_rect bbox;
1425     cos_stream_t *pcs;
1426     int code;
1427     gs_matrix ictm;
1428     byte bbox_str[6 + 6 * 15], matrix_str[6 + 6 * 15];
1429     int bbox_str_len, matrix_str_len;
1430     stream s;
1431 
1432     if (objname == 0 || count != 2 || !pdf_key_eq(&pairs[0], "/BBox"))
1433 	return_error(gs_error_rangecheck);
1434     code = gs_matrix_invert(pctm, &ictm);
1435     if (code < 0)
1436 	return code;
1437     if (sscanf((const char *)pairs[1].data, "[%lg %lg %lg %lg]",
1438 	       &bbox.p.x, &bbox.p.y, &bbox.q.x, &bbox.q.y) != 4)
1439 	return_error(gs_error_rangecheck);
1440     if ((pdev->used_mask << 1) == 0)
1441 	return_error(gs_error_limitcheck);
1442     code = start_XObject(pdev, pdev->params.CompressPages, &pcs);
1443     if (code < 0)
1444 	return code;
1445     {	byte *s = gs_alloc_string(pdev->memory, objname->size, "pdfmark_PS");
1446 
1447 	if (s == NULL)
1448 	    return_error(gs_error_VMerror);
1449 	memcpy(s, objname->data, objname->size);
1450 	pdev->objname.data = s;
1451 	pdev->objname.size = objname->size;
1452     }
1453     pcs->is_graphics = true;
1454     gs_bbox_transform(&bbox, pctm, &bbox);
1455     s_init(&s, NULL);
1456     swrite_string(&s, bbox_str, sizeof(bbox_str));
1457     pprintg4(&s, "[%g %g %g %g]",
1458 	    bbox.p.x, bbox.p.y, bbox.q.x, bbox.q.y);
1459     bbox_str_len = stell(&s);
1460     swrite_string(&s, matrix_str, sizeof(bbox_str));
1461     pprintg6(&s, "[%g %g %g %g %g %g]",
1462 	    ictm.xx, ictm.xy, ictm.yx, ictm.yy, ictm.tx, ictm.ty);
1463     matrix_str_len = stell(&s);
1464     if ((code = cos_stream_put_c_strings(pcs, "/Type", "/XObject")) < 0 ||
1465 	(code = cos_stream_put_c_strings(pcs, "/Subtype", "/Form")) < 0 ||
1466 	(code = cos_stream_put_c_strings(pcs, "/FormType", "1")) < 0 ||
1467 	(code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/BBox",
1468 					  bbox_str, bbox_str_len)) < 0 ||
1469 	(code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/Matrix",
1470 				      matrix_str, matrix_str_len)) < 0 ||
1471  	(code = cos_dict_put_c_key_object(cos_stream_dict(pcs), "/Resources",
1472  					  COS_OBJECT(pdev->substream_Resources))) < 0
1473 	)
1474 	return code;
1475     return 0;
1476 }
1477 
1478 /* [ /EP pdfmark */
1479 private int
pdfmark_EP(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * no_objname)1480 pdfmark_EP(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1481 	   const gs_matrix * pctm, const gs_param_string * no_objname)
1482 {
1483     int code;
1484     cos_value_t value;
1485     pdf_resource_t *pres = pdev->accumulating_substream_resource;
1486     gs_const_string objname = pdev->objname;
1487 
1488     code = pdf_add_procsets(pdev->substream_Resources, pdev->procsets);
1489     if (code < 0)
1490 	return code;
1491     code = pdf_exit_substream(pdev);
1492     if (code < 0)
1493 	return code;
1494     code = pdf_substitute_resource(pdev, &pres, resourceXObject, NULL, true);
1495     if (code < 0)
1496 	return code;
1497     if (objname.size) {
1498 	code = cos_dict_put(pdev->local_named_objects, objname.data,
1499 				objname.size, cos_object_value(&value, (cos_object_t *)pres->object));
1500 	if (code < 0)
1501 	    return code;
1502     }
1503     gs_free_const_string(pdev->memory, objname.data, objname.size, "pdfmark_EP");
1504     if (code < 0)
1505 	return code;
1506     return 0;
1507 }
1508 
1509 /* [ {obj} /SP pdfmark */
1510 private int
pdfmark_SP(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * no_objname)1511 pdfmark_SP(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1512 	   const gs_matrix * pctm, const gs_param_string * no_objname)
1513 {
1514     cos_object_t *pco;		/* stream */
1515     int code;
1516 
1517     if (count != 1)
1518 	return_error(gs_error_rangecheck);
1519     if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0)
1520 	return code;
1521     if (pco->is_open || !pco->is_graphics)
1522 	return_error(gs_error_rangecheck);
1523     code = pdf_open_contents(pdev, PDF_IN_STREAM);
1524     if (code < 0)
1525 	return code;
1526     pdf_put_matrix(pdev, "q ", pctm, "cm");
1527     pprintld1(pdev->strm, "/R%ld Do Q\n", pco->id);
1528     pco->pres->where_used |= pdev->used_mask;
1529     return 0;
1530 }
1531 
1532 /* [ /_objdef {array} /type /array /OBJ pdfmark */
1533 /* [ /_objdef {dict} /type /dict /OBJ pdfmark */
1534 /* [ /_objdef {stream} /type /stream /OBJ pdfmark */
1535 private int
pdfmark_OBJ(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1536 pdfmark_OBJ(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1537 	    const gs_matrix * pctm, const gs_param_string * objname)
1538 {
1539     cos_type_t cotype;
1540     cos_object_t *pco;
1541     bool stream = false;
1542     int code;
1543 
1544     if (objname == 0 || count != 2 || !pdf_key_eq(&pairs[0], "/type"))
1545 	return_error(gs_error_rangecheck);
1546     if (pdf_key_eq(&pairs[1], "/array"))
1547 	cotype = cos_type_array;
1548     else if (pdf_key_eq(&pairs[1], "/dict"))
1549 	cotype = cos_type_dict;
1550     else if ((stream = pdf_key_eq(&pairs[1], "/stream")))
1551 	cotype = cos_type_stream;
1552     else
1553 	return_error(gs_error_rangecheck);
1554     if ((code = pdf_make_named(pdev, objname, cotype, &pco, true)) < 0) {
1555 	/*
1556 	 * For Distiller compatibility, allows multiple /OBJ pdfmarks with
1557 	 * the same name and type, even though the pdfmark specification
1558 	 * doesn't say anything about this being legal.
1559 	 */
1560 	if (code == gs_error_rangecheck &&
1561 	    pdf_refer_named(pdev, objname, &pco) >= 0 &&
1562 	    cos_type(pco) == cotype
1563 	    )
1564 	    return 0;		/* already exists, but OK */
1565 	return code;
1566     }
1567     if (stream)
1568 	return setup_pdfmark_stream_compression((gx_device_psdf *)pdev,
1569 						     (cos_stream_t *)pco);
1570     return 0;
1571 }
1572 
1573 /* [ {array} index value /PUT pdfmark */
1574 /* Dictionaries are converted to .PUTDICT */
1575 /* Streams are converted to .PUTSTREAM */
1576 private int
pdfmark_PUT(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * no_objname)1577 pdfmark_PUT(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1578 	    const gs_matrix * pctm, const gs_param_string * no_objname)
1579 {
1580     cos_object_t *pco;
1581     cos_value_t value;
1582     int code, index;
1583 
1584     if (count != 3)
1585 	return_error(gs_error_rangecheck);
1586     if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0)
1587 	return code;
1588     if ((code = pdfmark_scan_int(&pairs[1], &index)) < 0)
1589 	return code;
1590     if (index < 0)
1591 	return_error(gs_error_rangecheck);
1592     return cos_array_put((cos_array_t *)pco, index,
1593 		cos_string_value(&value, pairs[2].data, pairs[2].size));
1594 }
1595 
1596 /* [ {dict} key value ... /.PUTDICT pdfmark */
1597 /* [ {stream} key value ... /.PUTDICT pdfmark */
1598 /*
1599  * Adobe's pdfmark documentation doesn't allow PUTDICT with a stream,
1600  * but it's reasonable and unambiguous, and Acrobat Distiller accepts it,
1601  * so we do too.
1602  */
1603 private int
pdfmark_PUTDICT(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * no_objname)1604 pdfmark_PUTDICT(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1605 		const gs_matrix * pctm, const gs_param_string * no_objname)
1606 {
1607     cos_object_t *pco;
1608     int code;
1609 
1610     if ((code = pdf_refer_named(pdev, &pairs[0], &pco)) < 0)
1611 	return code;
1612     if (cos_type(pco) != cos_type_dict && cos_type(pco) != cos_type_stream)
1613 	return_error(gs_error_typecheck);
1614     return pdfmark_put_pairs((cos_dict_t *)pco, pairs + 1, count - 1);
1615 }
1616 
1617 /* [ {stream} string ... /.PUTSTREAM pdfmark */
1618 private int
pdfmark_PUTSTREAM(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * no_objname)1619 pdfmark_PUTSTREAM(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1620 		  const gs_matrix * pctm, const gs_param_string * no_objname)
1621 {
1622     cos_object_t *pco;
1623     int code, i;
1624     uint l;
1625 
1626     if (count < 2)
1627 	return_error(gs_error_rangecheck);
1628     if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0)
1629 	return code;
1630     if (!pco->is_open)
1631 	return_error(gs_error_rangecheck);
1632     for (i = 1; i < count; ++i)
1633 	if (sputs(pco->input_strm, pairs[i].data, pairs[i].size, &l) != 0)
1634 	    return_error(gs_error_ioerror);
1635     return code;
1636 }
1637 
1638 /* [ {array} value /APPEND pdfmark */
1639 private int
pdfmark_APPEND(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1640 pdfmark_APPEND(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1641 	       const gs_matrix * pctm, const gs_param_string * objname)
1642 {
1643     cos_object_t *pco;
1644     cos_value_t value;
1645     int code;
1646 
1647     if (count != 2)
1648 	return_error(gs_error_rangecheck);
1649     if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0)
1650 	return code;
1651     return cos_array_add((cos_array_t *)pco,
1652 		cos_string_value(&value, pairs[1].data, pairs[1].size));
1653 }
1654 
1655 /* [ {array} index value ... /.PUTINTERVAL pdfmark */
1656 private int
pdfmark_PUTINTERVAL(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * no_objname)1657 pdfmark_PUTINTERVAL(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1658 		 const gs_matrix * pctm, const gs_param_string * no_objname)
1659 {
1660     cos_object_t *pco;
1661     cos_value_t value;
1662     int code, index, i;
1663 
1664     if (count < 2)
1665 	return_error(gs_error_rangecheck);
1666     if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0)
1667 	return code;
1668     if ((code = pdfmark_scan_int(&pairs[1], &index)) < 0)
1669 	return code;
1670     if (index < 0)
1671 	return_error(gs_error_rangecheck);
1672     for (i = 2; code >= 0 && i < count; ++i)
1673 	code = cos_array_put((cos_array_t *)pco, index + i - 2,
1674 		cos_string_value(&value, pairs[i].data, pairs[i].size));
1675     return code;
1676 }
1677 
1678 /* [ {stream} /CLOSE pdfmark */
1679 private int
pdfmark_CLOSE(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * no_objname)1680 pdfmark_CLOSE(gx_device_pdf * pdev, gs_param_string * pairs, uint count,
1681 	      const gs_matrix * pctm, const gs_param_string * no_objname)
1682 {
1683     cos_object_t *pco;
1684     int code;
1685 
1686     if (count != 1)
1687 	return_error(gs_error_rangecheck);
1688     if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0)
1689 	return code;
1690     if (!pco->is_open)
1691 	return_error(gs_error_rangecheck);
1692     /* Currently we don't do anything special when closing a stream. */
1693     pco->is_open = false;
1694     return 0;
1695 }
1696 
1697 /* [ /NamespacePush pdfmark */
1698 private int
pdfmark_NamespacePush(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1699 pdfmark_NamespacePush(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1700 		      const gs_matrix *pctm, const gs_param_string *objname)
1701 {
1702     if (count != 0)
1703 	return_error(gs_error_rangecheck);
1704     return pdf_push_namespace(pdev);
1705 }
1706 
1707 /* [ /NamespacePop pdfmark */
1708 private int
pdfmark_NamespacePop(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1709 pdfmark_NamespacePop(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1710 		     const gs_matrix *pctm, const gs_param_string *objname)
1711 {
1712     if (count != 0)
1713 	return_error(gs_error_rangecheck);
1714     cos_dict_objects_write(pdev->local_named_objects, pdev);
1715     return pdf_pop_namespace(pdev);
1716 }
1717 
1718 /* [ /_objdef {image} /NI pdfmark */
1719 private int
pdfmark_NI(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1720 pdfmark_NI(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1721 	   const gs_matrix *pctm, const gs_param_string *objname)
1722 {
1723     cos_object_t *pco;
1724     int code;
1725 
1726     if (objname == 0 || count != 0)
1727 	return_error(gs_error_rangecheck);
1728     code = pdf_make_named(pdev, objname, cos_type_dict, &pco, true);
1729     if (code < 0)
1730 	return code;
1731     return cos_array_add_object(pdev->NI_stack, pco);
1732 }
1733 
1734 /* ---------------- Named content pdfmarks ---------------- */
1735 
1736 /* [ tag /MP pdfmark */
1737 private int
pdfmark_MP(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1738 pdfmark_MP(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1739 	   const gs_matrix *pctm, const gs_param_string *objname)
1740 {
1741     return 0;			/****** NOT IMPLEMENTED YET ******/
1742 }
1743 
1744 /* [ tag propdict /DP pdfmark */
1745 private int
pdfmark_DP(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1746 pdfmark_DP(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1747 	   const gs_matrix *pctm, const gs_param_string *objname)
1748 {
1749     return 0;			/****** NOT IMPLEMENTED YET ******/
1750 }
1751 
1752 /* [ tag /BMC pdfmark */
1753 private int
pdfmark_BMC(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1754 pdfmark_BMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1755 	    const gs_matrix *pctm, const gs_param_string *objname)
1756 {
1757     return 0;			/****** NOT IMPLEMENTED YET ******/
1758 }
1759 
1760 /* [ tag propdict /BDC pdfmark */
1761 private int
pdfmark_BDC(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1762 pdfmark_BDC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1763 	    const gs_matrix *pctm, const gs_param_string *objname)
1764 {
1765     return 0;			/****** NOT IMPLEMENTED YET ******/
1766 }
1767 
1768 /* [ /EMC pdfmark */
1769 private int
pdfmark_EMC(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1770 pdfmark_EMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1771 	    const gs_matrix *pctm, const gs_param_string *objname)
1772 {
1773     return 0;			/****** NOT IMPLEMENTED YET ******/
1774 }
1775 
1776 /* ---------------- Document structure pdfmarks ---------------- */
1777 
1778 /* [ newsubtype1 stdsubtype1 ... /StRoleMap pdfmark */
1779 private int
pdfmark_StRoleMap(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1780 pdfmark_StRoleMap(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1781 		  const gs_matrix *pctm, const gs_param_string *objname)
1782 {
1783     return 0;			/****** NOT IMPLEMENTED YET ******/
1784 }
1785 
1786 /* [ class1 {attrobj1} ... /StClassMap pdfmark */
1787 private int
pdfmark_StClassMap(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1788 pdfmark_StClassMap(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1789 		   const gs_matrix *pctm, const gs_param_string *objname)
1790 {
1791     return 0;			/****** NOT IMPLEMENTED YET ******/
1792 }
1793 
1794 /*
1795  * [ [/_objdef {objname}] /Subtype name [/Title string] [/Alt string]
1796  *   [/ID string] [/Class name] [/At index] [/Bookmark dict] [action_pairs...]
1797  *   /StPNE pdfmark
1798  */
1799 private int
pdfmark_StPNE(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1800 pdfmark_StPNE(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1801 	      const gs_matrix *pctm, const gs_param_string *objname)
1802 {
1803     return 0;			/****** NOT IMPLEMENTED YET ******/
1804 }
1805 
1806 /* [ [/Title string] [/Open bool] [action_pairs...] /StBookmarkRoot pdfmark */
1807 private int
pdfmark_StBookmarkRoot(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1808 pdfmark_StBookmarkRoot(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1809 		       const gs_matrix *pctm, const gs_param_string *objname)
1810 {
1811     return 0;			/****** NOT IMPLEMENTED YET ******/
1812 }
1813 
1814 /* [ [/E {elt}] /StPush pdfmark */
1815 private int
pdfmark_StPush(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1816 pdfmark_StPush(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1817 	       const gs_matrix *pctm, const gs_param_string *objname)
1818 {
1819     return 0;			/****** NOT IMPLEMENTED YET ******/
1820 }
1821 
1822 /* [ /StPop pdfmark */
1823 private int
pdfmark_StPop(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1824 pdfmark_StPop(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1825 	      const gs_matrix *pctm, const gs_param_string *objname)
1826 {
1827     return 0;			/****** NOT IMPLEMENTED YET ******/
1828 }
1829 
1830 /* [ /StPopAll pdfmark */
1831 private int
pdfmark_StPopAll(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1832 pdfmark_StPopAll(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1833 		 const gs_matrix *pctm, const gs_param_string *objname)
1834 {
1835     return 0;			/****** NOT IMPLEMENTED YET ******/
1836 }
1837 
1838 /* [ [/T tagname] [/At index] /StBMC pdfmark */
1839 private int
pdfmark_StBMC(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1840 pdfmark_StBMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1841 	      const gs_matrix *pctm, const gs_param_string *objname)
1842 {
1843     return 0;			/****** NOT IMPLEMENTED YET ******/
1844 }
1845 
1846 /* [ [/P propdict] [/T tagname] [/At index] /StBDC pdfmark */
1847 private int
pdfmark_StBDC(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1848 pdfmark_StBDC(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1849 	      const gs_matrix *pctm, const gs_param_string *objname)
1850 {
1851     return 0;			/****** NOT IMPLEMENTED YET ******/
1852 }
1853 
1854 /* [ /Obj {obj} [/At index] /StOBJ pdfmark */
1855 private int
pdfmark_StOBJ(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1856 pdfmark_StOBJ(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1857 	      const gs_matrix *pctm, const gs_param_string *objname)
1858 {
1859     return 0;			/****** NOT IMPLEMENTED YET ******/
1860 }
1861 
1862 /* [ /Obj {obj} /StAttr pdfmark */
1863 private int
pdfmark_StAttr(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1864 pdfmark_StAttr(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1865 	       const gs_matrix *pctm, const gs_param_string *objname)
1866 {
1867     return 0;			/****** NOT IMPLEMENTED YET ******/
1868 }
1869 
1870 /* [ /StoreName name /StStore pdfmark */
1871 private int
pdfmark_StStore(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1872 pdfmark_StStore(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1873 		const gs_matrix *pctm, const gs_param_string *objname)
1874 {
1875     return 0;			/****** NOT IMPLEMENTED YET ******/
1876 }
1877 
1878 /* [ /StoreName name /StRetrieve pdfmark */
1879 private int
pdfmark_StRetrieve(gx_device_pdf * pdev,gs_param_string * pairs,uint count,const gs_matrix * pctm,const gs_param_string * objname)1880 pdfmark_StRetrieve(gx_device_pdf *pdev, gs_param_string *pairs, uint count,
1881 		   const gs_matrix *pctm, const gs_param_string *objname)
1882 {
1883     return 0;			/****** NOT IMPLEMENTED YET ******/
1884 }
1885 
1886 /* ---------------- Dispatch ---------------- */
1887 
1888 /*
1889  * Define the pdfmark types we know about.
1890  */
1891 private const pdfmark_name mark_names[] =
1892 {
1893 	/* Miscellaneous. */
1894     {"ANN",          pdfmark_ANN,         PDFMARK_NAMEABLE},
1895     {"LNK",          pdfmark_LNK,         PDFMARK_NAMEABLE},
1896     {"OUT",          pdfmark_OUT,         0},
1897     {"ARTICLE",      pdfmark_ARTICLE,     0},
1898     {"DEST",         pdfmark_DEST,        PDFMARK_NAMEABLE},
1899     {"PS",           pdfmark_PS,          PDFMARK_NAMEABLE},
1900     {"PAGES",        pdfmark_PAGES,       0},
1901     {"PAGE",         pdfmark_PAGE,        0},
1902     {"PAGELABEL",    pdfmark_PAGELABEL,   0},
1903     {"DOCINFO",      pdfmark_DOCINFO,     0},
1904     {"DOCVIEW",      pdfmark_DOCVIEW,     0},
1905 	/* Named objects. */
1906     {"BP",           pdfmark_BP,          PDFMARK_NAMEABLE | PDFMARK_TRUECTM},
1907     {"EP",           pdfmark_EP,          0},
1908     {"SP",           pdfmark_SP,          PDFMARK_ODD_OK | PDFMARK_KEEP_NAME | PDFMARK_TRUECTM},
1909     {"OBJ",          pdfmark_OBJ,         PDFMARK_NAMEABLE},
1910     {"PUT",          pdfmark_PUT,         PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
1911     {".PUTDICT",     pdfmark_PUTDICT,     PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
1912     {".PUTINTERVAL", pdfmark_PUTINTERVAL, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
1913     {".PUTSTREAM",   pdfmark_PUTSTREAM,   PDFMARK_ODD_OK | PDFMARK_KEEP_NAME |
1914                                           PDFMARK_NO_REFS},
1915     {"APPEND",       pdfmark_APPEND,      PDFMARK_KEEP_NAME},
1916     {"CLOSE",        pdfmark_CLOSE,       PDFMARK_ODD_OK | PDFMARK_KEEP_NAME},
1917     {"NamespacePush", pdfmark_NamespacePush, 0},
1918     {"NamespacePop", pdfmark_NamespacePop, 0},
1919     {"NI",           pdfmark_NI,          PDFMARK_NAMEABLE},
1920 	/* Marked content. */
1921     {"MP",           pdfmark_MP,          PDFMARK_ODD_OK},
1922     {"DP",           pdfmark_DP,          0},
1923     {"BMC",          pdfmark_BMC,         PDFMARK_ODD_OK},
1924     {"BDC",          pdfmark_BDC,         0},
1925     {"EMC",          pdfmark_EMC,         0},
1926 	/* Document structure. */
1927     {"StRoleMap",    pdfmark_StRoleMap,   0},
1928     {"StClassMap",   pdfmark_StClassMap,  0},
1929     {"StPNE",        pdfmark_StPNE,       PDFMARK_NAMEABLE},
1930     {"StBookmarkRoot", pdfmark_StBookmarkRoot, 0},
1931     {"StPush",       pdfmark_StPush,       0},
1932     {"StPop",        pdfmark_StPop,        0},
1933     {"StPopAll",     pdfmark_StPopAll,     0},
1934     {"StBMC",        pdfmark_StBMC,        0},
1935     {"StBDC",        pdfmark_StBDC,        0},
1936     /* EMC is listed under "Marked content" above. */
1937     {"StOBJ",        pdfmark_StOBJ,        0},
1938     {"StAttr",       pdfmark_StAttr,       0},
1939     {"StStore",      pdfmark_StStore,      0},
1940     {"StRetrieve",   pdfmark_StRetrieve,   0},
1941 	/* End of list. */
1942     {0, 0}
1943 };
1944 
1945 /* Process a pdfmark. */
1946 int
pdfmark_process(gx_device_pdf * pdev,const gs_param_string_array * pma)1947 pdfmark_process(gx_device_pdf * pdev, const gs_param_string_array * pma)
1948 {
1949     const gs_param_string *data = pma->data;
1950     uint size = pma->size;
1951     const gs_param_string *pts = &data[size - 1];
1952     const gs_param_string *objname = 0;
1953     gs_matrix ctm;
1954     const pdfmark_name *pmn;
1955     int code = 0;
1956 
1957     if (size < 2 ||
1958 	sscanf((const char *)pts[-1].data, "[%g %g %g %g %g %g]",
1959 	       &ctm.xx, &ctm.xy, &ctm.yx, &ctm.yy, &ctm.tx, &ctm.ty) != 6
1960 	)
1961 	return_error(gs_error_rangecheck);
1962     size -= 2;			/* remove CTM & pdfmark name */
1963     for (pmn = mark_names; pmn->mname != 0; ++pmn)
1964 	if (pdf_key_eq(pts, pmn->mname)) {
1965 	    gs_memory_t *mem = pdev->pdf_memory;
1966 	    int odd_ok = (pmn->options & PDFMARK_ODD_OK) != 0;
1967 	    gs_param_string *pairs;
1968 	    int j;
1969 
1970 	    /*
1971 	     * Our coordinate system is scaled so that user space is always
1972 	     * default user space.  Adjust the CTM to match this, except if this
1973 	     * particular pdfmark requires the "true" CTM.
1974 	     */
1975 	    if (pmn->options & PDFMARK_TRUECTM)
1976 		DO_NOTHING;
1977 	    else {
1978 		double xscale = 72.0 / pdev->HWResolution[0],
1979 		       yscale = 72.0 / pdev->HWResolution[1];
1980 		ctm.xx *= xscale, ctm.xy *= yscale;
1981 		ctm.yx *= xscale, ctm.yy *= yscale;
1982 		ctm.tx *= xscale, ctm.ty *= yscale;
1983 	    }
1984 	    if (size & !odd_ok)
1985 		return_error(gs_error_rangecheck);
1986 	    if (pmn->options & PDFMARK_NAMEABLE) {
1987 		/* Look for an object name. */
1988 		for (j = 0; j < size; j += 2) {
1989 		    if (pdf_key_eq(&data[j], "/_objdef")) {
1990 			objname = &data[j + 1];
1991 			if (!pdf_objname_is_valid(objname->data,
1992 						  objname->size)
1993 			    )
1994 			    return_error(gs_error_rangecheck);
1995 			/* Save the pairs without the name. */
1996 			size -= 2;
1997 			pairs = (gs_param_string *)
1998 			    gs_alloc_byte_array(mem, size,
1999 						sizeof(gs_param_string),
2000 						"pdfmark_process(pairs)");
2001 			if (!pairs)
2002 			    return_error(gs_error_VMerror);
2003 			memcpy(pairs, data, j * sizeof(*data));
2004 			memcpy(pairs + j, data + j + 2,
2005 			       (size - j) * sizeof(*data));
2006 			goto copied;
2007 		    }
2008 		}
2009 	    }
2010 	    /* Save all the pairs. */
2011 	    pairs = (gs_param_string *)
2012 		gs_alloc_byte_array(mem, size, sizeof(gs_param_string),
2013 				    "pdfmark_process(pairs)");
2014 	    if (!pairs)
2015 		return_error(gs_error_VMerror);
2016 	    memcpy(pairs, data, size * sizeof(*data));
2017 	copied:		/* Substitute object references for names. */
2018 	    if (!(pmn->options & PDFMARK_NO_REFS)) {
2019 		for (j = (pmn->options & PDFMARK_KEEP_NAME ? 1 : 1 - odd_ok);
2020 		     j < size; j += 2 - odd_ok
2021 		     ) {
2022 		    code = pdf_replace_names(pdev, &pairs[j], &pairs[j]);
2023 		    if (code < 0) {
2024 			gs_free_object(mem, pairs, "pdfmark_process(pairs)");
2025 			return code;
2026 		    }
2027 		}
2028 	    }
2029 	    code = (*pmn->proc) (pdev, pairs, size, &ctm, objname);
2030 	    gs_free_object(mem, pairs, "pdfmark_process(pairs)");
2031 	    break;
2032 	}
2033     return code;
2034 }
2035