/src/ghostpdl/devices/vector/gdevpdfm.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2025 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* pdfmark processing for PDF-writing driver */ |
18 | | #include "math_.h" |
19 | | #include "memory_.h" |
20 | | #include "gx.h" |
21 | | #include "gserrors.h" |
22 | | #include "gsutil.h" /* for bytes_compare */ |
23 | | #include "gdevpdfx.h" |
24 | | #include "gdevpdfo.h" |
25 | | #include "szlibx.h" |
26 | | #include "slzwx.h" |
27 | | #include "sbrotlix.h" |
28 | | |
29 | | /* GC descriptors */ |
30 | | private_st_pdf_article(); |
31 | | |
32 | | /* |
33 | | * The pdfmark pseudo-parameter indicates the occurrence of a pdfmark |
34 | | * operator in the input file. Its "value" is the arguments of the operator, |
35 | | * passed through essentially unchanged: |
36 | | * (key, value)*, CTM, type |
37 | | */ |
38 | | |
39 | | /* |
40 | | * Define an entry in a table of pdfmark-processing procedures. |
41 | | * (The actual table is at the end of this file, to avoid the need for |
42 | | * forward declarations for the procedures.) |
43 | | */ |
44 | 29.0k | #define PDFMARK_NAMEABLE 1 /* allows _objdef */ |
45 | 29.0k | #define PDFMARK_ODD_OK 2 /* OK if odd # of parameters */ |
46 | 21.5k | #define PDFMARK_KEEP_NAME 4 /* don't substitute reference for name */ |
47 | | /* in 1st argument */ |
48 | 29.0k | #define PDFMARK_NO_REFS 8 /* don't substitute references for names */ |
49 | | /* anywhere */ |
50 | 29.0k | #define PDFMARK_TRUECTM 16 /* pass the true CTM to the procedure, */ |
51 | | /* not the one transformed to reflect the default user space */ |
52 | | typedef struct pdfmark_name_s { |
53 | | const char *mname; |
54 | | pdfmark_proc((*proc)); |
55 | | byte options; |
56 | | } pdfmark_name; |
57 | | |
58 | | /* ---------------- Public utilities ---------------- */ |
59 | | |
60 | | /* Compare a C string and a gs_param_string. */ |
61 | | bool |
62 | | pdf_key_eq(const gs_param_string * pcs, const char *str) |
63 | 893k | { |
64 | 893k | return (strlen(str) == pcs->size && pcs->data && |
65 | 893k | !strncmp(str, (const char *)pcs->data, pcs->size)); |
66 | 893k | } |
67 | | |
68 | | /* Scan an integer out of a parameter string. */ |
69 | | int |
70 | | pdfmark_scan_int(const gs_param_string * pstr, int *pvalue) |
71 | 563 | { |
72 | 563 | #define MAX_INT_STR 20 |
73 | 563 | uint size = pstr->size; |
74 | 563 | char str[MAX_INT_STR + 1]; |
75 | | |
76 | 563 | if (size > MAX_INT_STR) |
77 | 0 | return_error(gs_error_limitcheck); |
78 | 563 | memcpy(str, pstr->data, size); |
79 | 563 | str[size] = 0; |
80 | 563 | return (sscanf(str, "%d", pvalue) == 1 ? 0 : |
81 | 563 | gs_note_error(gs_error_rangecheck)); |
82 | 563 | #undef MAX_INT_STR |
83 | 563 | } |
84 | | |
85 | | /* ---------------- Private utilities ---------------- */ |
86 | | |
87 | | /* Find a key in a dictionary. */ |
88 | | static bool |
89 | | pdfmark_find_key(const char *key, const gs_param_string * pairs, uint count, |
90 | | gs_param_string * pstr) |
91 | 2.04k | { |
92 | 2.04k | uint i; |
93 | | |
94 | 5.61k | for (i = 0; i < count; i += 2) |
95 | 4.29k | if (pdf_key_eq(&pairs[i], key)) { |
96 | 726 | *pstr = pairs[i + 1]; |
97 | 726 | return true; |
98 | 726 | } |
99 | 1.32k | pstr->data = 0; |
100 | 1.32k | pstr->size = 0; |
101 | 1.32k | return false; |
102 | 2.04k | } |
103 | | |
104 | | /* |
105 | | * Get the page number for a page referenced by number or as /Next or /Prev. |
106 | | * The result may be 0 if the page number is 0 or invalid. |
107 | | */ |
108 | | static int |
109 | | pdfmark_page_number(gx_device_pdf * pdev, const gs_param_string * pnstr) |
110 | 346 | { |
111 | 346 | int page = pdev->next_page + 1; |
112 | | |
113 | 346 | if (pnstr->data == 0); |
114 | 346 | else if (pdf_key_eq(pnstr, "/Next")) |
115 | 0 | ++page; |
116 | 346 | else if (pdf_key_eq(pnstr, "/Prev")) |
117 | 0 | --page; |
118 | 346 | else if (pdfmark_scan_int(pnstr, &page) < 0) |
119 | 0 | page = 0; |
120 | 346 | return page; |
121 | 346 | } |
122 | | |
123 | | /* |
124 | | * This routine checks the page number is inside the valid range FirstPage->LastPage |
125 | | * and if it is, and FirstPage is not 0, it updates the page number by rebasing it to |
126 | | * the new FirstPage. If all conditions are met we also update the 'max_referred_page' |
127 | | * which we check during closedown, and emit a warning if a reference should somehow |
128 | | * point outside the valid range of pages in the document. This can happen if we reference |
129 | | * a destination page that we haven't created yet, and when we get to the end of the |
130 | | * job we discover we never did create it. |
131 | | */ |
132 | | static int |
133 | | update_max_page_reference(gx_device_pdf * pdev, int *page) |
134 | 346 | { |
135 | 346 | if (*page < pdev->FirstPage || (pdev->LastPage != 0 && *page > pdev->LastPage)) { |
136 | 0 | emprintf1(pdev->memory, "Destination page %d lies outside the valid page range.\n", *page); |
137 | 0 | return -1; |
138 | 0 | } |
139 | 346 | else { |
140 | 346 | if (pdev->FirstPage != 0) |
141 | 0 | *page = (*page - pdev->FirstPage) + 1; |
142 | | |
143 | 346 | if (pdev->max_referred_page < *page) |
144 | 285 | pdev->max_referred_page = *page; |
145 | 346 | } |
146 | 346 | return 0; |
147 | 346 | } |
148 | | |
149 | | /* Construct a destination string specified by /Page and/or /View. */ |
150 | | /* Return 0 if none (but still fill in a default), 1 or 2 if present */ |
151 | | /* (1 if only one of /Page or /View, 2 if both), <0 if error. */ |
152 | | static int |
153 | | pdfmark_make_dest(char dstr[MAX_DEST_STRING], gx_device_pdf * pdev, |
154 | | const char *Page_key, const char *View_key, |
155 | | const gs_param_string * pairs, uint count, uint RequirePage) |
156 | 833 | { |
157 | 833 | gs_param_string page_string, view_string; |
158 | 833 | int present = |
159 | 833 | pdfmark_find_key(Page_key, pairs, count, &page_string) + |
160 | 833 | pdfmark_find_key(View_key, pairs, count, &view_string); |
161 | 833 | int page=0; |
162 | 833 | gs_param_string action; |
163 | 833 | int len, code = 0; |
164 | | |
165 | 833 | if (present || RequirePage) |
166 | 346 | page = pdfmark_page_number(pdev, &page_string); |
167 | | |
168 | 833 | if (view_string.size == 0) |
169 | 487 | param_string_from_string(view_string, "[/XYZ null null null]"); |
170 | 833 | if (page == 0) |
171 | 487 | strcpy(dstr, "[null "); |
172 | 346 | else if (pdfmark_find_key("/Action", pairs, count, &action) && |
173 | 346 | pdf_key_eq(&action, "/GoToR") |
174 | 346 | ) |
175 | 0 | gs_snprintf(dstr, MAX_DEST_STRING, "[%d ", page - 1); |
176 | 346 | else { |
177 | 346 | code = update_max_page_reference(pdev, &page); |
178 | 346 | if (code < 0) |
179 | 0 | return code; |
180 | 346 | gs_snprintf(dstr, MAX_DEST_STRING, "[%"PRId64" 0 R ", pdf_page_id(pdev, page)); |
181 | 346 | } |
182 | 833 | len = strlen(dstr); |
183 | 833 | if (len + view_string.size > MAX_DEST_STRING) |
184 | 0 | return_error(gs_error_limitcheck); |
185 | 833 | if (view_string.data[0] != '[' || |
186 | 833 | view_string.data[view_string.size - 1] != ']' |
187 | 833 | ) |
188 | 0 | return_error(gs_error_rangecheck); |
189 | 833 | memcpy(dstr + len, view_string.data + 1, view_string.size - 1); |
190 | 833 | dstr[len + view_string.size - 1] = 0; |
191 | 833 | return present; |
192 | 833 | } |
193 | | |
194 | | /* |
195 | | * If a named destination is specified by a string, convert it to a name, |
196 | | * update *dstr, and return 1; otherwise return 0. |
197 | | */ |
198 | | static int |
199 | | pdfmark_coerce_dest(gs_param_string *dstr, char dest[MAX_DEST_STRING]) |
200 | 0 | { |
201 | 0 | const byte *data = dstr->data; |
202 | 0 | uint size = dstr->size; |
203 | 0 | if (size > MAX_DEST_STRING) |
204 | 0 | return_error(gs_error_limitcheck); |
205 | 0 | if (size == 0 || data[0] != '(') |
206 | 0 | return 0; |
207 | | /****** HANDLE ESCAPES ******/ |
208 | 0 | memcpy(dest, data, size - 1); |
209 | 0 | dest[0] = '/'; |
210 | 0 | dest[size - 1] = 0; |
211 | 0 | dstr->data = (byte *)dest; |
212 | 0 | dstr->size = size - 1; |
213 | 0 | return 1; |
214 | 0 | } |
215 | | |
216 | | /* Put pairs in a dictionary. */ |
217 | | static int |
218 | | pdfmark_put_c_pair(cos_dict_t *pcd, const char *key, |
219 | | const gs_param_string * pvalue) |
220 | 756 | { |
221 | 756 | return cos_dict_put_c_key_string(pcd, key, pvalue->data, pvalue->size); |
222 | 756 | } |
223 | | static int |
224 | | pdfmark_put_pair(cos_dict_t *pcd, const gs_param_string * pair) |
225 | 10.6k | { |
226 | 10.6k | return cos_dict_put_string(pcd, pair->data, pair->size, |
227 | 10.6k | pair[1].data, pair[1].size); |
228 | 10.6k | } |
229 | | |
230 | | /* Scan a Rect value. */ |
231 | | static int |
232 | | pdfmark_scan_rect(gs_rect * prect, const gs_param_string * str, |
233 | | const gs_matrix * pctm) |
234 | 567 | { |
235 | 567 | uint size = str->size; |
236 | 567 | double v[4]; |
237 | 1.13k | #define MAX_RECT_STRING 100 |
238 | 567 | char chars[MAX_RECT_STRING + 3]; |
239 | 567 | int end_check; |
240 | | |
241 | 567 | if (str->size > MAX_RECT_STRING) |
242 | 0 | return_error(gs_error_limitcheck); |
243 | 567 | memcpy(chars, str->data, size); |
244 | 567 | strcpy(chars + size, " 0"); |
245 | 567 | if (sscanf(chars, "[%lg %lg %lg %lg]%d", |
246 | 567 | &v[0], &v[1], &v[2], &v[3], &end_check) != 5 |
247 | 567 | ) |
248 | 0 | return_error(gs_error_rangecheck); |
249 | 567 | gs_point_transform(v[0], v[1], pctm, &prect->p); |
250 | 567 | gs_point_transform(v[2], v[3], pctm, &prect->q); |
251 | 567 | return 0; |
252 | 567 | } |
253 | | |
254 | | /* Make a Rect value. */ |
255 | | static void |
256 | | pdfmark_make_rect(char str[MAX_RECT_STRING], const gs_rect * prect) |
257 | 567 | { |
258 | | /* |
259 | | * We have to use a stream and pprintf, rather than sprintf, |
260 | | * because printf formats can't express the PDF restrictions on |
261 | | * the form of the output. |
262 | | */ |
263 | 567 | stream s; |
264 | | |
265 | 567 | s_init(&s, NULL); |
266 | 567 | swrite_string(&s, (byte *)str, MAX_RECT_STRING - 1); |
267 | 567 | pprintg4(&s, "[%g %g %g %g]", |
268 | 567 | prect->p.x, prect->p.y, prect->q.x, prect->q.y); |
269 | 567 | str[stell(&s)] = 0; |
270 | 567 | } |
271 | | |
272 | 670 | #define MAX_BORDER_STRING 100 |
273 | | /* Write a transformed Border value on a stream. */ |
274 | | static int |
275 | | pdfmark_write_border(stream *s, const gs_param_string *str, |
276 | | const gs_matrix *pctm) |
277 | 335 | { |
278 | 335 | int i; |
279 | | |
280 | 2.73k | for (i = 0; i < str->size; i++) |
281 | 2.39k | stream_putc(s, str->data[i]); |
282 | 335 | return 0; |
283 | 335 | } |
284 | | |
285 | | /* Put an element in a stream's dictionary. */ |
286 | | static int |
287 | | cos_stream_put_c_strings(cos_stream_t *pcs, const char *key, const char *value) |
288 | 0 | { |
289 | 0 | return cos_dict_put_c_strings(cos_stream_dict(pcs), key, value); |
290 | 0 | } |
291 | | |
292 | | static int |
293 | | setup_pdfmark_stream_no_compression(gx_device_psdf *pdev0, |
294 | | cos_stream_t *pco) |
295 | 0 | { |
296 | | /* This function is for pdfwrite only. */ |
297 | 0 | gx_device_pdf *pdev = (gx_device_pdf *)pdev0; |
298 | 0 | gs_memory_t *mem = pdev->pdf_memory; |
299 | |
|
300 | 0 | pco->input_strm = cos_write_stream_alloc(pco, pdev, |
301 | 0 | "setup_pdfmark_stream_compression"); |
302 | 0 | if (pco->input_strm == 0) |
303 | 0 | return_error(gs_error_VMerror); |
304 | 0 | if (!pdev->binary_ok) { |
305 | 0 | stream_state *ss = s_alloc_state(mem, s_A85E_template.stype, |
306 | 0 | "setup_pdfmark_stream_compression"); |
307 | 0 | if (ss == 0) |
308 | 0 | return_error(gs_error_VMerror); |
309 | 0 | if (s_add_filter(&pco->input_strm, &s_A85E_template, ss, mem) == 0) { |
310 | 0 | gs_free_object(mem, ss, "setup_image_compression"); |
311 | 0 | return_error(gs_error_VMerror); |
312 | 0 | } |
313 | 0 | } |
314 | 0 | return 0; |
315 | 0 | } |
316 | | |
317 | | /* Setup pdfmak stream compression. */ |
318 | | static int |
319 | | setup_pdfmark_stream_compression(gx_device_psdf *pdev0, |
320 | | cos_stream_t *pco) |
321 | 204 | { |
322 | | /* This function is for pdfwrite only. */ |
323 | 204 | gx_device_pdf *pdev = (gx_device_pdf *)pdev0; |
324 | 204 | gs_memory_t *mem = pdev->pdf_memory; |
325 | 204 | static const pdf_filter_names_t fnames = { |
326 | 204 | PDF_FILTER_NAMES |
327 | 204 | }; |
328 | 204 | stream_state *st; |
329 | 204 | const stream_template *templat; |
330 | | |
331 | 204 | if (pdev->CompressStreams) { |
332 | 204 | if(pdev->version >= psdf_version_ll3) { |
333 | 196 | if (pdev->UseBrotli) |
334 | 0 | templat = &s_brotliE_template; |
335 | 196 | else |
336 | 196 | templat = &s_zlibE_template; |
337 | 196 | } else |
338 | 8 | templat = &s_LZWE_template; |
339 | 204 | } else |
340 | 0 | return 0; |
341 | | |
342 | 204 | pco->input_strm = cos_write_stream_alloc(pco, pdev, |
343 | 204 | "setup_pdfmark_stream_compression"); |
344 | 204 | if (pco->input_strm == 0) |
345 | 0 | return_error(gs_error_VMerror); |
346 | 204 | if (!pdev->binary_ok) { |
347 | 8 | stream_state *ss = s_alloc_state(mem, s_A85E_template.stype, |
348 | 8 | "setup_pdfmark_stream_compression"); |
349 | 8 | if (ss == 0) |
350 | 0 | return_error(gs_error_VMerror); |
351 | 8 | if (s_add_filter(&pco->input_strm, &s_A85E_template, ss, mem) == 0) { |
352 | 0 | gs_free_object(mem, ss, "setup_image_compression"); |
353 | 0 | return_error(gs_error_VMerror); |
354 | 0 | } |
355 | 8 | } |
356 | 204 | st = s_alloc_state(mem, templat->stype, |
357 | 204 | "setup_pdfmark_stream_compression"); |
358 | 204 | if (st == 0) |
359 | 0 | return_error(gs_error_VMerror); |
360 | 204 | if (templat->set_defaults) |
361 | 204 | (*templat->set_defaults) (st); |
362 | 204 | if (s_add_filter(&pco->input_strm, templat, st, mem) == 0) { |
363 | 0 | gs_free_object(mem, st, "setup_image_compression"); |
364 | 0 | return_error(gs_error_VMerror); |
365 | 0 | } |
366 | 204 | return pdf_put_filters(cos_stream_dict(pco), pdev, pco->input_strm, &fnames); |
367 | 204 | } |
368 | | |
369 | | static int |
370 | | pdfmark_bind_named_object(gx_device_pdf *pdev, const gs_const_string *objname, |
371 | | pdf_resource_t **pres) |
372 | 0 | { |
373 | 0 | int code; |
374 | |
|
375 | 0 | if (objname != NULL && objname->size) { |
376 | 0 | const cos_value_t *v = cos_dict_find(pdev->local_named_objects, objname->data, objname->size); |
377 | |
|
378 | 0 | if (v != NULL) { |
379 | 0 | if (v->value_type == COS_VALUE_OBJECT) { |
380 | 0 | if (cos_type(v->contents.object) == &cos_generic_procs) { |
381 | | /* The object was referred but not defined. |
382 | | Use the old object id. |
383 | | The old object stub to be dropped. */ |
384 | 0 | pdf_reserve_object_id(pdev, *pres, v->contents.object->id); |
385 | 0 | } else if (!v->contents.object->written) { |
386 | | /* We can't know whether the old object was referred or not. |
387 | | Write it out for a consistent result in any case. */ |
388 | 0 | code = cos_write_object(v->contents.object, pdev, resourceOther); |
389 | |
|
390 | 0 | if (code < 0) |
391 | 0 | return code; |
392 | 0 | v->contents.object->written = true; |
393 | 0 | } |
394 | 0 | } else |
395 | 0 | return_error(gs_error_rangecheck); /* Must not happen. */ |
396 | 0 | } |
397 | 0 | } |
398 | 0 | if ((*pres)->object->id == -1) { |
399 | 0 | if(objname != NULL && objname->size) |
400 | 0 | code = pdf_substitute_resource(pdev, pres, resourceXObject, NULL, false); |
401 | 0 | else |
402 | 0 | code = pdf_substitute_resource(pdev, pres, resourceXObject, NULL, true); |
403 | 0 | (*pres)->where_used |= pdev->used_mask; |
404 | 0 | if (code < 0) |
405 | 0 | return code; |
406 | 0 | } else { |
407 | | /* Unfortunately we can't apply pdf_substitute_resource, |
408 | | because the object may already be referred by its id. |
409 | | Redundant objects may happen in this case. |
410 | | For better results users should define objects before usage. |
411 | | */ |
412 | 0 | } |
413 | 0 | if (objname != NULL && objname->size) { |
414 | 0 | cos_value_t value; |
415 | |
|
416 | 0 | code = cos_dict_put(pdev->local_named_objects, objname->data, |
417 | 0 | objname->size, cos_object_value(&value, (cos_object_t *)(*pres)->object)); |
418 | 0 | if (code < 0) |
419 | 0 | return code; |
420 | 0 | } |
421 | 0 | return 0; |
422 | 0 | } |
423 | | |
424 | | /* ---------------- Miscellaneous pdfmarks ---------------- */ |
425 | | |
426 | | /* |
427 | | * Create the dictionary for an annotation or outline. For some |
428 | | * unfathomable reason, PDF requires the following key substitutions |
429 | | * relative to pdfmarks: |
430 | | * In annotation and link dictionaries: |
431 | | * /Action => /A, /Color => /C, /Title => /T |
432 | | * In outline directionaries: |
433 | | * /Action => /A, but *not* /Color or /Title |
434 | | * In Action subdictionaries: |
435 | | * /Dest => /D, /File => /F, /Subtype => /S |
436 | | * and also the following substitutions: |
437 | | * /Action /Launch /File xxx => |
438 | | * /A << /S /Launch /F xxx >> |
439 | | * /Action /GoToR /File xxx /Dest yyy => |
440 | | * /A << /S /GoToR /F xxx /D yyy' >> |
441 | | * /Action /Article /Dest yyy => |
442 | | * /A << /S /Thread /D yyy' >> |
443 | | * /Action /GoTo => drop the Action key |
444 | | * Also, \n in Contents strings must be replaced with \r. |
445 | | * Also, an outline dictionary with no action, Dest, Page, or View has an |
446 | | * implied GoTo action with Dest = [{ThisPage} /XYZ null null null]. |
447 | | * Note that for Thread actions, the Dest is not a real destination, |
448 | | * and must not be processed as one. |
449 | | * |
450 | | * We always treat /A and /F as equivalent to /Action and /File |
451 | | * respectively. The pdfmark and PDF documentation is so confused on the |
452 | | * issue of when the long and short names should be used that we only give |
453 | | * this a 50-50 chance of being right. |
454 | | * |
455 | | * Note that we must transform Rect and Border coordinates. |
456 | | */ |
457 | | |
458 | | typedef struct ao_params_s { |
459 | | gx_device_pdf *pdev; /* for pdfmark_make_dest */ |
460 | | const char *subtype; /* default Subtype in top-level dictionary */ |
461 | | int src_pg; /* set to SrcPg - 1 if any */ |
462 | | } ao_params_t; |
463 | | static int |
464 | | pdfmark_put_ao_pairs(gx_device_pdf * pdev, cos_dict_t *pcd, |
465 | | const gs_param_string * pairs, uint count, |
466 | | const gs_matrix * pctm, ao_params_t * params, |
467 | | bool for_outline) |
468 | 765 | { |
469 | 765 | const gs_param_string *Action = 0; |
470 | 765 | const gs_param_string *File = 0; |
471 | 765 | const gs_param_string *URI = 0; |
472 | 765 | gs_param_string Dest; |
473 | 765 | gs_param_string Subtype; |
474 | 765 | uint i; |
475 | 765 | int code; |
476 | 765 | char dest[MAX_DEST_STRING]; |
477 | 765 | bool coerce_dest = false; |
478 | | |
479 | 765 | Subtype.data = 0; |
480 | 765 | Subtype.size = 0; |
481 | 765 | Dest.data = 0; |
482 | 765 | Dest.size = 0; |
483 | 765 | if (params->subtype) |
484 | 548 | param_string_from_string(Subtype, params->subtype); |
485 | 217 | else |
486 | 217 | Subtype.data = 0; |
487 | 6.76k | for (i = 0; i < count; i += 2) { |
488 | 6.00k | const gs_param_string *pair = &pairs[i]; |
489 | 6.00k | unsigned int src_pg; |
490 | | |
491 | 6.00k | if (pdf_key_eq(pair, "/SrcPg")){ |
492 | 0 | unsigned char *buf0 = (unsigned char *)gs_alloc_bytes(pdev->memory, (pair[1].size + 1) * sizeof(unsigned char), |
493 | 0 | "pdf_xmp_write_translated"); |
494 | |
|
495 | 0 | if (buf0 == NULL) |
496 | 0 | return_error(gs_error_VMerror); |
497 | | |
498 | 0 | memcpy(buf0, pair[1].data, pair[1].size); |
499 | 0 | buf0[pair[1].size] = 0x00; |
500 | 0 | if (sscanf((char *)buf0, "%ud", &src_pg) == 1) |
501 | 0 | params->src_pg = src_pg - 1; |
502 | 0 | gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated"); |
503 | 0 | } |
504 | 6.00k | else if (!for_outline && pdf_key_eq(pair, "/Color")) |
505 | 0 | pdfmark_put_c_pair(pcd, "/C", pair + 1); |
506 | 6.00k | else if (!for_outline && pdf_key_eq(pair, "/Title")) |
507 | 0 | pdfmark_put_c_pair(pcd, "/T", pair + 1); |
508 | 6.00k | else if (pdf_key_eq(pair, "/Action") || pdf_key_eq(pair, "/A")) |
509 | 218 | Action = pair; |
510 | | /* Previously also catered for '/F', but at the top level (outside an |
511 | | * Action dict which is handled below), a /F can only be the Flags for |
512 | | * the annotation, not a File or JavaScript action. |
513 | | */ |
514 | 5.78k | else if (pdf_key_eq(pair, "/File") /* || pdf_key_eq(pair, "/F")*/) |
515 | 0 | File = pair; |
516 | 5.78k | else if (pdf_key_eq(pair, "/Dest")) { |
517 | 0 | Dest = pair[1]; |
518 | 0 | coerce_dest = true; |
519 | 0 | } |
520 | 5.78k | else if (pdf_key_eq(pair, "/URI")) { |
521 | 0 | URI = pair; /* save it for placing into the Action dict */ |
522 | 0 | } |
523 | 5.78k | else if (pdf_key_eq(pair, "/Page") || pdf_key_eq(pair, "/View")) { |
524 | | /* Make a destination even if this is for an outline. */ |
525 | 416 | if (Dest.data == 0) { |
526 | 208 | code = pdfmark_make_dest(dest, params->pdev, "/Page", "/View", |
527 | 208 | pairs, count, 0); |
528 | 208 | if (code >= 0) { |
529 | 208 | param_string_from_string(Dest, dest); |
530 | 208 | if (for_outline) |
531 | 192 | coerce_dest = false; |
532 | 208 | } else { |
533 | 0 | emprintf(pdev->memory, " **** Warning: Outline has invalid link that was discarded.\n"); |
534 | 0 | return gs_note_error(gs_error_rangecheck); |
535 | 0 | } |
536 | 208 | } |
537 | 5.36k | } else if (pdf_key_eq(pair, "/Subtype")) |
538 | 548 | Subtype = pair[1]; |
539 | | /* |
540 | | * We also have to replace all occurrences of \n in Contents |
541 | | * strings with \r. Unfortunately, they probably have already |
542 | | * been converted to \012.... |
543 | | */ |
544 | 4.82k | else if (pdf_key_eq(pair, "/Contents")) { |
545 | 182 | byte *cstr; |
546 | 182 | uint csize = pair[1].size; |
547 | 182 | cos_value_t *pcv; |
548 | 182 | uint i, j; |
549 | | |
550 | | /* |
551 | | * Copy the string into value storage, then update it in place. |
552 | | */ |
553 | 182 | pdfmark_put_pair(pcd, pair); |
554 | | /* Break const so we can update the (copied) string. */ |
555 | 182 | pcv = (cos_value_t *)cos_dict_find_c_key(pcd, "/Contents"); |
556 | 182 | if (pcv == NULL) |
557 | 0 | return_error(gs_error_ioerror); /* shouldn't be possible */ |
558 | 182 | cstr = pcv->contents.chars.data; |
559 | | /* Loop invariant: j <= i < csize. */ |
560 | 80.2k | for (i = j = 0; i < csize;) |
561 | 80.0k | if (csize - i >= 2 && !memcmp(cstr + i, "\\n", 2) && |
562 | 80.0k | (i == 0 || cstr[i - 1] != '\\') |
563 | 80.0k | ) { |
564 | 2 | cstr[j] = '\\', cstr[j + 1] = 'r'; |
565 | 2 | i += 2, j += 2; |
566 | 80.0k | } else if (csize - i >= 4 && !memcmp(cstr + i, "\\012", 4) && |
567 | 80.0k | (i == 0 || cstr[i - 1] != '\\') |
568 | 80.0k | ) { |
569 | 0 | cstr[j] = '\\', cstr[j + 1] = 'r'; |
570 | 0 | i += 4, j += 2; |
571 | 0 | } else |
572 | 80.0k | cstr[j++] = cstr[i++]; |
573 | 182 | if (j != i) { |
574 | 0 | pcv->contents.chars.data = |
575 | 0 | gs_resize_string(pdev->pdf_memory, cstr, csize, j, |
576 | 0 | "pdfmark_put_ao_pairs"); |
577 | 0 | pcv->contents.chars.size = j; |
578 | 0 | } |
579 | 4.63k | } else if (pdf_key_eq(pair, "/L")) { |
580 | 21 | gs_rect rect; |
581 | 21 | char rstr[MAX_RECT_STRING]; |
582 | 21 | int code = pdfmark_scan_rect(&rect, pair + 1, pctm); |
583 | 21 | if (code < 0) |
584 | 0 | return code; |
585 | 21 | pdfmark_make_rect(rstr, &rect); |
586 | 21 | cos_dict_put_c_key_string(pcd, "/L", (byte *)rstr, |
587 | 21 | strlen(rstr)); |
588 | 4.61k | } else if (pdf_key_eq(pair, "/Rect")) { |
589 | 546 | gs_rect rect; |
590 | 546 | char rstr[MAX_RECT_STRING]; |
591 | 546 | int code = pdfmark_scan_rect(&rect, pair + 1, pctm); |
592 | | |
593 | 546 | if (code < 0) |
594 | 0 | return code; |
595 | 546 | pdfmark_make_rect(rstr, &rect); |
596 | 546 | cos_dict_put_c_key_string(pcd, "/Rect", (byte *)rstr, |
597 | 546 | strlen(rstr)); |
598 | 4.07k | } else if (pdf_key_eq(pair, "/Border")) { |
599 | 335 | stream s; |
600 | 335 | char bstr[MAX_BORDER_STRING + 1]; |
601 | 335 | int code; |
602 | | |
603 | 335 | s_init(&s, NULL); |
604 | 335 | swrite_string(&s, (byte *)bstr, MAX_BORDER_STRING + 1); |
605 | 335 | code = pdfmark_write_border(&s, pair + 1, pctm); |
606 | 335 | if (code < 0) |
607 | 0 | return code; |
608 | 335 | if (stell(&s) > MAX_BORDER_STRING) |
609 | 0 | return_error(gs_error_limitcheck); |
610 | 335 | bstr[stell(&s)] = 0; |
611 | 335 | cos_dict_put_c_key_string(pcd, "/Border", (byte *)bstr, |
612 | 335 | strlen(bstr)); |
613 | 3.73k | } else if (for_outline && pdf_key_eq(pair, "/Count")) |
614 | 3.73k | DO_NOTHING; |
615 | 3.51k | else { |
616 | 3.51k | int i, j=0; |
617 | 3.51k | unsigned char *temp, *buf0 = (unsigned char *)gs_alloc_bytes(pdev->memory, pair[1].size * 2 * sizeof(unsigned char), |
618 | 3.51k | "pdf_xmp_write_translated"); |
619 | 3.51k | if (buf0 == NULL) |
620 | 0 | return_error(gs_error_VMerror); |
621 | 129k | for (i = 0; i < pair[1].size; i++) { |
622 | 126k | byte c = pair[1].data[i]; |
623 | | |
624 | 126k | buf0[j++] = c; |
625 | | /* Acrobat doesn't like the 'short' escapes. and wants them as octal.... |
626 | | * I beliwvw this should be considered an Acrtobat bug, either escapes |
627 | | * can be used, or not, we shouldn't have to force them to octal. |
628 | | */ |
629 | 126k | if (c == '\\') { |
630 | 3.30k | switch(pair[1].data[i + 1]) { |
631 | 0 | case 'b': |
632 | 0 | buf0[j++] = '0'; |
633 | 0 | buf0[j++] = '1'; |
634 | 0 | buf0[j++] = '0'; |
635 | 0 | i++; |
636 | 0 | break; |
637 | 0 | case 'f': |
638 | 0 | buf0[j++] = '0'; |
639 | 0 | buf0[j++] = '1'; |
640 | 0 | buf0[j++] = '4'; |
641 | 0 | i++; |
642 | 0 | break; |
643 | 109 | case 'n': |
644 | 109 | buf0[j++] = '0'; |
645 | 109 | buf0[j++] = '1'; |
646 | 109 | buf0[j++] = '2'; |
647 | 109 | i++; |
648 | 109 | break; |
649 | 4 | case 'r': |
650 | 4 | buf0[j++] = '0'; |
651 | 4 | buf0[j++] = '1'; |
652 | 4 | buf0[j++] = '5'; |
653 | 4 | i++; |
654 | 4 | break; |
655 | 0 | case 't': |
656 | 0 | buf0[j++] = '0'; |
657 | 0 | buf0[j++] = '1'; |
658 | 0 | buf0[j++] = '1'; |
659 | 0 | i++; |
660 | 0 | break; |
661 | 2 | case '\\': |
662 | 2 | buf0[j++] = '1'; |
663 | 2 | buf0[j++] = '3'; |
664 | 2 | buf0[j++] = '4'; |
665 | 2 | i++; |
666 | 2 | break; |
667 | 3.18k | default: |
668 | 3.18k | break; |
669 | 3.30k | } |
670 | 3.30k | } |
671 | 126k | } |
672 | 3.51k | i = pair[1].size; |
673 | 3.51k | temp = (unsigned char *)pair[1].data; |
674 | 3.51k | ((gs_param_string *)pair)[1].data = buf0; |
675 | 3.51k | ((gs_param_string *)pair)[1].size = j; |
676 | | |
677 | 3.51k | pdfmark_put_pair(pcd, pair); |
678 | | |
679 | 3.51k | ((gs_param_string *)pair)[1].data = temp; |
680 | 3.51k | ((gs_param_string *)pair)[1].size = i; |
681 | 3.51k | gs_free_object(pdev->memory, buf0, "pdf_xmp_write_translated"); |
682 | 3.51k | } |
683 | 6.00k | } |
684 | 765 | if (!for_outline && pdf_key_eq(&Subtype, "/Link")) { |
685 | 213 | if (Action) { |
686 | | /* Don't delete the Dest for GoTo or file-GoToR. */ |
687 | 197 | if (pdf_key_eq(Action + 1, "/GoTo") || |
688 | 197 | (File && pdf_key_eq(Action + 1, "/GoToR")) |
689 | 197 | ) |
690 | 197 | DO_NOTHING; |
691 | 197 | else |
692 | 197 | Dest.data = 0; |
693 | 197 | } |
694 | 213 | } |
695 | | |
696 | | /* Now handle the deferred keys. */ |
697 | 765 | if (Action) { |
698 | 218 | const byte *astr = Action[1].data; |
699 | 218 | const uint asize = Action[1].size; |
700 | | |
701 | 218 | if ((File != 0 || Dest.data != 0 || URI != 0) && |
702 | 218 | (pdf_key_eq(Action + 1, "/Launch") || |
703 | 0 | (pdf_key_eq(Action + 1, "/GoToR") && File) || |
704 | 0 | pdf_key_eq(Action + 1, "/Article")) |
705 | 218 | ) { |
706 | 0 | cos_dict_t *adict; |
707 | 0 | cos_value_t avalue; |
708 | |
|
709 | 0 | if (pdf_key_eq(Action + 1, "/Launch") && pdev->PDFA != 0) { |
710 | 0 | switch (pdev->PDFACompatibilityPolicy) { |
711 | | /* Default behaviour matches Adobe Acrobat, warn and continue, |
712 | | * output file will not be PDF/A compliant |
713 | | */ |
714 | 0 | case 0: |
715 | 0 | emprintf(pdev->memory, |
716 | 0 | "Launch annotations not permitted in PDF/A, reverting to normal PDF output\n"); |
717 | 0 | pdev->AbortPDFAX = true; |
718 | 0 | pdev->PDFA = 0; |
719 | 0 | break; |
720 | | /* Since the annotation would break PDF/A compatibility, do not |
721 | | * include it, but warn the user that it has been dropped. |
722 | | */ |
723 | 0 | case 1: |
724 | 0 | emprintf(pdev->memory, |
725 | 0 | "Launch annotations not permitted in PDF/A, cannot drop annotation, aborting conversion\n"); |
726 | 0 | return_error (gs_error_typecheck); |
727 | 0 | break; |
728 | 0 | case 2: |
729 | 0 | emprintf(pdev->memory, |
730 | 0 | "Launch annotations not permitted in PDF/A, aborting conversion\n"); |
731 | 0 | return_error (gs_error_typecheck); |
732 | 0 | break; |
733 | 0 | default: |
734 | 0 | emprintf(pdev->memory, |
735 | 0 | "Launch annotations not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n"); |
736 | 0 | pdev->AbortPDFAX = true; |
737 | 0 | pdev->PDFA = 0; |
738 | 0 | break; |
739 | 0 | } |
740 | 0 | } |
741 | | |
742 | 0 | adict = cos_dict_alloc(pdev, "action dict"); |
743 | 0 | if (adict == 0) |
744 | 0 | return_error(gs_error_VMerror); |
745 | 0 | if (!for_outline) { |
746 | | /* We aren't sure whether this is really needed.... */ |
747 | 0 | code = cos_dict_put_c_strings(adict, "/Type", "/Action"); |
748 | 0 | if (code < 0) |
749 | 0 | return code; |
750 | 0 | } |
751 | 0 | if (pdf_key_eq(Action + 1, "/Article")) { |
752 | 0 | code = cos_dict_put_c_strings(adict, "/S", "/Thread"); |
753 | 0 | if (code < 0) |
754 | 0 | return code; |
755 | 0 | coerce_dest = false; /* Dest is not a real destination */ |
756 | 0 | } |
757 | 0 | else |
758 | 0 | pdfmark_put_c_pair(adict, "/S", Action + 1); |
759 | 0 | if (Dest.data) { |
760 | 0 | if (coerce_dest) |
761 | 0 | pdfmark_coerce_dest(&Dest, dest); |
762 | 0 | pdfmark_put_c_pair(adict, "/D", &Dest); |
763 | 0 | Dest.data = 0; /* so we don't write it again */ |
764 | 0 | } |
765 | 0 | if (File) { |
766 | 0 | pdfmark_put_c_pair(adict, "/F", File + 1); |
767 | 0 | File = 0; /* so we don't write it again */ |
768 | 0 | } |
769 | 0 | if (URI) { |
770 | | /* Adobe Distiller puts a /URI key from pdfmark into the */ |
771 | | /* Action dict with /S /URI as Subtype */ |
772 | 0 | pdfmark_put_pair(adict, URI); |
773 | 0 | code = cos_dict_put_c_strings(adict, "/S", "/URI"); |
774 | 0 | if (code < 0) |
775 | 0 | return code; |
776 | 0 | } |
777 | 0 | cos_dict_put(pcd, (const byte *)"/A", 2, |
778 | 0 | COS_OBJECT_VALUE(&avalue, adict)); |
779 | 218 | } else if (asize >= 4 && !memcmp(astr, "<<", 2)) { |
780 | | /* Replace occurrences of /Dest, /File, and /Subtype. */ |
781 | 106 | const byte *scan = astr + 2; |
782 | 106 | const byte *end = astr + asize; |
783 | 106 | gs_param_string key, value; |
784 | 106 | cos_dict_t *adict = cos_dict_alloc(pdev, "action dict"); |
785 | 106 | cos_value_t avalue; |
786 | 106 | int code = 0; |
787 | | |
788 | 106 | if (adict == 0) |
789 | 0 | return_error(gs_error_VMerror); |
790 | 106 | if (URI) { |
791 | | /* Adobe Distiller puts a /URI key from pdfmark into the */ |
792 | | /* Action dict with /S /URI as Subtype */ |
793 | 0 | pdfmark_put_pair(adict, URI); |
794 | 0 | if(cos_dict_put_c_strings(adict, "/S", "/URI") < 0) |
795 | 0 | return code; |
796 | 0 | } |
797 | 384 | while ((code = pdf_scan_token(&scan, end, &key.data)) > 0) { |
798 | 384 | key.size = scan - key.data; |
799 | 384 | if (key.data[0] != '/' || |
800 | 384 | (code = pdf_scan_token_composite(&scan, end, &value.data)) != 1) |
801 | 106 | break; |
802 | 278 | value.size = scan - value.data; |
803 | 278 | if (pdev->PDFA != 0 && (pdf_key_eq(&key, "/Subtype") || pdf_key_eq(&key, "/S"))) { |
804 | 0 | if (pdf_key_eq(&value, "/Launch")) { |
805 | 0 | switch (pdev->PDFACompatibilityPolicy) { |
806 | | /* Default behaviour matches Adobe Acrobat, warn and continue, |
807 | | * output file will not be PDF/A compliant |
808 | | */ |
809 | 0 | case 0: |
810 | 0 | emprintf(pdev->memory, |
811 | 0 | "Launch annotations not permitted in PDF/A, reverting to normal PDF output\n"); |
812 | 0 | pdev->AbortPDFAX = true; |
813 | 0 | pdev->PDFA = 0; |
814 | 0 | break; |
815 | | /* Since the annotation would break PDF/A compatibility, do not |
816 | | * include it, but warn the user that it has been dropped. |
817 | | */ |
818 | 0 | case 1: |
819 | 0 | emprintf(pdev->memory, |
820 | 0 | "Launch annotations not permitted in PDF/A, cannot drop annotation, aborting conversion\n"); |
821 | 0 | gs_free_object(pdev->memory->stable_memory, adict, "action dict"); |
822 | 0 | return_error (gs_error_typecheck); |
823 | 0 | break; |
824 | 0 | case 2: |
825 | 0 | emprintf(pdev->memory, |
826 | 0 | "Launch annotations not permitted in PDF/A, aborting conversion\n"); |
827 | 0 | gs_free_object(pdev->memory->stable_memory, adict, "action dict"); |
828 | 0 | return_error (gs_error_typecheck); |
829 | 0 | break; |
830 | 0 | default: |
831 | 0 | emprintf(pdev->memory, |
832 | 0 | "Launch annotations not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n"); |
833 | 0 | pdev->AbortPDFAX = true; |
834 | 0 | pdev->PDFA = 0; |
835 | 0 | break; |
836 | 0 | } |
837 | 0 | } |
838 | 0 | } |
839 | 278 | if (pdf_key_eq(&key, "/Dest") || pdf_key_eq(&key, "/D")) { |
840 | 20 | param_string_from_string(key, "/D"); |
841 | 20 | if (value.data[0] == '(' && pdev->CompatibilityLevel < 1.2) { |
842 | 0 | int i; |
843 | |
|
844 | 0 | for (i = 0;i < value.size; i++) { |
845 | 0 | if (value.data[i] == '\\') { |
846 | 0 | emprintf(pdev->memory, |
847 | 0 | "Link Destination contains characters which cannot be represented in a name.\nDestinations cannot be strings in versions prior to PDF 1.2. Annotation removed in the output.\n"); |
848 | 0 | return gs_error_typecheck; |
849 | 0 | } |
850 | 0 | } |
851 | | /****** FIXME: DETECT ESCAPES ******/ |
852 | 0 | pdfmark_coerce_dest(&value, dest); |
853 | 0 | } |
854 | 258 | } else if (pdf_key_eq(&key, "/File")) |
855 | 0 | param_string_from_string(key, "/F"); |
856 | 258 | else if (pdf_key_eq(&key, "/Subtype")) |
857 | 0 | param_string_from_string(key, "/S"); |
858 | 278 | cos_dict_put_string(adict, key.data, key.size, |
859 | 278 | value.data, value.size); |
860 | 278 | } |
861 | 106 | if (code <= 0 || !pdf_key_eq(&key, ">>")) { |
862 | 0 | cos_free((cos_object_t *)adict, "action dict"); |
863 | 0 | return_error(gs_error_rangecheck); |
864 | 0 | } |
865 | 106 | cos_dict_put(pcd, (const byte *)"/A", 2, |
866 | 106 | COS_OBJECT_VALUE(&avalue, adict)); |
867 | 112 | } else if (pdf_key_eq(Action + 1, "/GoTo")) |
868 | 0 | pdfmark_put_pair(pcd, Action); |
869 | 112 | else if (Action[1].size < 30) { |
870 | | /* Hack: we could substitute names in pdfmark_process, |
871 | | now should recognize whether it was done. |
872 | | Not a perfect method though. |
873 | | Go with it for a while. */ |
874 | 112 | char buf[30]; |
875 | 112 | int d0, d1; |
876 | | |
877 | 112 | if (Action[1].size > 29) |
878 | 0 | return_error(gs_error_rangecheck); |
879 | 112 | memcpy(buf, Action[1].data, Action[1].size); |
880 | 112 | buf[Action[1].size] = 0; |
881 | 112 | if (sscanf(buf, "%d %d R", &d0, &d1) == 2) |
882 | 112 | pdfmark_put_pair(pcd, Action); |
883 | 112 | } |
884 | 218 | } |
885 | | /* |
886 | | * If we have /Dest or /File without the right kind of action, |
887 | | * simply write it at the top level. This doesn't seem right, |
888 | | * but I'm not sure what else to do. |
889 | | */ |
890 | 765 | if (Dest.data) { |
891 | 208 | if (coerce_dest) |
892 | 0 | pdfmark_coerce_dest(&Dest, dest); |
893 | | /* |
894 | | * For PDF 1.2 or better we write a Names tree, but in this case the names |
895 | | * are required to be (counter-intuitively) strings, NOT name objects.... |
896 | | */ |
897 | 208 | if (pdev->CompatibilityLevel > 1.1) { |
898 | 208 | gs_param_string DestString; |
899 | 208 | int i = 0, j; |
900 | 208 | char *D; |
901 | | |
902 | | /* |
903 | | * If the name has any 'unusual' characters, it is 'escaped' by starting with NULLs |
904 | | * I suspect we no longer need that, but here we remove the escaping NULLs |
905 | | */ |
906 | 208 | if (Dest.size > 3 && Dest.data[0] == 0x00 && Dest.data[1] == 0x00 && Dest.data[Dest.size - 1] == 0x00) { |
907 | 0 | i = 2; |
908 | 0 | if (Dest.size > 5 && Dest.data[2] == 0x00 && Dest.data[3] == 0x00) |
909 | 0 | i += 2; |
910 | | /* If it has preceeding NULLs, then it has a terminating NULL as well, get rid of that too */ |
911 | 0 | Dest.size--; |
912 | 0 | } |
913 | | |
914 | 208 | if (Dest.data[i] == '/') { |
915 | 0 | i++; |
916 | |
|
917 | 0 | DestString.data = gs_alloc_bytes(pdev->memory->stable_memory, (Dest.size * 2) + 1, "DEST pdfmark temp string"); |
918 | 0 | if (DestString.data == 0) |
919 | 0 | return_error(gs_error_VMerror); |
920 | 0 | DestString.size = (Dest.size * 2) + 1; |
921 | 0 | DestString.persistent = 0; |
922 | 0 | D = (char *)DestString.data; |
923 | 0 | D[0] = '('; |
924 | 0 | for (j=1;i<Dest.size;i++, j++) { |
925 | 0 | if (Dest.data[i] == '(' || Dest.data[i] == ')') { |
926 | 0 | D[j++] = '\\'; |
927 | 0 | } |
928 | 0 | D[j] = Dest.data[i]; |
929 | 0 | } |
930 | 0 | D[j++] = ')'; |
931 | 0 | DestString.size = j; |
932 | 0 | pdfmark_put_c_pair(pcd, "/Dest", &DestString); |
933 | 0 | gs_free_object(pdev->memory->stable_memory, D, "DEST pdfmark temp string"); |
934 | 0 | } else |
935 | 208 | pdfmark_put_c_pair(pcd, "/Dest", &Dest); |
936 | 208 | } else |
937 | 0 | pdfmark_put_c_pair(pcd, "/Dest", &Dest); |
938 | 557 | } else if (for_outline && !Action) { |
939 | | /* Make an implicit destination. */ |
940 | 4 | char dstr[1 + (sizeof(int64_t) * 8 / 3 + 1) + 25 + 1]; |
941 | 4 | int64_t page_id = pdf_page_id(pdev, pdev->next_page + 1); |
942 | | |
943 | 4 | gs_snprintf(dstr, MAX_DEST_STRING, "[%"PRId64" 0 R /XYZ null null null]", page_id); |
944 | 4 | cos_dict_put_c_key_string(pcd, "/Dest", (const unsigned char*) dstr, |
945 | 4 | strlen(dstr)); |
946 | 4 | } |
947 | 765 | if (File) |
948 | 0 | pdfmark_put_pair(pcd, File); |
949 | 765 | if (Subtype.data) |
950 | 548 | pdfmark_put_c_pair(pcd, "/Subtype", &Subtype); |
951 | 765 | return 0; |
952 | 765 | } |
953 | | |
954 | | /* Copy an annotation dictionary. */ |
955 | | static int |
956 | | pdfmark_annot(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
957 | | const gs_matrix * pctm, const gs_param_string *objname, |
958 | | const char *subtype) |
959 | 548 | { |
960 | 548 | ao_params_t params; |
961 | 548 | cos_dict_t *pcd; |
962 | 548 | int page_index = pdev->next_page; |
963 | 548 | cos_array_t *annots; |
964 | 548 | cos_value_t value; |
965 | 548 | int code; |
966 | | |
967 | | /* Annotations are only permitted in PDF/A if they have the |
968 | | * Print flag enabled, so we need to prescan for that here. |
969 | | */ |
970 | 548 | if(pdev->PDFA != 0) { |
971 | 0 | int i; |
972 | 0 | unsigned int Flags = 0; |
973 | | /* Check all the keys to see if we have a /F (Flags) key/value pair defined */ |
974 | 0 | for (i = 0; i < count; i += 2) { |
975 | 0 | const gs_param_string *pair = &pairs[i]; |
976 | |
|
977 | 0 | if (pdf_key_eq(pair, "/F")) { |
978 | 0 | char Buffer[32]; |
979 | |
|
980 | 0 | pair = &pairs[i+1]; |
981 | 0 | if (pair->size >= sizeof(Buffer)) |
982 | 0 | code = 0; |
983 | 0 | else { |
984 | 0 | memcpy(Buffer, pair->data, pair->size); |
985 | 0 | Buffer[pair->size] = 0x00; |
986 | 0 | code = sscanf(Buffer, "%u", &Flags); |
987 | 0 | } |
988 | 0 | if (code != 1) |
989 | 0 | emprintf(pdev->memory, |
990 | 0 | "Annotation has an invalid /Flags attribute\n"); |
991 | 0 | break; |
992 | 0 | } |
993 | 0 | } |
994 | | /* Check the Print flag, PDF/A annotations *must* be set to print */ |
995 | 0 | if ((Flags & 4) == 0){ |
996 | 0 | switch (pdev->PDFACompatibilityPolicy) { |
997 | | /* Default behaviour matches Adobe Acrobat, warn and continue, |
998 | | * output file will not be PDF/A compliant |
999 | | */ |
1000 | 0 | case 0: |
1001 | 0 | emprintf(pdev->memory, |
1002 | 0 | "Annotation set to non-printing,\n not permitted in PDF/A, reverting to normal PDF output\n"); |
1003 | 0 | pdev->AbortPDFAX = true; |
1004 | 0 | pdev->PDFA = 0; |
1005 | 0 | break; |
1006 | | /* Since the annotation would break PDF/A compatibility, do not |
1007 | | * include it, but warn the user that it has been dropped. |
1008 | | */ |
1009 | 0 | case 1: |
1010 | 0 | emprintf(pdev->memory, |
1011 | 0 | "Annotation set to non-printing,\n not permitted in PDF/A, annotation will not be present in output file\n"); |
1012 | 0 | return 0; |
1013 | 0 | break; |
1014 | 0 | case 2: |
1015 | 0 | emprintf(pdev->memory, |
1016 | 0 | "Annotation set to non-printing,\n not permitted in PDF/A, aborting conversion\n"); |
1017 | 0 | return_error(gs_error_invalidfont); |
1018 | 0 | break; |
1019 | 0 | default: |
1020 | 0 | emprintf(pdev->memory, |
1021 | 0 | "Annotation set to non-printing,\n not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n"); |
1022 | 0 | pdev->AbortPDFAX = true; |
1023 | 0 | pdev->PDFA = 0; |
1024 | 0 | break; |
1025 | 0 | } |
1026 | 0 | } |
1027 | 0 | } |
1028 | 548 | if (pdev->PDFX != 0) { |
1029 | 0 | gs_param_string Subtype; |
1030 | 0 | bool discard = true; |
1031 | 0 | pdf_page_t *page = 0L; |
1032 | |
|
1033 | 0 | page = &pdev->pages[pdev->next_page]; |
1034 | |
|
1035 | 0 | if (subtype) { |
1036 | 0 | param_string_from_string(Subtype, subtype); |
1037 | 0 | if (pdf_key_eq(&Subtype, "/TrapNet") || |
1038 | 0 | pdf_key_eq(&Subtype, "/PrinterMark")) |
1039 | 0 | discard = false; |
1040 | 0 | } |
1041 | 0 | if (discard) { |
1042 | 0 | int i; |
1043 | 0 | for (i = 0; i < count; i += 2) { |
1044 | 0 | const gs_param_string *pair = &pairs[i]; |
1045 | 0 | if (pdf_key_eq(pair, "/Rect")) { |
1046 | 0 | gs_rect rect; |
1047 | 0 | const cos_value_t *v_trimbox, *v_bleedbox, *v_artbox, *v_cropbox; |
1048 | 0 | const byte *p; |
1049 | 0 | char buf[100]; |
1050 | 0 | int size, code; |
1051 | 0 | float temp[4]; /* the type is float for sscanf. */ |
1052 | 0 | double pagebox[4] = {0, 0}; |
1053 | |
|
1054 | 0 | pagebox[2] = pdev->MediaSize[0]; |
1055 | 0 | pagebox[3] = pdev->MediaSize[1]; |
1056 | |
|
1057 | 0 | v_cropbox = v_bleedbox = v_trimbox = v_artbox = 0x0L; |
1058 | |
|
1059 | 0 | code = pdfmark_scan_rect(&rect, pair + 1, pctm); |
1060 | |
|
1061 | 0 | if (code < 0) |
1062 | 0 | return code; |
1063 | | |
1064 | 0 | if (page && page->Page) { |
1065 | 0 | v_trimbox = cos_dict_find_c_key(page->Page, "/TrimBox"); |
1066 | 0 | v_bleedbox = cos_dict_find_c_key(page->Page, "/BleedBox"); |
1067 | 0 | v_artbox = cos_dict_find_c_key(page->Page, "/ArtBox"); |
1068 | 0 | v_cropbox = cos_dict_find_c_key(page->Page, "/CropBox"); |
1069 | 0 | } |
1070 | |
|
1071 | 0 | if (v_cropbox != NULL && v_cropbox->value_type == COS_VALUE_SCALAR) { |
1072 | 0 | p = v_cropbox->contents.chars.data; |
1073 | 0 | size = min (v_cropbox->contents.chars.size, sizeof(buf) - 1); |
1074 | 0 | memcpy(buf, p, size); |
1075 | 0 | buf[size] = 0; |
1076 | 0 | if (sscanf(buf, "[ %g %g %g %g ]", |
1077 | 0 | &temp[0], &temp[1], &temp[2], &temp[3]) == 4) { |
1078 | 0 | if (temp[0] > pagebox[0]) pagebox[0] = temp[0]; |
1079 | 0 | if (temp[1] > pagebox[1]) pagebox[1] = temp[1]; |
1080 | 0 | } |
1081 | 0 | } |
1082 | 0 | if (v_bleedbox != NULL && v_bleedbox->value_type == COS_VALUE_SCALAR) { |
1083 | 0 | p = v_bleedbox->contents.chars.data; |
1084 | 0 | size = min (v_bleedbox->contents.chars.size, sizeof(buf) - 1); |
1085 | 0 | memcpy(buf, p, size); |
1086 | 0 | buf[size] = 0; |
1087 | 0 | if (sscanf(buf, "[ %g %g %g %g ]", |
1088 | 0 | &temp[0], &temp[1], &temp[2], &temp[3]) == 4) { |
1089 | 0 | if (temp[0] > pagebox[0]) pagebox[0] = temp[0]; |
1090 | 0 | if (temp[1] > pagebox[1]) pagebox[1] = temp[1]; |
1091 | 0 | } |
1092 | 0 | } |
1093 | 0 | if (v_trimbox != NULL && v_trimbox->value_type == COS_VALUE_SCALAR) { |
1094 | 0 | p = v_trimbox->contents.chars.data; |
1095 | 0 | size = min (v_trimbox->contents.chars.size, sizeof(buf) - 1); |
1096 | 0 | memcpy(buf, p, size); |
1097 | 0 | buf[size] = 0; |
1098 | 0 | if (sscanf(buf, "[ %g %g %g %g ]", |
1099 | 0 | &temp[0], &temp[1], &temp[2], &temp[3]) == 4) { |
1100 | 0 | if (temp[0] > pagebox[0]) pagebox[0] = temp[0]; |
1101 | 0 | if (temp[1] > pagebox[1]) pagebox[1] = temp[1]; |
1102 | 0 | } |
1103 | 0 | } |
1104 | 0 | if (v_artbox != NULL && v_artbox->value_type == COS_VALUE_SCALAR) { |
1105 | 0 | p = v_artbox->contents.chars.data; |
1106 | 0 | size = min (v_artbox->contents.chars.size, sizeof(buf) - 1); |
1107 | 0 | memcpy(buf, p, size); |
1108 | 0 | buf[size] = 0; |
1109 | 0 | if (sscanf(buf, "[ %g %g %g %g ]", |
1110 | 0 | &temp[0], &temp[1], &temp[2], &temp[3]) == 4) { |
1111 | 0 | if (temp[0] > pagebox[0]) pagebox[0] = temp[0]; |
1112 | 0 | if (temp[1] > pagebox[1]) pagebox[1] = temp[1]; |
1113 | 0 | } |
1114 | 0 | } |
1115 | 0 | if (v_cropbox == 0 && v_trimbox == 0 && v_artbox == 0 && v_bleedbox == 0) { |
1116 | 0 | if (pdev->PDFXTrimBoxToMediaBoxOffset.size >= 4 && |
1117 | 0 | pdev->PDFXTrimBoxToMediaBoxOffset.data[0] >= 0 && |
1118 | 0 | pdev->PDFXTrimBoxToMediaBoxOffset.data[1] >= 0 && |
1119 | 0 | pdev->PDFXTrimBoxToMediaBoxOffset.data[2] >= 0 && |
1120 | 0 | pdev->PDFXTrimBoxToMediaBoxOffset.data[3] >= 0) { |
1121 | 0 | pagebox[0] += pdev->PDFXTrimBoxToMediaBoxOffset.data[0]; |
1122 | 0 | pagebox[1] += pdev->PDFXTrimBoxToMediaBoxOffset.data[3]; |
1123 | 0 | pagebox[2] -= pdev->PDFXTrimBoxToMediaBoxOffset.data[1]; |
1124 | 0 | pagebox[3] -= pdev->PDFXTrimBoxToMediaBoxOffset.data[2]; |
1125 | 0 | } else if (pdev->PDFXBleedBoxToTrimBoxOffset.size >= 4 && |
1126 | 0 | pdev->PDFXBleedBoxToTrimBoxOffset.data[0] >= 0 && |
1127 | 0 | pdev->PDFXBleedBoxToTrimBoxOffset.data[1] >= 0 && |
1128 | 0 | pdev->PDFXBleedBoxToTrimBoxOffset.data[2] >= 0 && |
1129 | 0 | pdev->PDFXBleedBoxToTrimBoxOffset.data[3] >= 0) { |
1130 | 0 | pagebox[0] -= pdev->PDFXBleedBoxToTrimBoxOffset.data[0]; |
1131 | 0 | pagebox[1] -= pdev->PDFXBleedBoxToTrimBoxOffset.data[3]; |
1132 | 0 | pagebox[2] += pdev->PDFXBleedBoxToTrimBoxOffset.data[1]; |
1133 | 0 | pagebox[3] += pdev->PDFXBleedBoxToTrimBoxOffset.data[2]; |
1134 | 0 | } |
1135 | 0 | } |
1136 | |
|
1137 | 0 | if (rect.p.x > pagebox[2] || rect.q.x < pagebox[0] || |
1138 | 0 | rect.p.y > pagebox[3] || rect.q.y < pagebox[1]) |
1139 | 0 | break; |
1140 | 0 | switch (pdev->PDFACompatibilityPolicy) { |
1141 | | /* Default behaviour matches Adobe Acrobat, warn and continue, |
1142 | | * output file will not be PDF/A compliant |
1143 | | */ |
1144 | 0 | case 0: |
1145 | 0 | emprintf(pdev->memory, |
1146 | 0 | "Annotation (not TrapNet or PrinterMark) on page,\n not permitted in PDF/X, reverting to normal PDF output\n"); |
1147 | 0 | pdev->AbortPDFAX = true; |
1148 | 0 | pdev->PDFX = 0; |
1149 | 0 | break; |
1150 | | /* Since the annotation would break PDF/A compatibility, do not |
1151 | | * include it, but warn the user that it has been dropped. |
1152 | | */ |
1153 | 0 | case 1: |
1154 | 0 | emprintf(pdev->memory, |
1155 | 0 | "Annotation (not TrapNet or PrinterMark) on page,\n not permitted in PDF/X, annotation will not be present in output file\n"); |
1156 | 0 | return 0; |
1157 | 0 | break; |
1158 | 0 | case 2: |
1159 | 0 | emprintf(pdev->memory, |
1160 | 0 | "Annotation (not TrapNet or PrinterMark) on page,\n not permitted in PDF/X, aborting conversion\n"); |
1161 | 0 | return_error(gs_error_invalidfont); |
1162 | 0 | break; |
1163 | 0 | default: |
1164 | 0 | emprintf(pdev->memory, |
1165 | 0 | "Annotation s(not TrapNet or PrinterMark) on page,\n not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n"); |
1166 | 0 | pdev->AbortPDFAX = true; |
1167 | 0 | pdev->PDFX = 0; |
1168 | 0 | break; |
1169 | 0 | } |
1170 | 0 | break; |
1171 | 0 | } |
1172 | 0 | } |
1173 | 0 | if (i > count) { |
1174 | 0 | switch (pdev->PDFACompatibilityPolicy) { |
1175 | | /* Default behaviour matches Adobe Acrobat, warn and continue, |
1176 | | * output file will not be PDF/X compliant |
1177 | | */ |
1178 | 0 | case 0: |
1179 | 0 | emprintf(pdev->memory, |
1180 | 0 | "Annotation (not TrapNet or PrinterMark) potentially on page (no /Rect in dict),\n not permitted in PDF/X, reverting to normal PDF output\n"); |
1181 | 0 | pdev->AbortPDFAX = true; |
1182 | 0 | pdev->PDFX = 0; |
1183 | 0 | break; |
1184 | | /* Since the annotation would break PDF/X compatibility, do not |
1185 | | * include it, but warn the user that it has been dropped. |
1186 | | */ |
1187 | 0 | case 1: |
1188 | 0 | emprintf(pdev->memory, |
1189 | 0 | "Annotation (not TrapNet or PrinterMark) potentially on page (no /Rect in dict),\n not permitted in PDF/X, annotation will not be present in output file\n"); |
1190 | 0 | return 0; |
1191 | 0 | break; |
1192 | 0 | case 2: |
1193 | 0 | emprintf(pdev->memory, |
1194 | 0 | "Annotation (not TrapNet or PrinterMark) potentially on page (no /Rect in dict),\n not permitted in PDF/X, aborting conversion\n"); |
1195 | 0 | return_error(gs_error_invalidfont); |
1196 | 0 | break; |
1197 | 0 | default: |
1198 | 0 | emprintf(pdev->memory, |
1199 | 0 | "Annotation s(not TrapNet or PrinterMark) potentially on page (no /Rect in dict),\n not permitted in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n"); |
1200 | 0 | pdev->AbortPDFAX = true; |
1201 | 0 | pdev->PDFX = 0; |
1202 | 0 | break; |
1203 | 0 | } |
1204 | 0 | } |
1205 | 0 | } |
1206 | 0 | } |
1207 | 548 | params.pdev = pdev; |
1208 | 548 | params.subtype = subtype; |
1209 | 548 | params.src_pg = -1; |
1210 | 548 | code = pdf_make_named_dict(pdev, objname, &pcd, true); |
1211 | 548 | if (code < 0) |
1212 | 0 | return code; |
1213 | 548 | code = cos_dict_put_c_strings(pcd, "/Type", "/Annot"); |
1214 | 548 | if (code < 0) { |
1215 | 0 | (void)pdf_obj_mark_unused(pdev, pcd->id); |
1216 | 0 | cos_free((cos_object_t *)pcd, "pdfmark_annot"); |
1217 | 0 | return code; |
1218 | 0 | } |
1219 | 548 | code = pdfmark_put_ao_pairs(pdev, pcd, pairs, count, pctm, ¶ms, false); |
1220 | 548 | if (code < 0) { |
1221 | 0 | (void)pdf_obj_mark_unused(pdev, pcd->id); |
1222 | 0 | cos_free((cos_object_t *)pcd, "pdfmark_annot"); |
1223 | 0 | return code; |
1224 | 0 | } |
1225 | 548 | if (params.src_pg >= 0) |
1226 | 0 | page_index = params.src_pg; |
1227 | 548 | if (pdf_page_id(pdev, page_index + 1) <= 0) { |
1228 | 0 | (void)pdf_obj_mark_unused(pdev, pcd->id); |
1229 | 0 | cos_free((cos_object_t *)pcd, "pdfmark_annot"); |
1230 | 0 | return_error(gs_error_rangecheck); |
1231 | 0 | } |
1232 | 548 | annots = pdev->pages[page_index].Annots; |
1233 | 548 | if (annots == 0) { |
1234 | 274 | annots = cos_array_alloc(pdev, "pdfmark_annot"); |
1235 | 274 | if (annots == 0) { |
1236 | 0 | (void)pdf_obj_mark_unused(pdev, pcd->id); |
1237 | 0 | cos_free((cos_object_t *)pcd, "pdfmark_annot"); |
1238 | 0 | return_error(gs_error_VMerror); |
1239 | 0 | } |
1240 | 274 | pdev->pages[page_index].Annots = annots; |
1241 | 274 | } |
1242 | 548 | if (!objname) { |
1243 | | /* Write the annotation now. */ |
1244 | 548 | COS_WRITE_OBJECT(pcd, pdev, resourceAnnotation); |
1245 | 548 | COS_RELEASE(pcd, "pdfmark_annot"); |
1246 | 548 | } |
1247 | 548 | return cos_array_add(annots, |
1248 | 548 | cos_object_value(&value, COS_OBJECT(pcd))); |
1249 | 548 | } |
1250 | | |
1251 | | /* ANN pdfmark */ |
1252 | | static int |
1253 | | pdfmark_ANN(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
1254 | | const gs_matrix * pctm, const gs_param_string * objname) |
1255 | 335 | { |
1256 | 335 | return pdfmark_annot(pdev, pairs, count, pctm, objname, "/Text"); |
1257 | 335 | } |
1258 | | |
1259 | | /* LNK pdfmark (obsolescent) */ |
1260 | | static int |
1261 | | pdfmark_LNK(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
1262 | | const gs_matrix * pctm, const gs_param_string * objname) |
1263 | 213 | { |
1264 | 213 | return pdfmark_annot(pdev, pairs, count, pctm, objname, "/Link"); |
1265 | 213 | } |
1266 | | |
1267 | | /* Write and release one node of the outline tree. */ |
1268 | | static int |
1269 | | pdfmark_write_outline(gx_device_pdf * pdev, pdf_outline_node_t * pnode, |
1270 | | int64_t next_id) |
1271 | 217 | { |
1272 | 217 | stream *s; |
1273 | 217 | int code = 0; |
1274 | | |
1275 | 217 | pdf_open_separate(pdev, pnode->id, resourceOutline); |
1276 | 217 | if (pnode->action != NULL) |
1277 | 217 | pnode->action->id = pnode->id; |
1278 | 0 | else { |
1279 | 0 | emprintf1(pdev->memory, |
1280 | 0 | "pdfmark error: Outline node %ld has no action or destination.\n", |
1281 | 0 | (unsigned long)pnode->id); |
1282 | 0 | code = gs_note_error(gs_error_undefined); |
1283 | 0 | } |
1284 | 217 | s = pdev->strm; |
1285 | 217 | stream_puts(s, "<< "); |
1286 | 217 | if (pnode->action != NULL) |
1287 | 217 | cos_dict_elements_write(pnode->action, pdev); |
1288 | 217 | if (pnode->count) |
1289 | 35 | pprintd1(s, "/Count %d ", pnode->count); |
1290 | 217 | pprinti64d1(s, "/Parent %"PRId64" 0 R\n", pnode->parent_id); |
1291 | 217 | if (pnode->prev_id) |
1292 | 44 | pprinti64d1(s, "/Prev %"PRId64" 0 R\n", pnode->prev_id); |
1293 | 217 | if (next_id) |
1294 | 44 | pprinti64d1(s, "/Next %"PRId64" 0 R\n", next_id); |
1295 | 217 | if (pnode->first_id) |
1296 | 33 | pprinti64d2(s, "/First %"PRId64" 0 R /Last %"PRId64" 0 R\n", |
1297 | 33 | pnode->first_id, pnode->last_id); |
1298 | 217 | stream_puts(s, ">>\n"); |
1299 | 217 | pdf_end_separate(pdev, resourceOutline); |
1300 | 217 | if (pnode->action != NULL) |
1301 | 217 | COS_FREE(pnode->action, "pdfmark_write_outline"); |
1302 | 217 | pnode->action = 0; |
1303 | 217 | return code; |
1304 | 217 | } |
1305 | | |
1306 | | /* Adjust the parent's count when writing an outline node. */ |
1307 | | static void |
1308 | | pdfmark_adjust_parent_count(pdf_outline_level_t * plevel) |
1309 | 59 | { |
1310 | 59 | pdf_outline_level_t *parent = plevel - 1; |
1311 | 59 | int count = plevel->last.count; |
1312 | | |
1313 | 59 | if (count > 0) { |
1314 | 0 | if (parent->last.count < 0) |
1315 | 0 | parent->last.count -= count; |
1316 | 0 | else |
1317 | 0 | parent->last.count += count; |
1318 | 0 | } |
1319 | 59 | } |
1320 | | |
1321 | | /* |
1322 | | * Close the current level of the outline tree. Note that if we are at |
1323 | | * the end of the document, some of the levels may be incomplete if the |
1324 | | * Count values were incorrect. |
1325 | | */ |
1326 | | int |
1327 | | pdfmark_close_outline(gx_device_pdf * pdev) |
1328 | 175 | { |
1329 | 175 | int depth = pdev->outline_depth; |
1330 | 175 | pdf_outline_level_t *plevel = &pdev->outline_levels[depth]; |
1331 | 175 | int code = 0; |
1332 | | |
1333 | 175 | if (plevel->last.id) { /* check for incomplete tree */ |
1334 | 173 | code = pdfmark_write_outline(pdev, &plevel->last, 0); |
1335 | 173 | } |
1336 | 175 | if (depth > 0) { |
1337 | 35 | plevel[-1].last.last_id = plevel->last.id; |
1338 | 35 | pdfmark_adjust_parent_count(plevel); |
1339 | 35 | --plevel; |
1340 | 35 | if (plevel->last.count < 0) |
1341 | 3 | pdev->closed_outline_depth--; |
1342 | 35 | pdev->outline_depth--; |
1343 | 35 | } |
1344 | 175 | return code; |
1345 | 175 | } |
1346 | | |
1347 | | /* OUT pdfmark */ |
1348 | | static int |
1349 | | pdfmark_OUT(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
1350 | | const gs_matrix * pctm, const gs_param_string * no_objname) |
1351 | 217 | { |
1352 | 217 | int depth = pdev->outline_depth; |
1353 | 217 | pdf_outline_level_t *plevel = &pdev->outline_levels[depth]; |
1354 | 217 | int sub_count = 0; |
1355 | 217 | uint i; |
1356 | 217 | pdf_outline_node_t node; |
1357 | 217 | ao_params_t ao; |
1358 | 217 | int code; |
1359 | | |
1360 | 1.10k | for (i = 0; i < count; i += 2) { |
1361 | 883 | const gs_param_string *pair = &pairs[i]; |
1362 | | |
1363 | 883 | if (pdf_key_eq(pair, "/Count")) |
1364 | 217 | pdfmark_scan_int(pair + 1, &sub_count); |
1365 | 883 | } |
1366 | 217 | if (sub_count != 0 && depth == pdev->max_outline_depth - 1) { |
1367 | 0 | pdf_outline_level_t *new_ptr; |
1368 | |
|
1369 | 0 | new_ptr = (pdf_outline_level_t *)gs_alloc_bytes(pdev->pdf_memory, (pdev->max_outline_depth + INITIAL_MAX_OUTLINE_DEPTH) * sizeof(pdf_outline_level_t) * sizeof(pdf_outline_level_t), "outline_levels array"); |
1370 | 0 | if (!new_ptr) |
1371 | 0 | return_error(gs_error_VMerror); |
1372 | 0 | memcpy (new_ptr, pdev->outline_levels, pdev->max_outline_depth * sizeof(pdf_outline_level_t)); |
1373 | 0 | gs_free_object(pdev->pdf_memory, pdev->outline_levels, "outline_levels array"); |
1374 | 0 | pdev->outline_levels = new_ptr; |
1375 | 0 | pdev->max_outline_depth += INITIAL_MAX_OUTLINE_DEPTH; |
1376 | 0 | plevel = &pdev->outline_levels[depth]; |
1377 | 0 | } |
1378 | 217 | node.action = cos_dict_alloc(pdev, "pdfmark_OUT"); |
1379 | 217 | if (node.action == 0) |
1380 | 0 | return_error(gs_error_VMerror); |
1381 | 217 | ao.pdev = pdev; |
1382 | 217 | ao.subtype = 0; |
1383 | 217 | ao.src_pg = -1; |
1384 | 217 | code = pdfmark_put_ao_pairs(pdev, node.action, pairs, count, pctm, &ao, |
1385 | 217 | true); |
1386 | 217 | if (code < 0) { |
1387 | 0 | cos_free((cos_object_t *)node.action, "pdfmark_OUT"); |
1388 | 0 | return code; |
1389 | 0 | } |
1390 | 217 | if (pdev->outlines_id == 0) |
1391 | 140 | pdev->outlines_id = pdf_obj_ref(pdev); |
1392 | 217 | node.id = pdf_obj_ref(pdev); |
1393 | 217 | node.parent_id = |
1394 | 217 | (depth == 0 ? pdev->outlines_id : plevel[-1].last.id); |
1395 | 217 | node.prev_id = plevel->last.id; |
1396 | 217 | node.first_id = node.last_id = 0; |
1397 | 217 | node.count = sub_count; |
1398 | | /* Add this node to the outline at the current level. */ |
1399 | 217 | if (plevel->first.id == 0) { /* First node at this level. */ |
1400 | 173 | if (depth > 0) |
1401 | 33 | plevel[-1].last.first_id = node.id; |
1402 | 173 | node.prev_id = 0; |
1403 | 173 | plevel->first = node; |
1404 | 173 | plevel->first.action = 0; /* never used */ |
1405 | 173 | } else { /* Write the previous node. */ |
1406 | 44 | if (depth > 0) |
1407 | 24 | pdfmark_adjust_parent_count(plevel); |
1408 | 44 | pdfmark_write_outline(pdev, &plevel->last, node.id); |
1409 | 44 | } |
1410 | 217 | plevel->last = node; |
1411 | 217 | plevel->left--; |
1412 | 217 | if (!pdev->closed_outline_depth) |
1413 | 214 | pdev->outlines_open++; |
1414 | | /* If this node has sub-nodes, descend one level. */ |
1415 | 217 | if (sub_count != 0) { |
1416 | 35 | pdev->outline_depth++; |
1417 | 35 | ++plevel; |
1418 | 35 | plevel->left = (sub_count > 0 ? sub_count : -sub_count); |
1419 | 35 | plevel->first.id = 0; |
1420 | 35 | plevel->last.count = plevel->last.id = 0; |
1421 | 35 | plevel->first.action = plevel->last.action = 0; /* for GC */ |
1422 | 35 | if (sub_count < 0) |
1423 | 3 | pdev->closed_outline_depth++; |
1424 | 182 | } else { |
1425 | 212 | while ((depth = pdev->outline_depth) > 0 && |
1426 | 212 | pdev->outline_levels[depth].left == 0 |
1427 | 182 | ) |
1428 | 30 | pdfmark_close_outline(pdev); |
1429 | 182 | } |
1430 | 217 | return 0; |
1431 | 217 | } |
1432 | | |
1433 | | /* Write an article bead. */ |
1434 | | static int |
1435 | | pdfmark_write_bead(gx_device_pdf * pdev, const pdf_bead_t * pbead) |
1436 | 0 | { |
1437 | 0 | stream *s; |
1438 | 0 | char rstr[MAX_RECT_STRING]; |
1439 | |
|
1440 | 0 | pdf_open_separate(pdev, pbead->id, resourceArticle); |
1441 | 0 | s = pdev->strm; |
1442 | 0 | pprinti64d3(s, "<</T %"PRId64" 0 R/V %"PRId64" 0 R/N %"PRId64" 0 R", |
1443 | 0 | pbead->article_id, pbead->prev_id, pbead->next_id); |
1444 | 0 | if (pbead->page_id != 0) |
1445 | 0 | pprinti64d1(s, "/P %"PRId64" 0 R", pbead->page_id); |
1446 | 0 | pdfmark_make_rect(rstr, &pbead->rect); |
1447 | 0 | pprints1(s, "/R%s>>\n", rstr); |
1448 | 0 | return pdf_end_separate(pdev, resourceArticle); |
1449 | 0 | } |
1450 | | |
1451 | | /* Finish writing an article, and release its data. */ |
1452 | | int |
1453 | | pdfmark_write_article(gx_device_pdf * pdev, const pdf_article_t * part) |
1454 | 0 | { |
1455 | 0 | pdf_article_t art; |
1456 | 0 | stream *s; |
1457 | |
|
1458 | 0 | art = *part; |
1459 | 0 | if (art.last.id == 0) { |
1460 | | /* Only one bead in the article. */ |
1461 | 0 | art.first.prev_id = art.first.next_id = art.first.id; |
1462 | 0 | } else { |
1463 | | /* More than one bead in the article. */ |
1464 | 0 | art.first.prev_id = art.last.id; |
1465 | 0 | art.last.next_id = art.first.id; |
1466 | 0 | pdfmark_write_bead(pdev, &art.last); |
1467 | 0 | } |
1468 | 0 | pdfmark_write_bead(pdev, &art.first); |
1469 | 0 | pdf_open_separate(pdev, art.contents->id, resourceArticle); |
1470 | 0 | s = pdev->strm; |
1471 | 0 | pprinti64d1(s, "<</F %"PRId64" 0 R/I<<", art.first.id); |
1472 | 0 | cos_dict_elements_write(art.contents, pdev); |
1473 | 0 | stream_puts(s, ">> >>\n"); |
1474 | 0 | return pdf_end_separate(pdev, resourceArticle); |
1475 | 0 | } |
1476 | | |
1477 | | /* ARTICLE pdfmark */ |
1478 | | static int |
1479 | | pdfmark_ARTICLE(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
1480 | | const gs_matrix * pctm, const gs_param_string * no_objname) |
1481 | 0 | { |
1482 | 0 | gs_memory_t *mem = pdev->pdf_memory; |
1483 | 0 | gs_param_string title; |
1484 | 0 | gs_param_string rectstr; |
1485 | 0 | gs_rect rect; |
1486 | 0 | int64_t bead_id; |
1487 | 0 | pdf_article_t *part; |
1488 | 0 | int code; |
1489 | |
|
1490 | 0 | if (!pdfmark_find_key("/Title", pairs, count, &title) || |
1491 | 0 | !pdfmark_find_key("/Rect", pairs, count, &rectstr) |
1492 | 0 | ) |
1493 | 0 | return_error(gs_error_rangecheck); |
1494 | 0 | if ((code = pdfmark_scan_rect(&rect, &rectstr, pctm)) < 0) |
1495 | 0 | return code; |
1496 | 0 | bead_id = pdf_obj_ref(pdev); |
1497 | | |
1498 | | /* Find the article with this title, or create one. */ |
1499 | 0 | for (part = pdev->articles; part != 0; part = part->next) { |
1500 | 0 | const cos_value_t *a_title = |
1501 | 0 | cos_dict_find_c_key(part->contents, "/Title"); |
1502 | |
|
1503 | 0 | if (a_title != 0 && !COS_VALUE_IS_OBJECT(a_title) && |
1504 | 0 | !bytes_compare(a_title->contents.chars.data, |
1505 | 0 | a_title->contents.chars.size, |
1506 | 0 | title.data, title.size)) |
1507 | 0 | break; |
1508 | 0 | } |
1509 | 0 | if (part == 0) { /* Create the article. */ |
1510 | 0 | cos_dict_t *contents = |
1511 | 0 | cos_dict_alloc(pdev, "pdfmark_ARTICLE(contents)"); |
1512 | |
|
1513 | 0 | if (contents == 0) |
1514 | 0 | return_error(gs_error_VMerror); |
1515 | 0 | part = gs_alloc_struct(mem, pdf_article_t, &st_pdf_article, |
1516 | 0 | "pdfmark_ARTICLE(article)"); |
1517 | 0 | if (part == 0 || contents == 0) { |
1518 | 0 | gs_free_object(mem, part, "pdfmark_ARTICLE(article)"); |
1519 | 0 | if (contents) |
1520 | 0 | COS_FREE(contents, "pdfmark_ARTICLE(contents)"); |
1521 | 0 | return_error(gs_error_VMerror); |
1522 | 0 | } |
1523 | 0 | contents->id = pdf_obj_ref(pdev); |
1524 | 0 | part->next = pdev->articles; |
1525 | 0 | pdev->articles = part; |
1526 | 0 | cos_dict_put_string(contents, (const byte *)"/Title", 6, |
1527 | 0 | title.data, title.size); |
1528 | 0 | part->first.id = part->last.id = 0; |
1529 | 0 | part->contents = contents; |
1530 | 0 | } |
1531 | | /* |
1532 | | * Add the bead to the article. This is similar to what we do for |
1533 | | * outline nodes, except that articles have only a page number and |
1534 | | * not View/Dest. |
1535 | | */ |
1536 | 0 | if (part->last.id == 0) { |
1537 | 0 | part->first.next_id = bead_id; |
1538 | 0 | part->last.id = part->first.id; |
1539 | 0 | } else { |
1540 | 0 | part->last.next_id = bead_id; |
1541 | 0 | pdfmark_write_bead(pdev, &part->last); |
1542 | 0 | } |
1543 | 0 | part->last.prev_id = part->last.id; |
1544 | 0 | part->last.id = bead_id; |
1545 | 0 | part->last.article_id = part->contents->id; |
1546 | 0 | part->last.next_id = 0; |
1547 | 0 | part->last.rect = rect; |
1548 | 0 | { |
1549 | 0 | gs_param_string page_string; |
1550 | 0 | int page = 0; |
1551 | 0 | uint i; |
1552 | |
|
1553 | 0 | pdfmark_find_key("/Page", pairs, count, &page_string); |
1554 | 0 | page = pdfmark_page_number(pdev, &page_string); |
1555 | 0 | code = update_max_page_reference(pdev, &page); |
1556 | 0 | if (code < 0) |
1557 | 0 | return code; |
1558 | 0 | part->last.page_id = pdf_page_id(pdev, page); |
1559 | 0 | for (i = 0; i < count; i += 2) { |
1560 | 0 | if (pdf_key_eq(&pairs[i], "/Rect") || pdf_key_eq(&pairs[i], "/Page")) |
1561 | 0 | continue; |
1562 | 0 | pdfmark_put_pair(part->contents, &pairs[i]); |
1563 | 0 | } |
1564 | 0 | } |
1565 | 0 | if (part->first.id == 0) { /* This is the first bead of the article. */ |
1566 | 0 | part->first = part->last; |
1567 | 0 | part->last.id = 0; |
1568 | 0 | } |
1569 | 0 | return 0; |
1570 | 0 | } |
1571 | | |
1572 | | /* EMBED pdfmark */ |
1573 | | static int |
1574 | | pdfmark_EMBED(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
1575 | | const gs_matrix * pctm, const gs_param_string * objname) |
1576 | 17 | { |
1577 | 17 | gs_param_string key; |
1578 | 17 | int i; |
1579 | | |
1580 | 17 | if (pdev->CompatibilityLevel < 1.4) |
1581 | 0 | return_error(gs_error_undefined); |
1582 | 17 | if (pdev->PDFA > 0 && pdev->PDFA < 2) { |
1583 | 0 | switch(pdev->PDFACompatibilityPolicy) { |
1584 | 0 | default: |
1585 | 0 | case 0: |
1586 | 0 | emprintf(pdev->memory, |
1587 | 0 | "The PDF/A-1 specifcation prohibits the embedding of files, reverting to normal PDF output.\n"); |
1588 | 0 | pdev->AbortPDFAX = true; |
1589 | 0 | pdev->PDFA = 0; |
1590 | 0 | return 0; |
1591 | 0 | case 1: |
1592 | 0 | emprintf(pdev->memory, |
1593 | 0 | "The PDF/A-1 specifcation prohibits the embedding of files, pdfamrk operatoin ignored.\n"); |
1594 | 0 | break; |
1595 | 0 | case 2: |
1596 | 0 | return_error(gs_error_undefined); |
1597 | 0 | } |
1598 | 0 | } |
1599 | 17 | if (pdev->PDFA > 0 && pdev->PDFA < 3) { |
1600 | 0 | emprintf(pdev->memory, |
1601 | 0 | "The PDF/A-2 specifcation only permits the embedding of PDF/A-1 or PDF/A-2 files.\n"); |
1602 | 0 | emprintf(pdev->memory, |
1603 | 0 | "The pdfwrite device has not validated this embedded file, output may not conform to PDF/A-2.\n"); |
1604 | 0 | } |
1605 | 17 | if (!pdfmark_find_key("/FS", pairs, count, &key)) |
1606 | 0 | return_error(gs_error_rangecheck); |
1607 | 17 | if (!pdfmark_find_key("/Name", pairs, count, &key)) |
1608 | 0 | return_error(gs_error_rangecheck); |
1609 | 17 | if (!pdev->EmbeddedFiles) { |
1610 | 17 | pdev->EmbeddedFiles = cos_dict_alloc(pdev, "pdfmark_EMBED(EmbeddedFiles)"); |
1611 | 17 | if (pdev->EmbeddedFiles == 0) |
1612 | 0 | return_error(gs_error_VMerror); |
1613 | 17 | pdev->EmbeddedFiles->id = pdf_obj_ref(pdev); |
1614 | 17 | } |
1615 | 17 | if (pdev->PDFA == 3 && !pdev->AF && !cos_dict_find_c_key(pdev->Catalog, "/AF")) { |
1616 | 0 | pdev->AF = cos_array_alloc(pdev, "pdfmark_EMBED(EmbeddedFiles)"); |
1617 | 0 | if (pdev->AF == 0) |
1618 | 0 | return_error(gs_error_VMerror); |
1619 | 0 | pdev->AF->id = pdf_obj_ref(pdev); |
1620 | 0 | } |
1621 | | |
1622 | 34 | for (i = 0; i < count; i += 2) { |
1623 | 34 | if (pdf_key_eq(&pairs[i], "/FS")) { |
1624 | 17 | if (pdev->PDFA == 3 && !cos_dict_find_c_key(pdev->Catalog, "/AF")) { |
1625 | 0 | uint written; |
1626 | 0 | cos_value_t v; |
1627 | 0 | cos_object_t *object; |
1628 | 0 | int64_t id; |
1629 | 0 | int code; |
1630 | 0 | char *p = (char *)pairs[i+1].data; |
1631 | | |
1632 | | /* Skip past white space */ |
1633 | 0 | while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') |
1634 | 0 | p++; |
1635 | | |
1636 | | /* If we have a dictionary, then we assume this is a 'normal' /EMBED pdfmark */ |
1637 | 0 | if (*p == '<') { |
1638 | | /* Write the dictionary to an object in the PDF file, and note the id we are |
1639 | | * using. We need that below to write references to the object from the /EmbeddedFiles |
1640 | | * entry in the Catalog dictionary and the /AF array, also in the Catalog dictionary. |
1641 | | */ |
1642 | 0 | id = pdf_obj_ref(pdev); |
1643 | |
|
1644 | 0 | pdf_open_separate(pdev, id, resourceNone); |
1645 | 0 | sputs(pdev->strm, pairs[i+1].data, pairs[i+1].size, &written); |
1646 | 0 | if (pairs[i+1].data[pairs[i+1].size] != 0x0d && pdev->PDFA != 0) |
1647 | 0 | sputc(pdev->strm, 0x0d); |
1648 | 0 | pdf_end_separate(pdev, resourceNone); |
1649 | 0 | } else { |
1650 | | /* Otherwise, try to determine if we have an indirect reference to an existing FileSpec dictionary |
1651 | | * If we get what looks like an indirect reference, try using the id for the /EmbeddedFiles |
1652 | | * and /AF entries |
1653 | | */ |
1654 | 0 | if (sscanf(p, "%"PRId64" 0 R", &id) != 0) |
1655 | 0 | return_error(gs_error_syntaxerror); |
1656 | 0 | } |
1657 | | |
1658 | 0 | object = cos_reference_alloc(pdev, "embedded file"); |
1659 | 0 | if (object == NULL) |
1660 | 0 | return_error(gs_error_VMerror); |
1661 | 0 | object->id = id; |
1662 | 0 | COS_OBJECT_VALUE(&v, object); |
1663 | 0 | code = cos_dict_put(pdev->EmbeddedFiles, key.data, key.size, &v); |
1664 | 0 | if (code < 0) |
1665 | 0 | return code; |
1666 | | |
1667 | 0 | object = cos_reference_alloc(pdev, "embedded file"); |
1668 | 0 | if (object == NULL) |
1669 | 0 | return_error(gs_error_VMerror); |
1670 | 0 | object->id = id; |
1671 | 0 | COS_OBJECT_VALUE(&v, object); |
1672 | 0 | code = cos_array_add(pdev->AF, &v); |
1673 | 0 | if (code < 0) |
1674 | 0 | return code; |
1675 | 0 | } |
1676 | 17 | else |
1677 | 17 | return cos_dict_put_string(pdev->EmbeddedFiles, key.data, key.size, pairs[i+1].data, pairs[i+1].size); |
1678 | 17 | } |
1679 | 34 | } |
1680 | 0 | return 0; |
1681 | 17 | } |
1682 | | |
1683 | | /* DEST pdfmark */ |
1684 | | static int |
1685 | | pdfmark_DEST(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
1686 | | const gs_matrix * pctm, const gs_param_string * objname) |
1687 | 0 | { |
1688 | 0 | int present; |
1689 | 0 | char dest[MAX_DEST_STRING]; |
1690 | 0 | gs_param_string key; |
1691 | 0 | cos_value_t value; |
1692 | 0 | cos_dict_t *ddict; |
1693 | 0 | int i, code; |
1694 | |
|
1695 | 0 | if (!pdfmark_find_key("/Dest", pairs, count, &key) || |
1696 | 0 | (present = |
1697 | 0 | pdfmark_make_dest(dest, pdev, "/Page", "/View", pairs, count, 1)) < 0 |
1698 | 0 | ) |
1699 | 0 | return_error(gs_error_rangecheck); |
1700 | 0 | cos_string_value(&value, (byte *)dest, strlen(dest)); |
1701 | 0 | if (!pdev->Dests) { |
1702 | 0 | pdev->Dests = cos_dict_alloc(pdev, "pdfmark_DEST(Dests)"); |
1703 | 0 | if (pdev->Dests == 0) |
1704 | 0 | return_error(gs_error_VMerror); |
1705 | 0 | pdev->Dests->id = pdf_obj_ref(pdev); |
1706 | 0 | } |
1707 | | |
1708 | | /* |
1709 | | * Create the destination as a dictionary with a D key. |
1710 | | */ |
1711 | 0 | code = pdf_make_named_dict(pdev, objname, &ddict, false); |
1712 | 0 | ddict->id = pdf_obj_ref(pdev); |
1713 | |
|
1714 | 0 | if (code < 0) |
1715 | 0 | return code; |
1716 | 0 | code = cos_dict_put_c_key_string(ddict, "/D", (byte *)dest, |
1717 | 0 | strlen(dest)); |
1718 | 0 | for (i = 0; code >= 0 && i < count; i += 2) |
1719 | 0 | if (!pdf_key_eq(&pairs[i], "/Dest") && |
1720 | 0 | !pdf_key_eq(&pairs[i], "/Page") && |
1721 | 0 | !pdf_key_eq(&pairs[i], "/View") |
1722 | 0 | ) |
1723 | 0 | code = pdfmark_put_pair(ddict, &pairs[i]); |
1724 | 0 | if (code < 0) |
1725 | 0 | return code; |
1726 | 0 | COS_WRITE_OBJECT(ddict, pdev, resourceOther); |
1727 | 0 | COS_OBJECT_VALUE(&value, ddict); |
1728 | 0 | COS_RELEASE(ddict, "pdfmark_DEST(Dests dict)"); |
1729 | |
|
1730 | 0 | return cos_dict_put(pdev->Dests, key.data, key.size, &value); |
1731 | 0 | } |
1732 | | |
1733 | | /* Check that pass-through PostScript code is a string. */ |
1734 | | static bool |
1735 | | ps_source_ok(const gs_memory_t *mem, const gs_param_string * psource) |
1736 | 0 | { |
1737 | 0 | if (psource->size >= 2 && psource->data[0] == '(' && |
1738 | 0 | psource->data[psource->size - 1] == ')' |
1739 | 0 | ) |
1740 | 0 | return true; |
1741 | 0 | else { |
1742 | 0 | int i; |
1743 | 0 | lprintf("bad PS passthrough: "); |
1744 | 0 | for (i=0; i<psource->size; i++) |
1745 | 0 | errprintf(mem, "%c", psource->data[i]); |
1746 | 0 | errprintf(mem, "\n"); |
1747 | 0 | return false; |
1748 | 0 | } |
1749 | 0 | } |
1750 | | |
1751 | | /* Write the contents of pass-through PostScript code. */ |
1752 | | /* Return the size written on the file. */ |
1753 | | static uint |
1754 | | pdfmark_write_ps(stream *s, const gs_param_string * psource) |
1755 | 0 | { |
1756 | | /****** REMOVE ESCAPES WITH PSSDecode, SEE gdevpdfr p. 2 ******/ |
1757 | 0 | uint size = psource->size - 2; |
1758 | |
|
1759 | 0 | stream_write(s, psource->data + 1, size); |
1760 | 0 | stream_putc(s, '\n'); |
1761 | 0 | return size + 1; |
1762 | 0 | } |
1763 | | |
1764 | | /* Start a XObject. */ |
1765 | | static int |
1766 | | start_XObject(gx_device_pdf * pdev, bool compress, cos_stream_t **ppcs) |
1767 | 0 | { pdf_resource_t *pres; |
1768 | 0 | cos_stream_t *pcs; |
1769 | 0 | int code; |
1770 | |
|
1771 | 0 | code = pdf_open_page(pdev, PDF_IN_STREAM); |
1772 | 0 | if (code < 0) |
1773 | 0 | return code; |
1774 | 0 | code = pdf_enter_substream(pdev, resourceXObject, gs_no_id, &pres, false, |
1775 | 0 | pdev->CompressStreams); |
1776 | 0 | if (code < 0) |
1777 | 0 | return code; |
1778 | 0 | pdev->accumulating_a_global_object = true; |
1779 | 0 | pcs = (cos_stream_t *)pres->object; |
1780 | 0 | pdev->substream_Resources = cos_dict_alloc(pdev, "start_XObject"); |
1781 | 0 | if (!pdev->substream_Resources) |
1782 | 0 | return_error(gs_error_VMerror); |
1783 | 0 | if (pdev->ForOPDFRead) { |
1784 | 0 | code = cos_dict_put_c_key_bool((cos_dict_t *)pres->object, "/.Global", true); |
1785 | 0 | if (code < 0) |
1786 | 0 | return code; |
1787 | 0 | } |
1788 | 0 | pres->named = true; |
1789 | 0 | pres->where_used = 0; /* initially not used */ |
1790 | 0 | pcs->pres = pres; |
1791 | 0 | *ppcs = pcs; |
1792 | 0 | return 0; |
1793 | 0 | } |
1794 | | |
1795 | | /* PS pdfmark */ |
1796 | 0 | #define MAX_PS_INLINE 100 |
1797 | | static int |
1798 | | pdfmark_PS(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
1799 | | const gs_matrix * pctm, const gs_param_string * objname) |
1800 | 0 | { |
1801 | 0 | gs_param_string source; |
1802 | 0 | gs_param_string level1; |
1803 | |
|
1804 | 0 | if (!pdfmark_find_key("/DataSource", pairs, count, &source) || |
1805 | 0 | !ps_source_ok(pdev->memory, &source) || |
1806 | 0 | (pdfmark_find_key("/Level1", pairs, count, &level1) && |
1807 | 0 | !ps_source_ok(pdev->memory, &level1)) |
1808 | 0 | ) |
1809 | 0 | return_error(gs_error_rangecheck); |
1810 | 0 | if (level1.data == 0 && source.size <= MAX_PS_INLINE && objname == 0) { |
1811 | | /* Insert the PostScript code in-line */ |
1812 | 0 | int code = pdf_open_contents(pdev, PDF_IN_STREAM); |
1813 | 0 | stream *s; |
1814 | |
|
1815 | 0 | if (code < 0) |
1816 | 0 | return code; |
1817 | 0 | s = pdev->strm; |
1818 | 0 | stream_write(s, source.data, source.size); |
1819 | 0 | stream_puts(s, " PS\n"); |
1820 | 0 | } else { |
1821 | | /* Put the PostScript code in a resource. */ |
1822 | 0 | cos_stream_t *pcs; |
1823 | 0 | int code; |
1824 | 0 | gs_id level1_id = gs_no_id; |
1825 | 0 | pdf_resource_t *pres; |
1826 | |
|
1827 | 0 | if (level1.data != 0) { |
1828 | 0 | pdf_resource_t *pres; |
1829 | |
|
1830 | 0 | code = pdf_enter_substream(pdev, |
1831 | 0 | resourceXObject, |
1832 | 0 | gs_no_id, &pres, true, |
1833 | 0 | pdev->CompressStreams); |
1834 | 0 | if (code < 0) |
1835 | 0 | return code; |
1836 | 0 | pcs = (cos_stream_t *)pres->object; |
1837 | 0 | if (pdev->ForOPDFRead && objname != 0) { |
1838 | 0 | code = cos_dict_put_c_key_bool((cos_dict_t *)pres->object, "/.Global", true); |
1839 | 0 | if (code < 0) |
1840 | 0 | return code; |
1841 | 0 | } |
1842 | 0 | pres->named = (objname != 0); |
1843 | 0 | pres->where_used = 0; |
1844 | 0 | pcs->pres = pres; |
1845 | 0 | DISCARD(pdfmark_write_ps(pdev->strm, &level1)); |
1846 | 0 | code = pdf_exit_substream(pdev); |
1847 | 0 | if (code < 0) |
1848 | 0 | return code; |
1849 | 0 | code = cos_write_object(pres->object, pdev, resourceOther); |
1850 | 0 | if (code < 0) |
1851 | 0 | return code; |
1852 | 0 | level1_id = pres->object->id; |
1853 | 0 | } |
1854 | 0 | code = start_XObject(pdev, pdev->params.CompressPages, &pcs); |
1855 | 0 | if (code < 0) |
1856 | 0 | return code; |
1857 | 0 | pres = pdev->accumulating_substream_resource; |
1858 | 0 | if (pres == NULL) |
1859 | 0 | return_error(gs_error_unregistered); |
1860 | 0 | code = cos_stream_put_c_strings(pcs, "/Type", "/XObject"); |
1861 | 0 | if (code < 0) |
1862 | 0 | return code; |
1863 | 0 | code = cos_stream_put_c_strings(pcs, "/Subtype", "/PS"); |
1864 | 0 | if (code < 0) |
1865 | 0 | return code; |
1866 | 0 | if (level1_id != gs_no_id) { |
1867 | 0 | char r[MAX_DEST_STRING]; |
1868 | |
|
1869 | 0 | gs_snprintf(r, sizeof(r), "%ld 0 R", level1_id); |
1870 | 0 | code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/Level1", |
1871 | 0 | (byte *)r, strlen(r)); |
1872 | 0 | if (code < 0) |
1873 | 0 | return code; |
1874 | 0 | } |
1875 | 0 | DISCARD(pdfmark_write_ps(pdev->strm, &source)); |
1876 | 0 | code = pdf_exit_substream(pdev); |
1877 | 0 | if (code < 0) |
1878 | 0 | return code; |
1879 | 0 | { gs_const_string objname1, *pon = NULL; |
1880 | |
|
1881 | 0 | if (objname != NULL) { |
1882 | 0 | objname1.data = objname->data; |
1883 | 0 | objname1.size = objname->size; |
1884 | 0 | pon = &objname1; |
1885 | 0 | } |
1886 | 0 | code = pdfmark_bind_named_object(pdev, pon, &pres); |
1887 | 0 | if (code < 0) |
1888 | 0 | return code; |
1889 | 0 | } |
1890 | 0 | code = pdf_open_contents(pdev, PDF_IN_STREAM); |
1891 | 0 | if (code < 0) |
1892 | 0 | return code; |
1893 | 0 | pcs->pres->where_used |= pdev->used_mask; |
1894 | 0 | pprinti64d1(pdev->strm, "/R%"PRId64" Do\n", pcs->id); |
1895 | 0 | } |
1896 | 0 | return 0; |
1897 | 0 | } |
1898 | | |
1899 | | /* Common code for pdfmarks that do PUT into a specific object. */ |
1900 | | static int |
1901 | | pdfmark_put_pairs(cos_dict_t *pcd, gs_param_string * pairs, uint count) |
1902 | 5.87k | { |
1903 | 5.87k | int code = 0, i; |
1904 | | |
1905 | 5.87k | if (count & 1) |
1906 | 0 | return_error(gs_error_rangecheck); |
1907 | 11.0k | for (i = 0; code >= 0 && i < count; i += 2) |
1908 | 5.17k | code = pdfmark_put_pair(pcd, pairs + i); |
1909 | 5.87k | return code; |
1910 | 5.87k | } |
1911 | | |
1912 | | /* PAGES pdfmark */ |
1913 | | static int |
1914 | | pdfmark_PAGES(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
1915 | | const gs_matrix * pctm, const gs_param_string * no_objname) |
1916 | 0 | { |
1917 | 0 | return pdfmark_put_pairs(pdev->Pages, pairs, count); |
1918 | 0 | } |
1919 | | |
1920 | | /* PAGE pdfmark */ |
1921 | | static int |
1922 | | pdfmark_PAGE(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
1923 | | const gs_matrix * pctm, const gs_param_string * no_objname) |
1924 | 4.35k | { |
1925 | 4.35k | return pdfmark_put_pairs(pdf_current_page_dict(pdev), pairs, count); |
1926 | 4.35k | } |
1927 | | |
1928 | | /* Add a page label for the current page. The last label on a page |
1929 | | * overrides all previous labels for this page. Unlabeled pages will get |
1930 | | * empty page labels. label == NULL flushes the last label */ |
1931 | | static int |
1932 | | pdfmark_add_pagelabel(gx_device_pdf * pdev, const gs_param_string *label) |
1933 | 9.18k | { |
1934 | 9.18k | cos_value_t value; |
1935 | 9.18k | cos_dict_t *dict = 0; |
1936 | 9.18k | int code = 0; |
1937 | | |
1938 | | /* create label dict (and page label array if not present yet) */ |
1939 | 9.18k | if (label != 0) { |
1940 | 0 | if (!pdev->PageLabels) { |
1941 | 0 | pdev->PageLabels = cos_array_alloc(pdev, |
1942 | 0 | "pdfmark_add_pagelabel(PageLabels)"); |
1943 | 0 | if (pdev->PageLabels == 0) |
1944 | 0 | return_error(gs_error_VMerror); |
1945 | 0 | pdev->PageLabels->id = pdf_obj_ref(pdev); |
1946 | | |
1947 | | /* empty label for unlabled pages before first labled page */ |
1948 | 0 | pdev->PageLabels_current_page = 0; |
1949 | 0 | pdev->PageLabels_current_label = cos_dict_alloc(pdev, |
1950 | 0 | "pdfmark_add_pagelabel(first)"); |
1951 | 0 | if (pdev->PageLabels_current_label == 0) |
1952 | 0 | return_error(gs_error_VMerror); |
1953 | 0 | } |
1954 | | |
1955 | 0 | dict = cos_dict_alloc(pdev, "pdfmark_add_pagelabel(dict)"); |
1956 | 0 | if (dict == 0) |
1957 | 0 | return_error(gs_error_VMerror); |
1958 | | |
1959 | 0 | code = cos_dict_put_c_key(dict, "/P", cos_string_value(&value, |
1960 | 0 | label->data, label->size)); |
1961 | 0 | if (code < 0) { |
1962 | 0 | COS_FREE(dict, "pdfmark_add_pagelabel(dict)"); |
1963 | 0 | return code; |
1964 | 0 | } |
1965 | 0 | } |
1966 | | |
1967 | | /* flush current label */ |
1968 | 9.18k | if (label == 0 || pdev->next_page != pdev->PageLabels_current_page) { |
1969 | | /* handle current label */ |
1970 | 9.18k | if (pdev->PageLabels_current_label) { |
1971 | 0 | if (code >= 0) { |
1972 | 0 | code = cos_array_add_int(pdev->PageLabels, |
1973 | 0 | pdev->PageLabels_current_page); |
1974 | 0 | if (code >= 0) |
1975 | 0 | code = cos_array_add(pdev->PageLabels, |
1976 | 0 | COS_OBJECT_VALUE(&value, |
1977 | 0 | pdev->PageLabels_current_label)); |
1978 | 0 | } |
1979 | 0 | pdev->PageLabels_current_label = 0; |
1980 | 0 | } |
1981 | | |
1982 | | /* handle unlabled pages between current labeled page and |
1983 | | * next labeled page */ |
1984 | 9.18k | if (pdev->PageLabels) { |
1985 | 0 | if (pdev->next_page - pdev->PageLabels_current_page > 1) { |
1986 | 0 | cos_dict_t *tmp = cos_dict_alloc(pdev, |
1987 | 0 | "pdfmark_add_pagelabel(tmp)"); |
1988 | 0 | if (tmp == 0) |
1989 | 0 | return_error(gs_error_VMerror); |
1990 | | |
1991 | 0 | code = cos_array_add_int(pdev->PageLabels, |
1992 | 0 | pdev->PageLabels_current_page + 1); |
1993 | 0 | if (code >= 0) |
1994 | 0 | code = cos_array_add(pdev->PageLabels, |
1995 | 0 | COS_OBJECT_VALUE(&value, tmp)); |
1996 | 0 | } |
1997 | 0 | } |
1998 | 9.18k | } |
1999 | | |
2000 | | /* new current label */ |
2001 | 9.18k | if (pdev->PageLabels_current_label) |
2002 | 0 | COS_FREE(pdev->PageLabels_current_label, |
2003 | 9.18k | "pdfmark_add_pagelabel(current_label)"); |
2004 | 9.18k | pdev->PageLabels_current_label = dict; |
2005 | 9.18k | pdev->PageLabels_current_page = pdev->next_page; |
2006 | | |
2007 | 9.18k | return code; |
2008 | 9.18k | } |
2009 | | |
2010 | | /* Close the pagelabel numtree.*/ |
2011 | | int |
2012 | | pdfmark_end_pagelabels(gx_device_pdf * pdev) |
2013 | 9.18k | { |
2014 | 9.18k | return pdfmark_add_pagelabel(pdev, 0); |
2015 | 9.18k | } |
2016 | | |
2017 | | /* [ /Label string /PlateColor string pdfmark */ |
2018 | | /* FIXME: /PlateColor is ignored */ |
2019 | | static int |
2020 | | pdfmark_PAGELABEL(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
2021 | | const gs_matrix * pctm, const gs_param_string * no_objname) |
2022 | 0 | { |
2023 | 0 | gs_param_string key; |
2024 | |
|
2025 | 0 | if (pdev->CompatibilityLevel >= 1.3) { |
2026 | 0 | if (pdfmark_find_key("/Label", pairs, count, &key)) { |
2027 | 0 | return pdfmark_add_pagelabel(pdev, &key); |
2028 | 0 | } |
2029 | 0 | } |
2030 | 0 | return 0; |
2031 | 0 | } |
2032 | | |
2033 | | static int is_XMP_Key(const gs_param_string *param) |
2034 | 0 | { |
2035 | 0 | if (pdf_key_eq(param, "/Title")) |
2036 | 0 | return 1; |
2037 | 0 | if (pdf_key_eq(param, "/Author")) |
2038 | 0 | return 1; |
2039 | 0 | if (pdf_key_eq(param, "/Subject")) |
2040 | 0 | return 1; |
2041 | 0 | if (pdf_key_eq(param, "/Keywords")) |
2042 | 0 | return 1; |
2043 | 0 | if (pdf_key_eq(param, "/Creator")) |
2044 | 0 | return 1; |
2045 | 0 | if (pdf_key_eq(param, "/Producer")) |
2046 | 0 | return 1; |
2047 | | /* These two aren't string data types and so won't affect anything |
2048 | | * in the DOCINFO pdfmark, which is the only client for this code currently |
2049 | | * but we may want to use this in future for other purposed. |
2050 | | */ |
2051 | 0 | if (pdf_key_eq(param, "/CreationDate")) |
2052 | 0 | return 1; |
2053 | 0 | if (pdf_key_eq(param, "/ModDate")) |
2054 | 0 | return 1; |
2055 | 0 | return 0; |
2056 | 0 | } |
2057 | | |
2058 | | /* DOCINFO pdfmark */ |
2059 | | static int |
2060 | | pdfmark_DOCINFO(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
2061 | | const gs_matrix * pctm, const gs_param_string * no_objname) |
2062 | 940 | { |
2063 | | /* |
2064 | | * We could use pdfmark_put_pairs(pdev->Info, pairs, count), except |
2065 | | * that we want to replace "Distiller" with our own name as the |
2066 | | * Producer. |
2067 | | */ |
2068 | 940 | cos_dict_t *const pcd = pdev->Info; |
2069 | 940 | int code = 0, i; |
2070 | | |
2071 | 940 | if (count & 1) |
2072 | 0 | return_error(gs_error_rangecheck); |
2073 | 2.62k | for (i = 0; code >= 0 && i < count; i += 2) { |
2074 | 1.68k | const gs_param_string *pair = pairs + i; |
2075 | | |
2076 | 1.68k | if (pdev->CompatibilityLevel >= 2.0) { |
2077 | 0 | if (!pdf_key_eq(pairs + i, "/ModDate") && !pdf_key_eq(pairs + i, "/CreationDate")) |
2078 | 0 | continue; |
2079 | 0 | } |
2080 | | |
2081 | 1.68k | if (pdev->PDFA !=0 && is_XMP_Key(pair)) { |
2082 | 0 | const gs_param_string *p = pairs + i + 1; |
2083 | 0 | bool abort = false; |
2084 | | |
2085 | | /* Ensure that we can write the XMP string. If not, handle this according to PDFACompatibilityPolicy */ |
2086 | 0 | code = pdf_xmp_write_translated(pdev, NULL, p->data + 1, p->size - 2, NULL); |
2087 | 0 | abort = code < 0; |
2088 | |
|
2089 | 0 | if (abort == true) |
2090 | 0 | { |
2091 | | /* Can't handle UTF16BE in PDF/A1, so abort this pair or abort PDF/A or just abort, |
2092 | | * depending on PDFACompatibilityPolicy |
2093 | | */ |
2094 | 0 | switch (pdev->PDFACompatibilityPolicy) { |
2095 | 0 | case 0: |
2096 | 0 | emprintf(pdev->memory, |
2097 | 0 | "Text string detected in DOCINFO cannot be represented in XMP for PDF/A1, reverting to normal PDF output\n"); |
2098 | 0 | pdev->AbortPDFAX = true; |
2099 | 0 | pdev->PDFA = 0; |
2100 | 0 | break; |
2101 | 0 | case 1: |
2102 | 0 | emprintf(pdev->memory, |
2103 | 0 | "Text string detected in DOCINFO cannot be represented in XMP for PDF/A1, discarding DOCINFO\n"); |
2104 | 0 | continue; |
2105 | 0 | break; |
2106 | 0 | case 2: |
2107 | 0 | emprintf(pdev->memory, |
2108 | 0 | "Text string detected in DOCINFO cannot be represented in XMP for PDF/A1, aborting conversion.\n"); |
2109 | | /* If we don't return a fatal error then zputdeviceparams simply ignores it (!) */ |
2110 | 0 | return_error(gs_error_Fatal); |
2111 | 0 | break; |
2112 | 0 | default: |
2113 | 0 | break; |
2114 | 0 | } |
2115 | 0 | } |
2116 | 0 | } |
2117 | 1.68k | if (pdf_key_eq(pairs + i, "/Producer")) { |
2118 | | /* Slightly screwy - separating the G, P and L characters this way to avoid |
2119 | | * accidental replacement by the release script. |
2120 | | */ |
2121 | 0 | const byte gs_g_p_l_prod_fam[16] = {'G', 'P', 'L', ' ', 'G', 'h', 'o', 's', 't', 's', 'c', 'r', 'i', 'p', 't', '\0'}; |
2122 | 0 | string_match_params params; |
2123 | 0 | params = string_match_params_default; |
2124 | 0 | params.ignore_case = true; |
2125 | |
|
2126 | 0 | if (!string_match((const byte *)GS_PRODUCTFAMILY, strlen(GS_PRODUCTFAMILY), gs_g_p_l_prod_fam, 15, ¶ms)) |
2127 | 0 | code = pdfmark_put_pair(pcd, pair); |
2128 | 0 | } else |
2129 | 1.68k | code = pdfmark_put_pair(pcd, pair); |
2130 | 1.68k | if (code < 0) |
2131 | 0 | break; |
2132 | 1.68k | } |
2133 | 940 | return code; |
2134 | 940 | } |
2135 | | |
2136 | | /* DOCVIEW pdfmark */ |
2137 | | static int |
2138 | | pdfmark_DOCVIEW(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
2139 | | const gs_matrix * pctm, const gs_param_string * no_objname) |
2140 | 625 | { |
2141 | 625 | char dest[MAX_DEST_STRING]; |
2142 | 625 | int code = 0; |
2143 | | |
2144 | 625 | if (count & 1) |
2145 | 0 | return_error(gs_error_rangecheck); |
2146 | 625 | code = pdfmark_make_dest(dest, pdev, "/Page", "/View", pairs, count, 0); |
2147 | 625 | if (code < 0) |
2148 | 0 | return gs_note_error(gs_error_rangecheck); |
2149 | | |
2150 | 625 | if (code > 0) { |
2151 | 138 | int i; |
2152 | | |
2153 | 138 | code = cos_dict_put_c_key_string(pdev->Catalog, "/OpenAction", |
2154 | 138 | (byte *)dest, strlen(dest)); |
2155 | 414 | for (i = 0; code >= 0 && i < count; i += 2) |
2156 | 276 | if (!(pdf_key_eq(&pairs[i], "/Page") || |
2157 | 276 | pdf_key_eq(&pairs[i], "/View")) |
2158 | 276 | ) |
2159 | 0 | code = pdfmark_put_pair(pdev->Catalog, pairs + i); |
2160 | 138 | return code; |
2161 | 138 | } else |
2162 | 487 | return pdfmark_put_pairs(pdev->Catalog, pairs, count); |
2163 | 625 | } |
2164 | | |
2165 | | /* ---------------- Named object pdfmarks ---------------- */ |
2166 | | |
2167 | | /* [ /BBox [llx lly urx ury] /_objdef {obj} /BP pdfmark */ |
2168 | | static int |
2169 | | pdfmark_BP(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
2170 | | const gs_matrix * pctm, const gs_param_string * objname) |
2171 | 0 | { |
2172 | 0 | gs_rect bbox; |
2173 | 0 | cos_stream_t *pcs; |
2174 | 0 | int code; |
2175 | 0 | gs_matrix ictm; |
2176 | 0 | byte bbox_str[6 + 6 * 15], matrix_str[6 + 6 * 15]; |
2177 | 0 | char chars[MAX_RECT_STRING + 1]; |
2178 | 0 | int bbox_str_len, matrix_str_len; |
2179 | 0 | stream s; |
2180 | |
|
2181 | 0 | if (objname == 0 || count != 2 || !pdf_key_eq(&pairs[0], "/BBox")) |
2182 | 0 | return_error(gs_error_rangecheck); |
2183 | 0 | code = gs_matrix_invert(pctm, &ictm); |
2184 | 0 | if (code < 0) |
2185 | 0 | return code; |
2186 | 0 | if (pairs[1].size > MAX_RECT_STRING) |
2187 | 0 | return_error(gs_error_limitcheck); |
2188 | 0 | memcpy(chars, pairs[1].data, pairs[1].size); |
2189 | 0 | chars[pairs[1].size] = 0; |
2190 | 0 | if (sscanf(chars, "[%lg %lg %lg %lg]", |
2191 | 0 | &bbox.p.x, &bbox.p.y, &bbox.q.x, &bbox.q.y) != 4) |
2192 | 0 | return_error(gs_error_rangecheck); |
2193 | 0 | if ((pdev->used_mask << 1) == 0) |
2194 | 0 | return_error(gs_error_limitcheck); |
2195 | 0 | code = start_XObject(pdev, pdev->params.CompressPages, &pcs); |
2196 | 0 | if (code < 0) |
2197 | 0 | return code; |
2198 | 0 | { byte *s = gs_alloc_string(pdev->memory, objname->size, "pdfmark_PS"); |
2199 | |
|
2200 | 0 | if (s == NULL) |
2201 | 0 | return_error(gs_error_VMerror); |
2202 | 0 | memcpy(s, objname->data, objname->size); |
2203 | 0 | pdev->objname.data = s; |
2204 | 0 | pdev->objname.size = objname->size; |
2205 | 0 | } |
2206 | 0 | pcs->is_graphics = true; |
2207 | 0 | gs_bbox_transform(&bbox, pctm, &bbox); |
2208 | 0 | s_init(&s, NULL); |
2209 | 0 | swrite_string(&s, bbox_str, sizeof(bbox_str)); |
2210 | 0 | pprintg4(&s, "[%g %g %g %g]", |
2211 | 0 | bbox.p.x, bbox.p.y, bbox.q.x, bbox.q.y); |
2212 | 0 | bbox_str_len = stell(&s); |
2213 | 0 | swrite_string(&s, matrix_str, sizeof(bbox_str)); |
2214 | 0 | pprintg6(&s, "[%g %g %g %g %g %g]", |
2215 | 0 | ictm.xx, ictm.xy, ictm.yx, ictm.yy, ictm.tx, ictm.ty); |
2216 | 0 | matrix_str_len = stell(&s); |
2217 | 0 | if ((code = cos_stream_put_c_strings(pcs, "/Type", "/XObject")) < 0 || |
2218 | 0 | (code = cos_stream_put_c_strings(pcs, "/Subtype", "/Form")) < 0 || |
2219 | 0 | (code = cos_stream_put_c_strings(pcs, "/FormType", "1")) < 0 || |
2220 | 0 | (code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/BBox", |
2221 | 0 | bbox_str, bbox_str_len)) < 0 || |
2222 | 0 | (code = cos_dict_put_c_key_string(cos_stream_dict(pcs), "/Matrix", |
2223 | 0 | matrix_str, matrix_str_len)) < 0 || |
2224 | 0 | (code = cos_dict_put_c_key_object(cos_stream_dict(pcs), "/Resources", |
2225 | 0 | COS_OBJECT(pdev->substream_Resources))) < 0 |
2226 | 0 | ) |
2227 | 0 | return code; |
2228 | | /* Don't add to local_named_objects untill the object is created |
2229 | | to prevent pending references, which may appear |
2230 | | if /PUT pdfmark executes before pdf_substitute_resource in pdfmark_EP |
2231 | | drops this object. |
2232 | | */ |
2233 | 0 | pdev->FormDepth++; |
2234 | 0 | return 0; |
2235 | 0 | } |
2236 | | |
2237 | | /* [ /EP pdfmark */ |
2238 | | static int |
2239 | | pdfmark_EP(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
2240 | | const gs_matrix * pctm, const gs_param_string * no_objname) |
2241 | 0 | { |
2242 | 0 | int code; |
2243 | 0 | pdf_resource_t *pres = pdev->accumulating_substream_resource; |
2244 | 0 | gs_const_string objname = pdev->objname; |
2245 | | |
2246 | | /* We are not currently accumulating a resource, this suggests an /EP |
2247 | | * without an opening /BP. This is (obviously) an error, Distiller throws |
2248 | | * an 'undefined' error, so we will too. |
2249 | | */ |
2250 | 0 | if (pres == NULL) |
2251 | 0 | return_error(gs_error_undefined); |
2252 | | |
2253 | 0 | if (pdev->CompatibilityLevel <= 1.7) { |
2254 | 0 | code = pdf_add_procsets(pdev->substream_Resources, pdev->procsets); |
2255 | 0 | if (code < 0) |
2256 | 0 | return code; |
2257 | 0 | } |
2258 | 0 | code = pdf_exit_substream(pdev); |
2259 | 0 | if (code < 0) |
2260 | 0 | return code; |
2261 | 0 | code = pdfmark_bind_named_object(pdev, &objname, &pres); |
2262 | 0 | if (code < 0) |
2263 | 0 | return 0; |
2264 | 0 | gs_free_const_string(pdev->memory, objname.data, objname.size, "pdfmark_EP"); |
2265 | 0 | pdev->FormDepth--; |
2266 | 0 | return 0; |
2267 | 0 | } |
2268 | | |
2269 | | /* [ {obj} /SP pdfmark */ |
2270 | | static int |
2271 | | pdfmark_SP(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
2272 | | const gs_matrix * pctm, const gs_param_string * no_objname) |
2273 | 0 | { |
2274 | 0 | cos_object_t *pco; /* stream */ |
2275 | 0 | int code; |
2276 | |
|
2277 | 0 | if (count != 1) |
2278 | 0 | return_error(gs_error_rangecheck); |
2279 | 0 | if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0) |
2280 | 0 | return code; |
2281 | 0 | if (pco->is_open || !pco->is_graphics) |
2282 | 0 | return_error(gs_error_rangecheck); |
2283 | 0 | code = pdf_open_contents(pdev, PDF_IN_STREAM); |
2284 | 0 | if (code < 0) |
2285 | 0 | return code; |
2286 | 0 | pdf_put_matrix(pdev, "q ", pctm, "cm"); |
2287 | 0 | pprinti64d1(pdev->strm, "/R%"PRId64" Do Q\n", pco->id); |
2288 | 0 | pco->pres->where_used |= pdev->used_mask; |
2289 | |
|
2290 | 0 | code = pdf_add_resource(pdev, pdev->substream_Resources, "/XObject", pco->pres); |
2291 | 0 | if (code < 0) |
2292 | 0 | return code; |
2293 | | |
2294 | 0 | return 0; |
2295 | 0 | } |
2296 | | |
2297 | | /* [ /_objdef {array} /type /array /OBJ pdfmark */ |
2298 | | /* [ /_objdef {dict} /type /dict /OBJ pdfmark */ |
2299 | | /* [ /_objdef {stream} /type /stream /OBJ pdfmark */ |
2300 | | static int |
2301 | | pdfmark_OBJ(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
2302 | | const gs_matrix * pctm, const gs_param_string * objname) |
2303 | 1.10k | { |
2304 | 1.10k | cos_type_t cotype; |
2305 | 1.10k | cos_object_t *pco; |
2306 | 1.10k | bool stream = false; |
2307 | 1.10k | int code; |
2308 | | |
2309 | 1.10k | if (objname == 0 || count != 2 || !pdf_key_eq(&pairs[0], "/type")) |
2310 | 0 | return_error(gs_error_rangecheck); |
2311 | 1.10k | if (pdf_key_eq(&pairs[1], "/array")) |
2312 | 0 | cotype = cos_type_array; |
2313 | 1.10k | else if (pdf_key_eq(&pairs[1], "/dict")) |
2314 | 900 | cotype = cos_type_dict; |
2315 | 204 | else if ((stream = pdf_key_eq(&pairs[1], "/stream"))) |
2316 | 204 | cotype = cos_type_stream; |
2317 | 0 | else |
2318 | 0 | return_error(gs_error_rangecheck); |
2319 | 1.10k | if ((code = pdf_make_named(pdev, objname, cotype, &pco, true)) < 0) { |
2320 | | /* |
2321 | | * For Distiller compatibility, allows multiple /OBJ pdfmarks with |
2322 | | * the same name and type, even though the pdfmark specification |
2323 | | * doesn't say anything about this being legal. |
2324 | | */ |
2325 | 0 | if (code == gs_error_rangecheck && |
2326 | 0 | pdf_refer_named(pdev, objname, &pco) >= 0 && |
2327 | 0 | cos_type(pco) == cotype |
2328 | 0 | ) |
2329 | 0 | return 0; /* already exists, but OK */ |
2330 | 0 | return code; |
2331 | 0 | } |
2332 | 1.10k | if (stream) { |
2333 | 204 | if (pdev->CompressStreams) |
2334 | 204 | return setup_pdfmark_stream_compression((gx_device_psdf *)pdev, |
2335 | 204 | (cos_stream_t *)pco); |
2336 | 0 | else |
2337 | 0 | return setup_pdfmark_stream_no_compression((gx_device_psdf *)pdev, |
2338 | 0 | (cos_stream_t *)pco); |
2339 | 204 | } |
2340 | 900 | return 0; |
2341 | 1.10k | } |
2342 | | |
2343 | | /* [ {array} index value /PUT pdfmark */ |
2344 | | /* Dictionaries are converted to .PUTDICT */ |
2345 | | /* Streams are converted to .PUTSTREAM */ |
2346 | | static int |
2347 | | pdfmark_PUT(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
2348 | | const gs_matrix * pctm, const gs_param_string * no_objname) |
2349 | 0 | { |
2350 | 0 | cos_object_t *pco; |
2351 | 0 | cos_value_t value; |
2352 | 0 | int code, index; |
2353 | |
|
2354 | 0 | if (count != 3) |
2355 | 0 | return_error(gs_error_rangecheck); |
2356 | 0 | if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0) |
2357 | 0 | return code; |
2358 | 0 | if ((code = pdfmark_scan_int(&pairs[1], &index)) < 0) |
2359 | 0 | return code; |
2360 | 0 | if (index < 0) |
2361 | 0 | return_error(gs_error_rangecheck); |
2362 | 0 | if (pco->written) |
2363 | 0 | return_error(gs_error_rangecheck); |
2364 | 0 | return cos_array_put((cos_array_t *)pco, index, |
2365 | 0 | cos_string_value(&value, pairs[2].data, pairs[2].size)); |
2366 | 0 | } |
2367 | | |
2368 | | /* [ {dict} key value ... /.PUTDICT pdfmark */ |
2369 | | /* [ {stream} key value ... /.PUTDICT pdfmark */ |
2370 | | /* |
2371 | | * Adobe's pdfmark documentation doesn't allow PUTDICT with a stream, |
2372 | | * but it's reasonable and unambiguous, and Acrobat Distiller accepts it, |
2373 | | * so we do too. |
2374 | | */ |
2375 | | static int |
2376 | | pdfmark_PUTDICT(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
2377 | | const gs_matrix * pctm, const gs_param_string * no_objname) |
2378 | 1.03k | { |
2379 | 1.03k | cos_object_t *pco; |
2380 | 1.03k | int code, i; |
2381 | | |
2382 | 1.03k | if ((code = pdf_refer_named(pdev, &pairs[0], &pco)) < 0) |
2383 | 0 | return code; |
2384 | 1.03k | if (cos_type(pco) != cos_type_dict && cos_type(pco) != cos_type_stream) |
2385 | 0 | return_error(gs_error_typecheck); |
2386 | 1.03k | if (pco->written) |
2387 | 0 | return_error(gs_error_rangecheck); |
2388 | | |
2389 | | /* If this is a stream, and we are doing PDF/A output, and the stream |
2390 | | * is a Metadata stream, then we must not write it compressed. Bizarrely PDF/A |
2391 | | * excludes this. |
2392 | | * Actually, we cna't write Metadata streams at all. Because we don't know how to extend |
2393 | | * the XMP Metadata to include them. |
2394 | | */ |
2395 | 1.03k | if (cos_type(pco) == cos_type_stream && pdev->PDFA) { |
2396 | 0 | for (i=0;i<count;i++) { |
2397 | 0 | if (pairs[i].size == 9 && strncmp((const char *)pairs[i].data, "/Metadata", 9) == 0) { |
2398 | 0 | cos_dict_t *pcd = (cos_dict_t *)pco; |
2399 | |
|
2400 | 0 | if (pdev->PDFA) { |
2401 | 0 | switch (pdev->PDFACompatibilityPolicy) { |
2402 | 0 | case 0: |
2403 | 0 | emprintf(pdev->memory, |
2404 | 0 | "Cannot preserve Marked Content in PDF/A, reverting to normal PDF output\n\n"); |
2405 | 0 | pdev->AbortPDFAX = true; |
2406 | 0 | pdev->PDFA = 0; |
2407 | 0 | break; |
2408 | 0 | case 1: |
2409 | 0 | emprintf(pdev->memory, |
2410 | 0 | "Cannot preserve Marked Content in PDF/A, dropping feature to preserve PDF/A compatibility\n"); |
2411 | 0 | return 0; |
2412 | 0 | break; |
2413 | 0 | case 2: |
2414 | 0 | emprintf(pdev->memory, |
2415 | 0 | "Cannot preserve Marked Content in PDF/A, aborting conversion\n"); |
2416 | 0 | return_error (gs_error_typecheck); |
2417 | 0 | break; |
2418 | 0 | default: |
2419 | 0 | emprintf(pdev->memory, |
2420 | 0 | "Cannot preserve Marked Content in PDF/A, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n"); |
2421 | 0 | pdev->AbortPDFAX = true; |
2422 | 0 | pdev->PDFA = 0; |
2423 | 0 | break; |
2424 | 0 | } |
2425 | 0 | } |
2426 | | |
2427 | | /* Close the compressed stream */ |
2428 | 0 | gs_free_object(pdev->pdf_memory, pco->input_strm, "free old stream, replacing with new stream"); |
2429 | | /* And create a new uncompressed stream */ |
2430 | 0 | code = setup_pdfmark_stream_no_compression((gx_device_psdf *)pdev, |
2431 | 0 | (cos_stream_t *)pco); |
2432 | 0 | if (code < 0) |
2433 | 0 | return code; |
2434 | | |
2435 | | /* We also need to remove any compression filters from the stream dictionary |
2436 | | * The only possible error here is that the key isn't found, which is possible |
2437 | | * and we don't care, so ignore the return code. |
2438 | | */ |
2439 | 0 | cos_dict_delete_c_key(pcd, "/Filter"); |
2440 | 0 | cos_dict_delete_c_key(pcd, "/DecodeParams"); |
2441 | |
|
2442 | 0 | } |
2443 | 0 | } |
2444 | 0 | } |
2445 | 1.03k | return pdfmark_put_pairs((cos_dict_t *)pco, pairs + 1, count - 1); |
2446 | 1.03k | } |
2447 | | |
2448 | | /* [ {stream} string ... /.PUTSTREAM pdfmark */ |
2449 | | static int |
2450 | | pdfmark_PUTSTREAM(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
2451 | | const gs_matrix * pctm, const gs_param_string * no_objname) |
2452 | 201 | { |
2453 | 201 | cos_object_t *pco; |
2454 | 201 | int code, i; |
2455 | 201 | uint l; |
2456 | | |
2457 | 201 | if (count < 2) |
2458 | 0 | return_error(gs_error_rangecheck); |
2459 | 201 | if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0) |
2460 | 0 | return code; |
2461 | 201 | if (!pco->is_open) { |
2462 | 0 | pdev->pdfmark_dup_stream = true; |
2463 | 0 | return 0; |
2464 | 0 | } |
2465 | 402 | for (i = 1; i < count; ++i) |
2466 | 201 | if (sputs(pco->input_strm, pairs[i].data, pairs[i].size, &l) != 0) |
2467 | 0 | return_error(gs_error_ioerror); |
2468 | 201 | if (pco->written) |
2469 | 0 | return_error(gs_error_rangecheck); |
2470 | 201 | pdev->pdfmark_dup_stream = false; |
2471 | 201 | return code; |
2472 | 201 | } |
2473 | | |
2474 | | /* [ {array} value /APPEND pdfmark */ |
2475 | | static int |
2476 | | pdfmark_APPEND(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
2477 | | const gs_matrix * pctm, const gs_param_string * objname) |
2478 | 0 | { |
2479 | 0 | cos_object_t *pco; |
2480 | 0 | cos_value_t value; |
2481 | 0 | int code; |
2482 | |
|
2483 | 0 | if (count != 2) |
2484 | 0 | return_error(gs_error_rangecheck); |
2485 | 0 | if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0) |
2486 | 0 | return code; |
2487 | 0 | return cos_array_add((cos_array_t *)pco, |
2488 | 0 | cos_string_value(&value, pairs[1].data, pairs[1].size)); |
2489 | 0 | } |
2490 | | |
2491 | | /* [ {array} index value ... /.PUTINTERVAL pdfmark */ |
2492 | | static int |
2493 | | pdfmark_PUTINTERVAL(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
2494 | | const gs_matrix * pctm, const gs_param_string * no_objname) |
2495 | 0 | { |
2496 | 0 | cos_object_t *pco; |
2497 | 0 | cos_value_t value; |
2498 | 0 | int code, index, i; |
2499 | |
|
2500 | 0 | if (count < 2) |
2501 | 0 | return_error(gs_error_rangecheck); |
2502 | 0 | if ((code = pdf_get_named(pdev, &pairs[0], cos_type_array, &pco)) < 0) |
2503 | 0 | return code; |
2504 | 0 | if ((code = pdfmark_scan_int(&pairs[1], &index)) < 0) |
2505 | 0 | return code; |
2506 | 0 | if (index < 0) |
2507 | 0 | return_error(gs_error_rangecheck); |
2508 | 0 | for (i = 2; code >= 0 && i < count; ++i) |
2509 | 0 | code = cos_array_put((cos_array_t *)pco, index + i - 2, |
2510 | 0 | cos_string_value(&value, pairs[i].data, pairs[i].size)); |
2511 | 0 | return code; |
2512 | 0 | } |
2513 | | |
2514 | | /* [ {stream} /CLOSE pdfmark */ |
2515 | | static int |
2516 | | pdfmark_CLOSE(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
2517 | | const gs_matrix * pctm, const gs_param_string * no_objname) |
2518 | 195 | { |
2519 | 195 | cos_object_t *pco; |
2520 | 195 | int code; |
2521 | | |
2522 | 195 | if (count != 1) |
2523 | 0 | return_error(gs_error_rangecheck); |
2524 | 195 | if ((code = pdf_get_named(pdev, &pairs[0], cos_type_stream, &pco)) < 0) |
2525 | 0 | return code; |
2526 | 195 | if (!pco->is_open && !pdev->pdfmark_dup_stream) |
2527 | 0 | return_error(gs_error_rangecheck); |
2528 | 195 | pdev->pdfmark_dup_stream = false; |
2529 | | /* Currently we don't do anything special when closing a stream. */ |
2530 | 195 | pco->is_open = false; |
2531 | 195 | return 0; |
2532 | 195 | } |
2533 | | |
2534 | | /* [ /NamespacePush pdfmark */ |
2535 | | static int |
2536 | | pdfmark_NamespacePush(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2537 | | const gs_matrix *pctm, const gs_param_string *objname) |
2538 | 8 | { |
2539 | 8 | if (count != 0) |
2540 | 0 | return_error(gs_error_rangecheck); |
2541 | 8 | return pdf_push_namespace(pdev); |
2542 | 8 | } |
2543 | | |
2544 | | /* [ /NamespacePop pdfmark */ |
2545 | | static int |
2546 | | pdfmark_NamespacePop(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2547 | | const gs_matrix *pctm, const gs_param_string *objname) |
2548 | 1 | { |
2549 | 1 | if (count != 0) |
2550 | 0 | return_error(gs_error_rangecheck); |
2551 | 1 | cos_dict_objects_write(pdev->local_named_objects, pdev); |
2552 | 1 | return pdf_pop_namespace(pdev); |
2553 | 1 | } |
2554 | | |
2555 | | /* [ /_objdef {image} /NI pdfmark */ |
2556 | | static int |
2557 | | pdfmark_NI(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2558 | | const gs_matrix *pctm, const gs_param_string *objname) |
2559 | 6 | { |
2560 | 6 | cos_object_t *pco; |
2561 | 6 | int code; |
2562 | | |
2563 | 6 | if (objname == 0 || count != 0) |
2564 | 0 | return_error(gs_error_rangecheck); |
2565 | 6 | code = pdf_make_named(pdev, objname, cos_type_dict, &pco, true); |
2566 | 6 | if (code < 0) |
2567 | 0 | return code; |
2568 | 6 | return cos_array_add_object(pdev->NI_stack, pco); |
2569 | 6 | } |
2570 | | |
2571 | | /* ---------------- Named content pdfmarks ---------------- */ |
2572 | | |
2573 | | /* [ tag /MP pdfmark */ |
2574 | | static int |
2575 | | pdfmark_MP(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2576 | | const gs_matrix *pctm, const gs_param_string *objname) |
2577 | 39 | { |
2578 | 39 | int code; |
2579 | 39 | char *tag; |
2580 | | |
2581 | 39 | if (count != 1) return_error(gs_error_rangecheck); |
2582 | | |
2583 | 39 | tag = (char *)gs_alloc_bytes(pdev->memory, (pairs[0].size + 1) * sizeof(unsigned char), |
2584 | 39 | "pdfmark_MP"); |
2585 | 39 | if (tag == NULL) |
2586 | 0 | return_error(gs_error_VMerror); |
2587 | | |
2588 | 39 | memcpy(tag, pairs[0].data, pairs[0].size); |
2589 | 39 | tag[pairs[0].size] = 0x00; |
2590 | | |
2591 | 39 | code = pdf_open_contents(pdev, PDF_IN_STREAM); |
2592 | 39 | if (code < 0) return code; |
2593 | | |
2594 | 39 | pprints1(pdev->strm, "%s MP\n", tag); |
2595 | | |
2596 | 39 | gs_free_object(pdev->memory, tag, "pdfmark_MP"); |
2597 | 39 | return 0; |
2598 | 39 | } |
2599 | | |
2600 | | /* [ tag propdict /DP pdfmark */ |
2601 | | static int |
2602 | | pdfmark_DP(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2603 | | const gs_matrix *pctm, const gs_param_string *objname) |
2604 | 1 | { |
2605 | 1 | int code; |
2606 | 1 | cos_object_t *pco; |
2607 | 1 | char *cstring; |
2608 | 1 | pdf_resource_t *pres; |
2609 | | |
2610 | 1 | if (count != 2) return_error(gs_error_rangecheck); |
2611 | | |
2612 | | /* check tag for /Name object syntax */ |
2613 | 1 | if ((pairs[0].data)[0] != '/') return_error(gs_error_rangecheck); |
2614 | | |
2615 | | /* check propdict for {object name} syntax */ |
2616 | 1 | if (pdf_objname_is_valid(pairs[1].data, pairs[1].size)) |
2617 | 0 | { |
2618 | 0 | code = pdf_refer_named(pdev, &pairs[1], &pco); |
2619 | 0 | if(code < 0) return code; |
2620 | 0 | } |
2621 | 1 | else /* << inline prop dict >> */ |
2622 | 1 | { |
2623 | | /* strip << and >> */ |
2624 | 1 | if ((pairs[1].data)[0]=='<'&&(pairs[1].data)[1]=='<') |
2625 | 1 | { |
2626 | 1 | int ix = 0; |
2627 | 1 | byte *p = (byte *)pairs[1].data; |
2628 | | |
2629 | | /* Fortunately we don't use the 'size' when freeing the memory |
2630 | | * so we can quickly copy the string content up two places and reduce |
2631 | | * the size by 2 to remove the '<<'. This saves an alloc and free of the |
2632 | | * string data. |
2633 | | */ |
2634 | 43 | for (ix = 0; ix < pairs[1].size - 2;ix++) |
2635 | 42 | p[ix] = pairs[1].data[ix + 2]; |
2636 | 1 | pairs[1].size-=2; |
2637 | 1 | } |
2638 | 0 | else |
2639 | 0 | return_error(gs_error_rangecheck); |
2640 | | |
2641 | 1 | if ((pairs[1].data)[pairs[1].size-1]=='>'&&(pairs[1].data)[pairs[1].size-2]=='>') |
2642 | 1 | pairs[1].size-=2; |
2643 | | |
2644 | | /* convert inline propdict to C string with object names replaced by refs */ |
2645 | 1 | code = pdf_replace_names(pdev, &pairs[1], &pairs[1]); |
2646 | 1 | if (code<0) return code; |
2647 | 1 | cstring = (char *)gs_alloc_bytes(pdev->memory, (pairs[1].size + 1) * sizeof(unsigned char), |
2648 | 1 | "pdfmark_DP"); |
2649 | 1 | if (cstring == NULL) |
2650 | 0 | return_error(gs_error_VMerror); |
2651 | | |
2652 | 1 | memcpy(cstring, pairs[1].data, pairs[1].size); |
2653 | 1 | cstring[pairs[1].size] = 0x00; |
2654 | | |
2655 | 1 | code = pdf_make_named_dict(pdev, NULL, (cos_dict_t**) &pco, true); |
2656 | 1 | if (code<0) return code; |
2657 | | |
2658 | | /* copy inline propdict to new object */ |
2659 | 1 | code = cos_dict_put_c_strings((cos_dict_t*) pco, cstring, ""); |
2660 | 1 | if(code < 0) return code; |
2661 | 1 | COS_WRITE_OBJECT(pco, pdev, resourceProperties); |
2662 | 1 | COS_RELEASE(pco, "pdfmark_DP"); |
2663 | 1 | gs_free_object(pdev->memory, cstring, "pdfmark_DP"); |
2664 | 1 | } |
2665 | | |
2666 | 1 | pres = pdf_find_resource_by_resource_id(pdev, resourceProperties, pco->id); |
2667 | 1 | if (pres==0){ |
2668 | 1 | if ((code = pdf_alloc_resource(pdev, resourceProperties, pco->id, &(pco->pres), pco->id))<0) |
2669 | 0 | return code; |
2670 | 1 | } |
2671 | | |
2672 | 1 | cstring = (char *)gs_alloc_bytes(pdev->memory, (pairs[0].size + 1) * sizeof(unsigned char), |
2673 | 1 | "pdfmark_DP"); |
2674 | 1 | if (cstring == NULL) |
2675 | 0 | return_error(gs_error_VMerror); |
2676 | 1 | memcpy(cstring, pairs[0].data, pairs[0].size); |
2677 | 1 | cstring[pairs[0].size] = 0x00; |
2678 | | |
2679 | | /* make sure we write to the correct stream */ |
2680 | 1 | code = pdf_open_contents(pdev, PDF_IN_STREAM); |
2681 | 1 | if (code < 0) return code; |
2682 | | |
2683 | 1 | pprints1(pdev->strm, "%s", cstring); /* write tag */ |
2684 | 1 | pprinti64d1(pdev->strm, "/R%"PRId64" DP\n", pco->id); |
2685 | 1 | pco->pres->where_used |= pdev->used_mask; |
2686 | 1 | if ((code = pdf_add_resource(pdev, pdev->substream_Resources, "/Properties", pco->pres))<0) |
2687 | 0 | return code; |
2688 | | |
2689 | 1 | gs_free_object(pdev->memory, cstring, "pdfmark_DP"); |
2690 | 1 | return 0; |
2691 | 1 | } |
2692 | | |
2693 | | /* [ tag /BMC pdfmark */ |
2694 | | static int |
2695 | | pdfmark_BMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2696 | | const gs_matrix *pctm, const gs_param_string *objname) |
2697 | 370 | { |
2698 | 370 | int code; |
2699 | 370 | char *tag; |
2700 | | |
2701 | 370 | if (count != 1) return_error(gs_error_rangecheck); |
2702 | | |
2703 | 370 | tag = (char *)gs_alloc_bytes(pdev->memory, (pairs[0].size + 1) * sizeof(unsigned char), |
2704 | 370 | "pdfmark_BMC"); |
2705 | 370 | if (tag == NULL) |
2706 | 0 | return_error(gs_error_VMerror); |
2707 | | |
2708 | 370 | memcpy(tag, pairs[0].data, pairs[0].size); |
2709 | 370 | tag[pairs[0].size] = 0x00; |
2710 | | |
2711 | 370 | code = pdf_open_contents(pdev, PDF_IN_STREAM); |
2712 | 370 | if (code < 0) return code; |
2713 | | |
2714 | 370 | pprints1(pdev->strm, "%s BMC\n", tag); |
2715 | | |
2716 | 370 | gs_free_object(pdev->memory, tag, "pdfmark_BMC"); |
2717 | 370 | return 0; |
2718 | 370 | } |
2719 | | |
2720 | | /* [ tag propdict /BDC pdfmark */ |
2721 | | static int |
2722 | | pdfmark_BDC(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2723 | | const gs_matrix *pctm, const gs_param_string *objname) |
2724 | 7.29k | { |
2725 | 7.29k | int code, id = 0, i, esc_size = 0; |
2726 | 7.29k | cos_object_t *pco = NULL; |
2727 | 7.29k | char *cstring; |
2728 | 7.29k | pdf_resource_t *pres; |
2729 | | |
2730 | 7.29k | if (count != 2) return_error(gs_error_rangecheck); |
2731 | | |
2732 | | /* check tag for /Name object syntax */ |
2733 | 7.29k | if ((pairs[0].data)[0] != '/') return_error(gs_error_rangecheck); |
2734 | | |
2735 | | /* Check for /OC (Optional Content), if it is Optional Content, check the output PDF level is at least 1.5 */ |
2736 | 7.29k | if (pairs[0].size == 3 && memcmp(pairs[0].data, "/OC", 3) == 0) { |
2737 | 1.40k | if (pdev->CompatibilityLevel < 1.4999) { |
2738 | 0 | if (pdev->PDFA) { |
2739 | 0 | switch (pdev->PDFACompatibilityPolicy) { |
2740 | 0 | case 0: |
2741 | 0 | emprintf(pdev->memory, |
2742 | 0 | "Optional (Marked) Content not valid in this version of PDF, reverting to normal PDF output\n\n"); |
2743 | 0 | pdev->AbortPDFAX = true; |
2744 | 0 | pdev->PDFA = 0; |
2745 | 0 | break; |
2746 | 0 | case 1: |
2747 | 0 | emprintf(pdev->memory, |
2748 | 0 | "Optional (Marked) Content not valid in this version of PDF. Dropping feature to preserve PDF/A compatibility\n"); |
2749 | 0 | return 0; |
2750 | 0 | break; |
2751 | 0 | case 2: |
2752 | 0 | emprintf(pdev->memory, |
2753 | 0 | "Optional (Marked) Content not valid in this version of PDF, aborting conversion\n"); |
2754 | 0 | return_error (gs_error_typecheck); |
2755 | 0 | break; |
2756 | 0 | default: |
2757 | 0 | emprintf(pdev->memory, |
2758 | 0 | "Optional (Marked) Content not valid in this version of PDF, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n"); |
2759 | 0 | pdev->AbortPDFAX = true; |
2760 | 0 | pdev->PDFA = 0; |
2761 | 0 | break; |
2762 | 0 | } |
2763 | 0 | } else { |
2764 | 0 | emprintf(pdev->memory, |
2765 | 0 | "Optional (Marked) Content not valid in this version of PDF. Dropping feature to preserve compatibility\n"); |
2766 | 0 | return 0; |
2767 | 0 | } |
2768 | 0 | } |
2769 | 1.40k | } |
2770 | | |
2771 | | /* check propdict for {object name} syntax */ |
2772 | 7.29k | if (pdf_objname_is_valid(pairs[1].data, pairs[1].size)) |
2773 | 1.64k | { |
2774 | 1.64k | code = pdf_refer_named(pdev, &pairs[1], &pco); |
2775 | 1.64k | if(code < 0) return code; |
2776 | 1.64k | id = pco->id; |
2777 | 1.64k | } |
2778 | 5.64k | else /* << inline prop dict >> */ |
2779 | 5.64k | { |
2780 | | /* strip << and >> */ |
2781 | 5.64k | if ((pairs[1].data)[0]=='<'&&(pairs[1].data)[1]=='<') |
2782 | 5.64k | { |
2783 | 5.64k | int ix = 0; |
2784 | 5.64k | byte *p = (byte *)pairs[1].data; |
2785 | | |
2786 | | /* Fortunately we don't use the 'size' when freeing the memory |
2787 | | * so we can quickly copy the string content up two places and reduce |
2788 | | * the size by 2 to remove the '<<'. This saves an alloc and free of the |
2789 | | * string data. |
2790 | | */ |
2791 | 99.9k | for (ix = 0; ix < pairs[1].size - 2;ix++) |
2792 | 94.3k | p[ix] = pairs[1].data[ix + 2]; |
2793 | 5.64k | pairs[1].size-=2; |
2794 | | |
2795 | 5.64k | if ((pairs[1].data)[pairs[1].size-1]=='>'&&(pairs[1].data)[pairs[1].size-2]=='>') |
2796 | 5.64k | pairs[1].size-=2; |
2797 | | |
2798 | | /* convert inline propdict to C string with object names replaced by refs */ |
2799 | 5.64k | code = pdf_replace_names(pdev, &pairs[1], &pairs[1]); |
2800 | 5.64k | if (code<0) return code; |
2801 | 5.64k | cstring = (char *)gs_alloc_bytes(pdev->memory, (pairs[1].size + 1) * sizeof(unsigned char), |
2802 | 5.64k | "pdfmark_BDC"); |
2803 | 5.64k | if (cstring == NULL) |
2804 | 0 | return_error(gs_error_VMerror); |
2805 | | |
2806 | 5.64k | memcpy(cstring, pairs[1].data, pairs[1].size); |
2807 | 5.64k | cstring[pairs[1].size] = 0x00; |
2808 | | |
2809 | 5.64k | code = pdf_make_named_dict(pdev, NULL, (cos_dict_t**) &pco, true); |
2810 | 5.64k | if (code<0) return code; |
2811 | | |
2812 | | /* copy inline propdict to new object */ |
2813 | 5.64k | code = cos_dict_put_c_strings((cos_dict_t*) pco, cstring, ""); |
2814 | 5.64k | if(code < 0) return code; |
2815 | | |
2816 | 5.64k | COS_WRITE_OBJECT(pco, pdev, resourceProperties); |
2817 | | |
2818 | 5.64k | COS_RELEASE(pco, "pdfmark_BDC"); |
2819 | 5.64k | gs_free_object(pdev->memory, cstring, "pdfmark_BDC"); |
2820 | 5.64k | id = pco->id; |
2821 | 5.64k | } |
2822 | 0 | else { |
2823 | 0 | if ((pairs[1].data)[pairs[1].size-1]!='R') { |
2824 | 0 | if ((pairs[1].data)[pairs[1].size-2]==' ' && (pairs[1].data)[pairs[1].size-1]!='R') |
2825 | 0 | return_error(gs_error_rangecheck); |
2826 | 0 | if (sscanf((const char *)pairs[1].data, "%d 0 R", &id) != 1) |
2827 | 0 | return_error(gs_error_unknownerror); |
2828 | 0 | } |
2829 | |
|
2830 | 0 | } |
2831 | 5.64k | } |
2832 | | |
2833 | 7.29k | pres = pdf_find_resource_by_resource_id(pdev, resourceProperties, id); |
2834 | 7.29k | if (pres==0){ |
2835 | 5.98k | if (pco != NULL) { |
2836 | 5.98k | if ((code = pdf_alloc_resource(pdev, resourceProperties, pco->id, &(pco->pres), pco->id))<0) { |
2837 | 0 | return code; |
2838 | 0 | } |
2839 | 5.98k | } |
2840 | 0 | else |
2841 | 0 | if ((code = pdf_alloc_resource(pdev, resourceProperties, id, &pres, id))<0) |
2842 | 0 | return code; |
2843 | 5.98k | } |
2844 | | |
2845 | | /* We need to make sure we escape any white space in the tag */ |
2846 | 53.6k | for (i = 0;i < pairs[0].size;i++) { |
2847 | 46.3k | switch(pairs[0].data[i]) { |
2848 | 0 | case 0x00: |
2849 | 0 | case 0x09: |
2850 | 0 | case 0x0a: |
2851 | 0 | case 0x0c: |
2852 | 0 | case 0x0d: |
2853 | 0 | case 0x20: |
2854 | 0 | esc_size += 3; |
2855 | 0 | break; |
2856 | 46.3k | default: |
2857 | 46.3k | esc_size++; |
2858 | 46.3k | break; |
2859 | 46.3k | } |
2860 | 46.3k | } |
2861 | | |
2862 | 7.29k | cstring = (char *)gs_alloc_bytes(pdev->memory, (esc_size + 1) * sizeof(unsigned char), |
2863 | 7.29k | "pdfmark_BDC"); |
2864 | 7.29k | if (cstring == NULL) |
2865 | 0 | return_error(gs_error_VMerror); |
2866 | | |
2867 | 7.29k | esc_size = 0; |
2868 | 53.6k | for (i = 0;i < pairs[0].size;i++) { |
2869 | 46.3k | switch(pairs[0].data[i]) { |
2870 | 0 | case 0x00: |
2871 | 0 | case 0x09: |
2872 | 0 | case 0x0a: |
2873 | 0 | case 0x0c: |
2874 | 0 | case 0x0d: |
2875 | 0 | case 0x20: |
2876 | 0 | cstring[esc_size++] = '#'; |
2877 | 0 | cstring[esc_size++] = (pairs[0].data[i] >> 4) + 0x30; |
2878 | 0 | cstring[esc_size++] = (pairs[0].data[i] & 0x0f) + 0x30; |
2879 | 0 | break; |
2880 | 46.3k | default: |
2881 | 46.3k | cstring[esc_size++] = pairs[0].data[i]; |
2882 | 46.3k | break; |
2883 | 46.3k | } |
2884 | 46.3k | } |
2885 | 7.29k | cstring[esc_size] = 0x00; |
2886 | | |
2887 | | /* make sure we write to the correct stream */ |
2888 | 7.29k | code = pdf_open_contents(pdev, PDF_IN_STREAM); |
2889 | 7.29k | if (code < 0) return code; |
2890 | | |
2891 | 7.29k | pprints1(pdev->strm, "%s", cstring); /* write tag */ |
2892 | 7.29k | pprinti64d1(pdev->strm, "/R%"PRId64" BDC\n", id); |
2893 | 7.29k | if (pco != NULL) { |
2894 | 7.29k | pco->pres->where_used |= pdev->used_mask; |
2895 | 7.29k | if ((code = pdf_add_resource(pdev, pdev->substream_Resources, "/Properties", pco->pres))<0) |
2896 | 0 | return code; |
2897 | 7.29k | } |
2898 | 0 | else { |
2899 | 0 | pres->where_used |= pdev->used_mask; |
2900 | 0 | if ((code = pdf_add_resource(pdev, pdev->substream_Resources, "/Properties", pres))<0) |
2901 | 0 | return code; |
2902 | 0 | } |
2903 | | |
2904 | | |
2905 | 7.29k | gs_free_object(pdev->memory, cstring, "pdfmark_BDC"); |
2906 | 7.29k | return 0; |
2907 | 7.29k | } |
2908 | | |
2909 | | /* [ /EMC pdfmark */ |
2910 | | static int |
2911 | | pdfmark_EMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2912 | | const gs_matrix *pctm, const gs_param_string *objname) |
2913 | 12.0k | { |
2914 | 12.0k | int code; |
2915 | | |
2916 | 12.0k | code = pdf_open_contents(pdev, PDF_IN_STREAM); |
2917 | 12.0k | if (code < 0) return code; |
2918 | 12.0k | stream_puts(pdev->strm, "EMC\n"); |
2919 | | |
2920 | 12.0k | return 0; |
2921 | 12.0k | } |
2922 | | |
2923 | | /* ---------------- Document structure pdfmarks ---------------- */ |
2924 | | |
2925 | | /* [ newsubtype1 stdsubtype1 ... /StRoleMap pdfmark */ |
2926 | | static int |
2927 | | pdfmark_StRoleMap(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2928 | | const gs_matrix *pctm, const gs_param_string *objname) |
2929 | 0 | { |
2930 | 0 | return 0; /****** NOT IMPLEMENTED YET ******/ |
2931 | 0 | } |
2932 | | |
2933 | | /* [ class1 {attrobj1} ... /StClassMap pdfmark */ |
2934 | | static int |
2935 | | pdfmark_StClassMap(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2936 | | const gs_matrix *pctm, const gs_param_string *objname) |
2937 | 0 | { |
2938 | 0 | return 0; /****** NOT IMPLEMENTED YET ******/ |
2939 | 0 | } |
2940 | | |
2941 | | /* |
2942 | | * [ [/_objdef {objname}] /Subtype name [/Title string] [/Alt string] |
2943 | | * [/ID string] [/Class name] [/At index] [/Bookmark dict] [action_pairs...] |
2944 | | * /StPNE pdfmark |
2945 | | */ |
2946 | | static int |
2947 | | pdfmark_StPNE(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2948 | | const gs_matrix *pctm, const gs_param_string *objname) |
2949 | 0 | { |
2950 | 0 | return 0; /****** NOT IMPLEMENTED YET ******/ |
2951 | 0 | } |
2952 | | |
2953 | | /* [ [/Title string] [/Open bool] [action_pairs...] /StBookmarkRoot pdfmark */ |
2954 | | static int |
2955 | | pdfmark_StBookmarkRoot(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2956 | | const gs_matrix *pctm, const gs_param_string *objname) |
2957 | 0 | { |
2958 | 0 | return 0; /****** NOT IMPLEMENTED YET ******/ |
2959 | 0 | } |
2960 | | |
2961 | | /* [ [/E {elt}] /StPush pdfmark */ |
2962 | | static int |
2963 | | pdfmark_StPush(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2964 | | const gs_matrix *pctm, const gs_param_string *objname) |
2965 | 0 | { |
2966 | 0 | return 0; /****** NOT IMPLEMENTED YET ******/ |
2967 | 0 | } |
2968 | | |
2969 | | /* [ /StPop pdfmark */ |
2970 | | static int |
2971 | | pdfmark_StPop(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2972 | | const gs_matrix *pctm, const gs_param_string *objname) |
2973 | 0 | { |
2974 | 0 | return 0; /****** NOT IMPLEMENTED YET ******/ |
2975 | 0 | } |
2976 | | |
2977 | | /* [ /StPopAll pdfmark */ |
2978 | | static int |
2979 | | pdfmark_StPopAll(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2980 | | const gs_matrix *pctm, const gs_param_string *objname) |
2981 | 0 | { |
2982 | 0 | return 0; /****** NOT IMPLEMENTED YET ******/ |
2983 | 0 | } |
2984 | | |
2985 | | /* [ [/T tagname] [/At index] /StBMC pdfmark */ |
2986 | | static int |
2987 | | pdfmark_StBMC(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2988 | | const gs_matrix *pctm, const gs_param_string *objname) |
2989 | 0 | { |
2990 | 0 | return 0; /****** NOT IMPLEMENTED YET ******/ |
2991 | 0 | } |
2992 | | |
2993 | | /* [ [/P propdict] [/T tagname] [/At index] /StBDC pdfmark */ |
2994 | | static int |
2995 | | pdfmark_StBDC(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
2996 | | const gs_matrix *pctm, const gs_param_string *objname) |
2997 | 0 | { |
2998 | 0 | return 0; /****** NOT IMPLEMENTED YET ******/ |
2999 | 0 | } |
3000 | | |
3001 | | /* [ /Obj {obj} [/At index] /StOBJ pdfmark */ |
3002 | | static int |
3003 | | pdfmark_StOBJ(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
3004 | | const gs_matrix *pctm, const gs_param_string *objname) |
3005 | 0 | { |
3006 | 0 | return 0; /****** NOT IMPLEMENTED YET ******/ |
3007 | 0 | } |
3008 | | |
3009 | | /* [ /Obj {obj} /StAttr pdfmark */ |
3010 | | static int |
3011 | | pdfmark_StAttr(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
3012 | | const gs_matrix *pctm, const gs_param_string *objname) |
3013 | 0 | { |
3014 | 0 | return 0; /****** NOT IMPLEMENTED YET ******/ |
3015 | 0 | } |
3016 | | |
3017 | | /* [ /StoreName name /StStore pdfmark */ |
3018 | | static int |
3019 | | pdfmark_StStore(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
3020 | | const gs_matrix *pctm, const gs_param_string *objname) |
3021 | 0 | { |
3022 | 0 | return 0; /****** NOT IMPLEMENTED YET ******/ |
3023 | 0 | } |
3024 | | |
3025 | | /* [ /StoreName name /StRetrieve pdfmark */ |
3026 | | static int |
3027 | | pdfmark_StRetrieve(gx_device_pdf *pdev, gs_param_string *pairs, uint count, |
3028 | | const gs_matrix *pctm, const gs_param_string *objname) |
3029 | 0 | { |
3030 | 0 | return 0; /****** NOT IMPLEMENTED YET ******/ |
3031 | 0 | } |
3032 | | |
3033 | | static int |
3034 | | pdfmark_Metadata(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
3035 | | const gs_matrix * pctm, const gs_param_string * objname) |
3036 | 0 | { |
3037 | 0 | int i; |
3038 | 0 | gs_param_string key; |
3039 | 0 | char data[10] = "/Metadata"; |
3040 | |
|
3041 | 0 | if (pdev->CompatibilityLevel < 1.4) { |
3042 | 0 | dmprintf(pdev->pdf_memory, "Cannot add Metadata to PDF files with version earlier than 1.4.\n"); |
3043 | 0 | return 0; |
3044 | 0 | } |
3045 | 0 | if (pdev->PDFA != 0) |
3046 | 0 | dmprintf(pdev->pdf_memory, "Warning: PDF/A output requires specific metadata, this pdfmark has overridden that,\n output conformance cannot be guaranteed\n"); |
3047 | 0 | if (pdev->PDFX != 0) |
3048 | 0 | dmprintf(pdev->pdf_memory, "Warning: PDF/X output requires specific metadata, this pdfmark has overridden that,\n output conformance cannot be guaranteed\n"); |
3049 | |
|
3050 | 0 | if(pdev->ExtensionMetadata) { |
3051 | 0 | dmprintf(pdev->pdf_memory, "Extension metadata exists when /Metadata pdfmark executed, discarding extension metadata.\n"); |
3052 | 0 | gs_free_object(pdev->pdf_memory->stable_memory, pdev->ExtensionMetadata, "Extension metadata discarded on /Metadata pdfmark"); |
3053 | 0 | } |
3054 | |
|
3055 | 0 | if (!pdev->Catalog) { |
3056 | 0 | gs_param_string nstr; |
3057 | |
|
3058 | 0 | param_string_from_string(nstr, "{Catalog}"); |
3059 | 0 | pdf_create_named_dict(pdev, &nstr, &pdev->Catalog, 0L); |
3060 | 0 | } |
3061 | |
|
3062 | 0 | key.data = (const byte *)&data; |
3063 | 0 | key.size = 9; |
3064 | |
|
3065 | 0 | for (i = 0; i < count; i += 2) { |
3066 | 0 | if (pdf_key_eq(&pairs[i], "{Catalog}")) { |
3067 | 0 | return cos_dict_put_string(pdev->Catalog, key.data, key.size, |
3068 | 0 | pairs[i+1].data, pairs[i+1].size); |
3069 | 0 | } |
3070 | 0 | } |
3071 | 0 | return 0; |
3072 | 0 | } |
3073 | | |
3074 | | static int |
3075 | | pdfmark_Ext_Metadata(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
3076 | | const gs_matrix * pctm, const gs_param_string * objname) |
3077 | 0 | { |
3078 | 0 | int i, j=0; |
3079 | |
|
3080 | 0 | if (pdev->CompatibilityLevel < 1.4) { |
3081 | 0 | dmprintf(pdev->pdf_memory, "Cannot add Metadata to PDF files with version earlier than 1.4.\n"); |
3082 | 0 | return 0; |
3083 | 0 | } |
3084 | | |
3085 | 0 | if (cos_dict_find_c_key(pdev->Catalog, "/Metadata")) { |
3086 | 0 | dmprintf(pdev->pdf_memory, "Cannot add extension to Metadata specified with the /Metadata pdfmark\n"); |
3087 | 0 | return 0; |
3088 | 0 | } |
3089 | | |
3090 | 0 | if(pdev->ExtensionMetadata) { |
3091 | 0 | dmprintf(pdev->pdf_memory, "Extension metadata already defined, discarding old data.\n"); |
3092 | 0 | gs_free_object(pdev->pdf_memory->stable_memory, pdev->ExtensionMetadata, "Extension metadata"); |
3093 | 0 | } |
3094 | 0 | pdev->ExtensionMetadata = (char *)gs_alloc_bytes(pdev->pdf_memory->stable_memory, pairs[1].size - 1, "Extension metadata"); |
3095 | 0 | if (pdev->ExtensionMetadata == NULL) |
3096 | 0 | return_error(gs_error_VMerror); |
3097 | | |
3098 | 0 | memset(pdev->ExtensionMetadata, 0x00, pairs[1].size - 1); |
3099 | 0 | for (i=1;i<pairs[1].size - 1;i++) { |
3100 | 0 | if (pairs[1].data[i] == '\\') { |
3101 | 0 | switch(pairs[1].data[i+1]) { |
3102 | 0 | case '(': |
3103 | 0 | case ')': |
3104 | 0 | case '\\': |
3105 | 0 | pdev->ExtensionMetadata[j++] = pairs[1].data[i+1]; |
3106 | 0 | i++; |
3107 | 0 | break; |
3108 | 0 | case 'r': |
3109 | 0 | pdev->ExtensionMetadata[j++] = 0x0D; |
3110 | 0 | i++; |
3111 | 0 | break; |
3112 | 0 | case 'n': |
3113 | 0 | pdev->ExtensionMetadata[j++] = 0x0A; |
3114 | 0 | i++; |
3115 | 0 | break; |
3116 | 0 | case 't': |
3117 | 0 | pdev->ExtensionMetadata[j++] = 0x09; |
3118 | 0 | i++; |
3119 | 0 | break; |
3120 | 0 | case 'b': |
3121 | 0 | pdev->ExtensionMetadata[j++] = 0x08; |
3122 | 0 | i++; |
3123 | 0 | break; |
3124 | 0 | case 'f': |
3125 | 0 | pdev->ExtensionMetadata[j++] = 0x0C; |
3126 | 0 | i++; |
3127 | 0 | break; |
3128 | 0 | default: |
3129 | 0 | if((pairs[1].data[i+1]) >= 0x30 && (pairs[1].data[i+1]) <= 0x39) { |
3130 | 0 | pdev->ExtensionMetadata[j++] = (pairs[1].data[i+1] - 0x30) * 64 + (pairs[1].data[i+2] - 0x30) * 8 + (pairs[1].data[i+3] - 0x30); |
3131 | 0 | i += 3; |
3132 | 0 | } else |
3133 | 0 | pdev->ExtensionMetadata[j++] = pairs[1].data[i]; |
3134 | 0 | break; |
3135 | 0 | } |
3136 | 0 | } else |
3137 | 0 | pdev->ExtensionMetadata[j++] = pairs[1].data[i]; |
3138 | 0 | } |
3139 | 0 | return 0; |
3140 | 0 | } |
3141 | | |
3142 | | static int |
3143 | | pdfmark_OCProperties(gx_device_pdf * pdev, gs_param_string * pairs, uint count, |
3144 | | const gs_matrix * pctm, const gs_param_string * objname) |
3145 | 60 | { |
3146 | 60 | char *str; |
3147 | | |
3148 | 60 | if (pdev->CompatibilityLevel < 1.4999) { |
3149 | 0 | if (pdev->PDFA) { |
3150 | 0 | switch (pdev->PDFACompatibilityPolicy) { |
3151 | 0 | case 0: |
3152 | 0 | emprintf(pdev->memory, |
3153 | 0 | "Optional Content Properties not valid in this version of PDF, reverting to normal PDF output\n\n"); |
3154 | 0 | pdev->AbortPDFAX = true; |
3155 | 0 | pdev->PDFA = 0; |
3156 | 0 | break; |
3157 | 0 | case 1: |
3158 | 0 | emprintf(pdev->memory, |
3159 | 0 | "Optional Content Properties not valid in this version of PDF. Dropping feature to preserve PDF/A compatibility\n"); |
3160 | 0 | break; |
3161 | 0 | case 2: |
3162 | 0 | emprintf(pdev->memory, |
3163 | 0 | "Optional Content Properties not valid in this version of PDF, aborting conversion\n"); |
3164 | 0 | return_error (gs_error_typecheck); |
3165 | 0 | break; |
3166 | 0 | default: |
3167 | 0 | emprintf(pdev->memory, |
3168 | 0 | "Optional Content Properties not valid in this version of PDF, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n"); |
3169 | 0 | pdev->AbortPDFAX = true; |
3170 | 0 | pdev->PDFA = 0; |
3171 | 0 | break; |
3172 | 0 | } |
3173 | 0 | } else { |
3174 | 0 | emprintf(pdev->memory, |
3175 | 0 | "Optional Content Properties not valid in this version of PDF. Dropping feature to preserve compatibility\n"); |
3176 | 0 | } |
3177 | 60 | } else { |
3178 | 60 | str = (char *)gs_alloc_bytes(pdev->memory, pairs[0].size + 1, "pdfmark_OCProperties"); |
3179 | 60 | if (str == NULL) |
3180 | 0 | return_error(gs_error_VMerror); |
3181 | | |
3182 | 60 | memset(str, 0x00, pairs[0].size + 1); |
3183 | 60 | memcpy(str, pairs[0].data, pairs[0].size); |
3184 | | |
3185 | 60 | (void)cos_dict_put_c_key_string(pdev->Catalog, "/OCProperties", |
3186 | 60 | (byte *)str, strlen(str)); |
3187 | | |
3188 | 60 | gs_free_object(pdev->memory, str, "pdfmark_OCProperties"); |
3189 | 60 | } |
3190 | 60 | return 0; |
3191 | 60 | } |
3192 | | |
3193 | | /* ---------------- Dispatch ---------------- */ |
3194 | | |
3195 | | /* |
3196 | | * Define the pdfmark types we know about. |
3197 | | */ |
3198 | | static const pdfmark_name mark_names[] = |
3199 | | { |
3200 | | /* Miscellaneous. */ |
3201 | | {"ANN", pdfmark_ANN, PDFMARK_NAMEABLE}, |
3202 | | {"LNK", pdfmark_LNK, PDFMARK_NAMEABLE}, |
3203 | | {"OUT", pdfmark_OUT, 0}, |
3204 | | {"ARTICLE", pdfmark_ARTICLE, 0}, |
3205 | | {"DEST", pdfmark_DEST, PDFMARK_NAMEABLE}, |
3206 | | {"EMBED", pdfmark_EMBED, PDFMARK_NAMEABLE}, |
3207 | | {"PS", pdfmark_PS, PDFMARK_NAMEABLE}, |
3208 | | {"PAGES", pdfmark_PAGES, 0}, |
3209 | | {"PAGE", pdfmark_PAGE, 0}, |
3210 | | {"PAGELABEL", pdfmark_PAGELABEL, 0}, |
3211 | | {"DOCINFO", pdfmark_DOCINFO, 0}, |
3212 | | {"DOCVIEW", pdfmark_DOCVIEW, 0}, |
3213 | | /* Named objects. */ |
3214 | | {"BP", pdfmark_BP, PDFMARK_NAMEABLE | PDFMARK_TRUECTM}, |
3215 | | {"EP", pdfmark_EP, 0}, |
3216 | | {"SP", pdfmark_SP, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME | PDFMARK_TRUECTM}, |
3217 | | {"OBJ", pdfmark_OBJ, PDFMARK_NAMEABLE}, |
3218 | | {"PUT", pdfmark_PUT, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME}, |
3219 | | {".PUTDICT", pdfmark_PUTDICT, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME}, |
3220 | | {".PUTINTERVAL", pdfmark_PUTINTERVAL, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME}, |
3221 | | {".PUTSTREAM", pdfmark_PUTSTREAM, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME | |
3222 | | PDFMARK_NO_REFS}, |
3223 | | {"APPEND", pdfmark_APPEND, PDFMARK_KEEP_NAME}, |
3224 | | {"CLOSE", pdfmark_CLOSE, PDFMARK_ODD_OK | PDFMARK_KEEP_NAME}, |
3225 | | {"NamespacePush", pdfmark_NamespacePush, 0}, |
3226 | | {"NamespacePop", pdfmark_NamespacePop, 0}, |
3227 | | {"NI", pdfmark_NI, PDFMARK_NAMEABLE}, |
3228 | | /* Marked content. */ |
3229 | | {"MP", pdfmark_MP, PDFMARK_ODD_OK}, |
3230 | | {"DP", pdfmark_DP, 0}, |
3231 | | {"BMC", pdfmark_BMC, PDFMARK_ODD_OK}, |
3232 | | {"BDC", pdfmark_BDC, PDFMARK_NO_REFS}, |
3233 | | {"EMC", pdfmark_EMC, 0}, |
3234 | | /* Document structure. */ |
3235 | | {"StRoleMap", pdfmark_StRoleMap, 0}, |
3236 | | {"StClassMap", pdfmark_StClassMap, 0}, |
3237 | | {"StPNE", pdfmark_StPNE, PDFMARK_NAMEABLE}, |
3238 | | {"StBookmarkRoot", pdfmark_StBookmarkRoot, 0}, |
3239 | | {"StPush", pdfmark_StPush, 0}, |
3240 | | {"StPop", pdfmark_StPop, 0}, |
3241 | | {"StPopAll", pdfmark_StPopAll, 0}, |
3242 | | {"StBMC", pdfmark_StBMC, 0}, |
3243 | | {"StBDC", pdfmark_StBDC, 0}, |
3244 | | /* EMC is listed under "Marked content" above. */ |
3245 | | {"StOBJ", pdfmark_StOBJ, 0}, |
3246 | | {"StAttr", pdfmark_StAttr, 0}, |
3247 | | {"StStore", pdfmark_StStore, 0}, |
3248 | | {"StRetrieve", pdfmark_StRetrieve, 0}, |
3249 | | /* Metadata and extension */ |
3250 | | {"Metadata", pdfmark_Metadata, 0}, |
3251 | | {"Ext_Metadata", pdfmark_Ext_Metadata, 0}, |
3252 | | {"OCProperties", pdfmark_OCProperties, PDFMARK_ODD_OK}, |
3253 | | /* End of list. */ |
3254 | | {0, 0} |
3255 | | }; |
3256 | | |
3257 | | /* Process a pdfmark. */ |
3258 | | int |
3259 | | pdfmark_process(gx_device_pdf * pdev, const gs_param_string_array * pma) |
3260 | 29.0k | { |
3261 | 29.0k | const gs_param_string *data = pma->data; |
3262 | 29.0k | uint size = pma->size; |
3263 | 29.0k | const gs_param_string *pts = &data[size - 1]; |
3264 | 29.0k | const gs_param_string *objname = 0; |
3265 | 29.0k | gs_matrix ctm; |
3266 | 29.0k | const pdfmark_name *pmn; |
3267 | 29.0k | int code = 0; |
3268 | | |
3269 | 29.0k | if (size < 2) |
3270 | 0 | return_error(gs_error_stackunderflow); |
3271 | | |
3272 | 29.0k | { int cnt, len = pts[-1].size; |
3273 | 29.0k | char buf[200]; /* 6 doubles should fit (%g == -0.14285714285714285e-101 = 25 chars) */ |
3274 | | |
3275 | 29.0k | if (len > sizeof(buf) - 1) |
3276 | 0 | return_error(gs_error_rangecheck); |
3277 | 29.0k | memcpy(buf, pts[-1].data, len); |
3278 | 29.0k | buf[len] = 0; |
3279 | 29.0k | cnt = sscanf(buf, "[%g %g %g %g %g %g]", |
3280 | 29.0k | &ctm.xx, &ctm.xy, &ctm.yx, &ctm.yy, &ctm.tx, &ctm.ty); |
3281 | 29.0k | if (cnt != 6) |
3282 | 5 | return_error(gs_error_rangecheck); |
3283 | 29.0k | } |
3284 | 29.0k | size -= 2; /* remove CTM & pdfmark name */ |
3285 | 689k | for (pmn = mark_names; pmn->mname != 0; ++pmn) |
3286 | 689k | if (pdf_key_eq(pts, pmn->mname)) { |
3287 | 29.0k | gs_memory_t *mem = pdev->pdf_memory; |
3288 | 29.0k | int odd_ok = (pmn->options & PDFMARK_ODD_OK) != 0; |
3289 | 29.0k | gs_param_string *pairs; |
3290 | 29.0k | int j, index; |
3291 | | |
3292 | | /* |
3293 | | * Our coordinate system is scaled so that user space is always |
3294 | | * default user space. Adjust the CTM to match this, except if this |
3295 | | * particular pdfmark requires the "true" CTM. |
3296 | | */ |
3297 | 29.0k | if (pmn->options & PDFMARK_TRUECTM) |
3298 | 29.0k | DO_NOTHING; |
3299 | 29.0k | else { |
3300 | 29.0k | double xscale = 72.0 / pdev->HWResolution[0], |
3301 | 29.0k | yscale = 72.0 / pdev->HWResolution[1]; |
3302 | 29.0k | ctm.xx *= xscale, ctm.xy *= yscale; |
3303 | 29.0k | ctm.yx *= xscale, ctm.yy *= yscale; |
3304 | 29.0k | ctm.tx *= xscale, ctm.ty *= yscale; |
3305 | 29.0k | } |
3306 | 29.0k | if (size & !odd_ok) |
3307 | 0 | return_error(gs_error_rangecheck); |
3308 | 29.0k | if (pmn->options & PDFMARK_NAMEABLE) { |
3309 | | /* Look for an object name. */ |
3310 | 6.82k | for (j = 0; j < size; j += 2) { |
3311 | 6.26k | if (pdf_key_eq(&data[j], "/_objdef")) { |
3312 | 1.11k | objname = &data[j + 1]; |
3313 | 1.11k | if (!pdf_objname_is_valid(objname->data, |
3314 | 1.11k | objname->size) |
3315 | 1.11k | ) |
3316 | 0 | return_error(gs_error_rangecheck); |
3317 | | /* Save the pairs without the name. */ |
3318 | 1.11k | size -= 2; |
3319 | 1.11k | pairs = (gs_param_string *) |
3320 | 1.11k | gs_alloc_byte_array(mem, size, |
3321 | 1.11k | sizeof(gs_param_string), |
3322 | 1.11k | "pdfmark_process(pairs)"); |
3323 | 1.11k | if (!pairs) |
3324 | 0 | return_error(gs_error_VMerror); |
3325 | | |
3326 | 3.31k | for (index=0;index < size;index++) |
3327 | 2.20k | pairs[index].data = NULL; |
3328 | 1.11k | for (index=0;index < j;index++) { |
3329 | 0 | pairs[index].data = gs_alloc_bytes(mem, data[index].size, "pdfmark_process(pairs)"); |
3330 | 0 | if (pairs[index].data == NULL) |
3331 | 0 | goto error; |
3332 | 0 | memcpy((byte *)pairs[index].data, data[index].data, data[index].size); |
3333 | 0 | pairs[index].size = data[index].size; |
3334 | 0 | pairs[index].persistent = 1; |
3335 | 0 | } |
3336 | 3.31k | for (index=j+2;index < size + 2;index++) { |
3337 | 2.20k | pairs[index - 2].data = gs_alloc_bytes(mem, data[index].size, "pdfmark_process(pairs)"); |
3338 | 2.20k | if (pairs[index - 2].data == NULL) |
3339 | 0 | goto error; |
3340 | 2.20k | memcpy((byte *)pairs[index - 2].data, data[index].data, data[index].size); |
3341 | 2.20k | pairs[index - 2].size = data[index].size; |
3342 | 2.20k | pairs[index - 2].persistent = 1; |
3343 | 2.20k | } |
3344 | 1.11k | goto copied; |
3345 | 1.11k | } |
3346 | 6.26k | } |
3347 | 1.67k | } |
3348 | | /* Save all the pairs. */ |
3349 | 27.9k | pairs = (gs_param_string *) |
3350 | 27.9k | gs_alloc_byte_array(mem, size, sizeof(gs_param_string), |
3351 | 27.9k | "pdfmark_process(pairs)"); |
3352 | 27.9k | if (!pairs) |
3353 | 0 | return_error(gs_error_VMerror); |
3354 | 70.9k | for (j=0;j < size;j++) |
3355 | 43.0k | pairs[j].data = NULL; |
3356 | 70.9k | for (j=0;j < size;j++) { |
3357 | 43.0k | pairs[j].data = gs_alloc_bytes(mem, data[j].size, "pdfmark_process(pairs)"); |
3358 | 43.0k | if (pairs[j].data == NULL) |
3359 | 0 | goto error; |
3360 | 43.0k | memcpy((byte *)pairs[j].data, data[j].data, data[j].size); |
3361 | 43.0k | pairs[j].size = data[j].size; |
3362 | 43.0k | pairs[j].persistent = 1; |
3363 | 43.0k | } |
3364 | 29.0k | copied: /* Substitute object references for names. */ |
3365 | 29.0k | if (!(pmn->options & PDFMARK_NO_REFS)) { |
3366 | 21.5k | for (j = (pmn->options & PDFMARK_KEEP_NAME ? 1 : 1 - odd_ok); |
3367 | 38.7k | j < size; j += 2 - odd_ok |
3368 | 21.5k | ) { |
3369 | 17.1k | code = pdf_replace_names(pdev, &pairs[j], &pairs[j]); |
3370 | 17.1k | if (code < 0) { |
3371 | 0 | gs_free_object(mem, pairs, "pdfmark_process(pairs)"); |
3372 | 0 | return code; |
3373 | 0 | } |
3374 | 17.1k | } |
3375 | 21.5k | } |
3376 | 29.0k | code = (*pmn->proc) (pdev, pairs, size, &ctm, objname); |
3377 | 29.0k | error: |
3378 | 74.2k | for (j=0;j < size;j++) |
3379 | 45.2k | gs_free_object(mem, (byte *)pairs[j].data, "pdfmark_process(pairs)"); |
3380 | 29.0k | gs_free_object(mem, pairs, "pdfmark_process(pairs)"); |
3381 | 29.0k | break; |
3382 | 29.0k | } |
3383 | 29.0k | return code; |
3384 | 29.0k | } |