1 /* Copyright (C) 2000 Artifex Software Inc. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: zdscpars.c,v 1.17 2004/11/17 19:48:01 ray Exp $ */
18 /* C language interface routines to DSC parser */
19
20 /*
21 * The DSC parser consists of three pieces. The first piece is a DSC parser
22 * which was coded by Russell Lang (dscparse.c and dscparse.h). The second
23 * piece is this module. These two are sufficient to parse DSC comments
24 * and make them available to a client written in PostScript. The third
25 * piece is a PostScript language module (gs_dscp.ps) that uses certain
26 * comments to affect the interpretation of the file.
27 *
28 * The .initialize_dsc_parser operator defined in this file creates an
29 * instance of Russell's parser, and puts it in a client-supplied dictionary
30 * under a known name (/DSC_struct).
31 *
32 * When the PostScript scanner sees a possible DSC comment (first characters
33 * in a line are %%), it calls the procedure that is the value of the user
34 * parameter ProcessDSCComments. This procedure should loads the dictionary
35 * that was passed to .initialize_dsc_parser, and then call the
36 * .parse_dsc_comments operator defined in this file.
37 *
38 * These two operators comprise the interface between PostScript and C code.
39 *
40 * There is a "feature" named usedsc that loads a PostScript file
41 * (gs_dscp.ps), which installs a simple framework for processing DSC
42 * comments and having them affect interpretation of the file (e.g., by
43 * setting page device parameters). See gs_dscp.ps for more information.
44 *
45 * .parse_dsc_comments pulls the comment string off of the stack and passes
46 * it to Russell's parser. That parser parses the comment and puts any
47 * parameter values into a DSC structure. That parser also returns a code
48 * which indicates which type of comment was found. .parse_dsc_comments
49 * looks at the return code and transfers any interesting parameters from
50 * the DSC structure into key value pairs in the dsc_dict dictionary. It
51 * also translates the comment type code into a key name (comment name).
52 * The key name is placed on the operand stack. Control then returns to
53 * PostScript code, which can pull the key name from the operand stack and
54 * use it to determine what further processing needs to be done at the PS
55 * language level.
56 *
57 * To add support for new DSC comments:
58 *
59 * 1. Verify that Russell's parser supports the comment. If not, then add
60 * the required support.
61 *
62 * 2. Add an entry into DSCcmdlist. This table contains three values for
63 * each command that we support. The first is Russell's return code for
64 * the command. The second is the key name that we pass back on the
65 * operand stack. (Thus this table translates Russell's codes into key
66 * names for the PostScript client.) The third entry is a pointer to a
67 * local function for transferring values from Russell's DSC structure
68 * into key/value pairs in dsc_dict.
69 *
70 * 3. Create the local function described at the end of the last item.
71 * There are some support routines like dsc_put_integer() and
72 * dsc_put_string() to help implement these functions.
73 *
74 * 4. If the usedsc feature should recognize and process the new comments,
75 * add a processing routine into the dictionary DSCparserprocs in
76 * gs_dscp.ps. The keys in this dictionary are the key names described
77 * in item 2 above.
78 */
79
80 #include "ghost.h"
81 #include "string_.h"
82 #include "memory_.h"
83 #include "gsstruct.h"
84 #include "ialloc.h"
85 #include "iname.h"
86 #include "istack.h" /* for iparam.h */
87 #include "iparam.h"
88 #include "ivmspace.h"
89 #include "oper.h"
90 #include "estack.h"
91 #include "store.h"
92 #include "idict.h"
93 #include "iddict.h"
94 #include "dscparse.h"
95
96 /*
97 * Declare the structure we use to represent an instance of the parser
98 * as a t_struct. Currently it just saves a pointer to Russell's
99 * data structure.
100 */
101 typedef struct dsc_data_s {
102 CDSC *dsc_data_ptr;
103 } dsc_data_t;
104
105 /* Structure descriptors */
106 private void dsc_finalize(void *vptr);
107 gs_private_st_simple_final(st_dsc_data_t, dsc_data_t, "dsc_data_struct", dsc_finalize);
108
109 /* Define the key name for storing the instance pointer in a dictionary. */
110 private const char * const dsc_dict_name = "DSC_struct";
111
112 /* ---------------- Initialization / finalization ---------------- */
113
114 /*
115 * If we return CDSC_OK then Russell's parser will make it best guess when
116 * it encounters unexpected comment situations.
117 */
118 private int
dsc_error_handler(void * caller_data,CDSC * dsc,unsigned int explanation,const char * line,unsigned int line_len)119 dsc_error_handler(void *caller_data, CDSC *dsc, unsigned int explanation,
120 const char *line, unsigned int line_len)
121 {
122 return CDSC_OK;
123 }
124
125 /*
126 * This operator creates a new, initialized instance of the DSC parser.
127 */
128 /* <dict> .initialize_dsc_parser - */
129 private int
zinitialize_dsc_parser(i_ctx_t * i_ctx_p)130 zinitialize_dsc_parser(i_ctx_t *i_ctx_p)
131 {
132 ref local_ref;
133 int code;
134 os_ptr const op = osp;
135 dict * const pdict = op->value.pdict;
136 gs_memory_t * const mem = (gs_memory_t *)dict_memory(pdict);
137 dsc_data_t * const data =
138 gs_alloc_struct(mem, dsc_data_t, &st_dsc_data_t,
139 "DSC parser init");
140
141 data->dsc_data_ptr = dsc_init((void *) "Ghostscript DSC parsing");
142 if (!data->dsc_data_ptr)
143 return_error(e_VMerror);
144 dsc_set_error_function(data->dsc_data_ptr, dsc_error_handler);
145 make_astruct(&local_ref, a_readonly | r_space(op), (byte *) data);
146 code = idict_put_string(op, dsc_dict_name, &local_ref);
147 if (code >= 0)
148 pop(1);
149 return code;
150 }
151
152 /*
153 * This routine will free the memory associated with Russell's parser.
154 */
155 private void
dsc_finalize(void * vptr)156 dsc_finalize(void *vptr)
157 {
158 dsc_data_t * const st = vptr;
159
160 if (st->dsc_data_ptr)
161 dsc_free(st->dsc_data_ptr);
162 st->dsc_data_ptr = NULL;
163 }
164
165
166 /* ---------------- Parsing ---------------- */
167
168 /* ------ Utilities for returning values ------ */
169
170 /* Return an integer value. */
171 private int
dsc_put_int(gs_param_list * plist,const char * keyname,int value)172 dsc_put_int(gs_param_list *plist, const char *keyname, int value)
173 {
174 return param_write_int(plist, keyname, &value);
175 }
176
177 /* Return a string value. */
178 private int
dsc_put_string(gs_param_list * plist,const char * keyname,const char * string)179 dsc_put_string(gs_param_list *plist, const char *keyname,
180 const char *string)
181 {
182 gs_param_string str;
183
184 param_string_from_transient_string(str, string);
185 return param_write_string(plist, keyname, &str);
186 }
187
188 /* Return a BoundingBox value. */
189 private int
dsc_put_bounding_box(gs_param_list * plist,const char * keyname,const CDSCBBOX * pbbox)190 dsc_put_bounding_box(gs_param_list *plist, const char *keyname,
191 const CDSCBBOX *pbbox)
192 {
193 /* pbbox is NULL iff the bounding box values was "(atend)". */
194 int values[4];
195 gs_param_int_array va;
196
197 if (!pbbox)
198 return 0;
199 values[0] = pbbox->llx;
200 values[1] = pbbox->lly;
201 values[2] = pbbox->urx;
202 values[3] = pbbox->ury;
203 va.data = values;
204 va.size = 4;
205 va.persistent = false;
206 return param_write_int_array(plist, keyname, &va);
207 }
208
209 /* ------ Return values for individual comment types ------ */
210
211 /*
212 * These routines transfer data from the C structure into Postscript
213 * key/value pairs in a dictionary.
214 */
215 private int
dsc_adobe_header(gs_param_list * plist,const CDSC * pData)216 dsc_adobe_header(gs_param_list *plist, const CDSC *pData)
217 {
218 return dsc_put_int(plist, "EPSF", (int)(pData->epsf? 1: 0));
219 }
220
221 private int
dsc_creator(gs_param_list * plist,const CDSC * pData)222 dsc_creator(gs_param_list *plist, const CDSC *pData)
223 {
224 return dsc_put_string(plist, "Creator", pData->dsc_creator );
225 }
226
227 private int
dsc_creation_date(gs_param_list * plist,const CDSC * pData)228 dsc_creation_date(gs_param_list *plist, const CDSC *pData)
229 {
230 return dsc_put_string(plist, "CreationDate", pData->dsc_date );
231 }
232
233 private int
dsc_title(gs_param_list * plist,const CDSC * pData)234 dsc_title(gs_param_list *plist, const CDSC *pData)
235 {
236 return dsc_put_string(plist, "Title", pData->dsc_title );
237 }
238
239 private int
dsc_for(gs_param_list * plist,const CDSC * pData)240 dsc_for(gs_param_list *plist, const CDSC *pData)
241 {
242 return dsc_put_string(plist, "For", pData->dsc_for);
243 }
244
245 private int
dsc_bounding_box(gs_param_list * plist,const CDSC * pData)246 dsc_bounding_box(gs_param_list *plist, const CDSC *pData)
247 {
248 return dsc_put_bounding_box(plist, "BoundingBox", pData->bbox);
249 }
250
251 private int
dsc_page(gs_param_list * plist,const CDSC * pData)252 dsc_page(gs_param_list *plist, const CDSC *pData)
253 {
254 int page_num = pData->page_count;
255
256 if (page_num) /* If we have page information */
257 return dsc_put_int(plist, "PageNum",
258 pData->page[page_num - 1].ordinal );
259 else /* No page info - so return page=0 */
260 return dsc_put_int(plist, "PageNum", 0 );
261 }
262
263 private int
dsc_pages(gs_param_list * plist,const CDSC * pData)264 dsc_pages(gs_param_list *plist, const CDSC *pData)
265 {
266 return dsc_put_int(plist, "NumPages", pData->page_pages);
267 }
268
269 private int
dsc_page_bounding_box(gs_param_list * plist,const CDSC * pData)270 dsc_page_bounding_box(gs_param_list *plist, const CDSC *pData)
271 {
272 return dsc_put_bounding_box(plist, "PageBoundingBox", pData->page_bbox);
273 }
274
275 /*
276 * Translate Russell's defintions of orientation into Postscript's.
277 */
278 private int
convert_orient(CDSC_ORIENTATION_ENUM orient)279 convert_orient(CDSC_ORIENTATION_ENUM orient)
280 {
281 switch (orient) {
282 case CDSC_PORTRAIT: return 0;
283 case CDSC_LANDSCAPE: return 1;
284 case CDSC_UPSIDEDOWN: return 2;
285 case CDSC_SEASCAPE: return 3;
286 default: return -1;
287 }
288 }
289
290 private int
dsc_page_orientation(gs_param_list * plist,const CDSC * pData)291 dsc_page_orientation(gs_param_list *plist, const CDSC *pData)
292 {
293 int page_num = pData->page_count;
294
295 /*
296 * The PageOrientation comment might be either in the 'defaults'
297 * section or in a page section. If in the defaults then fhe value
298 * will be in page_orientation.
299 */
300 if (page_num && pData->page[page_num - 1].orientation != CDSC_ORIENT_UNKNOWN)
301 return dsc_put_int(plist, "PageOrientation",
302 convert_orient(pData->page[page_num - 1].orientation));
303 else
304 return dsc_put_int(plist, "Orientation",
305 convert_orient(pData->page_orientation));
306 }
307
308 private int
dsc_orientation(gs_param_list * plist,const CDSC * pData)309 dsc_orientation(gs_param_list *plist, const CDSC *pData)
310 {
311 return dsc_put_int(plist, "Orientation",
312 convert_orient(pData->page_orientation));
313 }
314
315 private int
dsc_viewing_orientation(gs_param_list * plist,const CDSC * pData)316 dsc_viewing_orientation(gs_param_list *plist, const CDSC *pData)
317 {
318 int page_num = pData->page_count;
319 const char *key;
320 const CDSCCTM *pctm;
321 float values[4];
322 gs_param_float_array va;
323
324 /*
325 * As for PageOrientation, ViewingOrientation may be either in the
326 * 'defaults' section or in a page section.
327 */
328 if (page_num && pData->page[page_num - 1].viewing_orientation != NULL) {
329 key = "PageViewingOrientation";
330 pctm = pData->page[page_num - 1].viewing_orientation;
331 } else {
332 key = "ViewingOrientation";
333 pctm = pData->viewing_orientation;
334 }
335 values[0] = pctm->xx;
336 values[1] = pctm->xy;
337 values[2] = pctm->yx;
338 values[3] = pctm->yy;
339 va.data = values;
340 va.size = 4;
341 va.persistent = false;
342 return param_write_float_array(plist, key, &va);
343 }
344
345 /*
346 * This list is used to translate the commment code returned
347 * from Russell's DSC parser, define a name, and a parameter procedure.
348 */
349 typedef struct cmd_list_s {
350 int code; /* Russell's DSC parser code (see dsc.h) */
351 const char *comment_name; /* A name to be returned to postscript caller */
352 int (*dsc_proc) (gs_param_list *, const CDSC *);
353 /* A routine for transferring parameter values
354 from C data structure to postscript dictionary
355 key/value pairs. */
356 } cmdlist_t;
357
358 private const cmdlist_t DSCcmdlist[] = {
359 { CDSC_PSADOBE, "Header", dsc_adobe_header },
360 { CDSC_CREATOR, "Creator", dsc_creator },
361 { CDSC_CREATIONDATE, "CreationDate", dsc_creation_date },
362 { CDSC_TITLE, "Title", dsc_title },
363 { CDSC_FOR, "For", dsc_for },
364 { CDSC_BOUNDINGBOX, "BoundingBox", dsc_bounding_box },
365 { CDSC_ORIENTATION, "Orientation", dsc_orientation },
366 { CDSC_BEGINDEFAULTS, "BeginDefaults", NULL },
367 { CDSC_ENDDEFAULTS, "EndDefaults", NULL },
368 { CDSC_PAGE, "Page", dsc_page },
369 { CDSC_PAGES, "Pages", dsc_pages },
370 { CDSC_PAGEORIENTATION, "PageOrientation", dsc_page_orientation },
371 { CDSC_PAGEBOUNDINGBOX, "PageBoundingBox", dsc_page_bounding_box },
372 { CDSC_VIEWINGORIENTATION, "ViewingOrientation", dsc_viewing_orientation },
373 { CDSC_EOF, "EOF", NULL },
374 { 0, "NOP", NULL } /* Table terminator */
375 };
376
377 /* ------ Parser interface ------ */
378
379 /*
380 * There are a few comments that we do not want to send to Russell's
381 * DSC parser. If we send the data block type comments, Russell's
382 * parser will want to skip the specified block of data. This is not
383 * appropriate for our situation. So we use this list to check for this
384 * type of comment and do not send it to Russell's parser if found.
385 */
386 private const char * const BadCmdlist[] = {
387 "%%BeginData:",
388 "%%EndData",
389 "%%BeginBinary:",
390 "%%EndBinary",
391 NULL /* List terminator */
392 };
393
394 /* See comments at start of module for description. */
395 /* <dict> <string> .parse_dsc_comments <dict> <dsc code> */
396 private int
zparse_dsc_comments(i_ctx_t * i_ctx_p)397 zparse_dsc_comments(i_ctx_t *i_ctx_p)
398 {
399 #define MAX_DSC_MSG_SIZE (DSC_LINE_LENGTH + 4) /* Allow for %% and CR/LF */
400 os_ptr const opString = osp;
401 os_ptr const opDict = opString - 1;
402 uint ssize;
403 int comment_code, code;
404 char dsc_buffer[MAX_DSC_MSG_SIZE + 2];
405 const cmdlist_t *pCmdList = DSCcmdlist;
406 const char * const *pBadList = BadCmdlist;
407 ref * pvalue;
408 CDSC * dsc_data = NULL;
409 dict_param_list list;
410
411 /*
412 * Verify operand types and length of DSC comment string. If a comment
413 * is too long then we simply truncate it. Russell's parser gets to
414 * handle any errors that may result. (Crude handling but the comment
415 * is bad, so ...).
416 */
417 check_type(*opString, t_string);
418 check_dict_write(*opDict);
419 ssize = r_size(opString);
420 if (ssize > MAX_DSC_MSG_SIZE) /* need room for EOL + \0 */
421 ssize = MAX_DSC_MSG_SIZE;
422 /*
423 * Pick up the comment string to be parsed.
424 */
425 memcpy(dsc_buffer, opString->value.bytes, ssize);
426 dsc_buffer[ssize] = 0x0d; /* Russell wants a 'line end' */
427 dsc_buffer[ssize + 1] = 0; /* Terminate string */
428 /*
429 * Skip data block comments (see comments in front of BadCmdList).
430 */
431 while (*pBadList && strncmp(*pBadList, dsc_buffer, strlen(*pBadList)))
432 pBadList++;
433 if (*pBadList) { /* If found in list, then skip comment */
434 comment_code = 0; /* Force NOP */
435 }
436 else {
437 /*
438 * Parse comments - use Russell Lang's DSC parser. We need to get
439 * data area for Russell Lang's parser. Note: We have saved the
440 * location of the data area for the parser in our DSC dict.
441 */
442 code = dict_find_string(opDict, dsc_dict_name, &pvalue);
443 dsc_data = r_ptr(pvalue, dsc_data_t)->dsc_data_ptr;
444 if (code < 0)
445 return code;
446 comment_code = dsc_scan_data(dsc_data, dsc_buffer, ssize + 1);
447 if_debug1('%', "[%%].parse_dsc_comments: code = %d\n", comment_code);
448 /*
449 * We ignore any errors from Russell's parser. The only value that
450 * it will return for an error is -1 so there is very little information.
451 * We also do not want bad DSC comments to abort processing of an
452 * otherwise valid PS file.
453 */
454 if (comment_code < 0)
455 comment_code = 0;
456 }
457 /*
458 * Transfer data from DSC structure to postscript variables.
459 * Look up proper handler in the local cmd decode list.
460 */
461 while (pCmdList->code && pCmdList->code != comment_code )
462 pCmdList++;
463 if (pCmdList->dsc_proc) {
464 code = dict_param_list_write(&list, opDict, NULL, iimemory);
465 if (code < 0)
466 return code;
467 code = (pCmdList->dsc_proc)((gs_param_list *)&list, dsc_data);
468 iparam_list_release(&list);
469 if (code < 0)
470 return code;
471 }
472
473 /* Put DSC comment name onto operand stack (replace string). */
474
475 return name_enter_string(imemory, pCmdList->comment_name, opString);
476 }
477
478 /* ------ Initialization procedure ------ */
479
480 const op_def zdscpars_op_defs[] = {
481 {"1.initialize_dsc_parser", zinitialize_dsc_parser},
482 {"2.parse_dsc_comments", zparse_dsc_comments},
483 op_def_end(0)
484 };
485