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, ¶ms, 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, ¶ms) ||
1353 string_match(vdata, vsize,
1354 (const byte *)"*\000D\000i\000s\000t\000i\000l\000l\000e\000r*",
1355 20, ¶ms)
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