1
/* Copyright (C) 2001-2006 Artifex Software, Inc.
4
This software is provided AS-IS with no warranty, either express or
7
This software is distributed under license and may not be copied, modified
8
or distributed except as expressly authorized under the terms of that
9
license. Refer to licensing information at http://www.artifex.com/
10
or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
11
San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
14
/* $Id: zdscpars.c 8653 2008-04-20 23:40:49Z alexcher $ */
15
/* C language interface routines to DSC parser */
18
* The DSC parser consists of three pieces. The first piece is a DSC parser
19
* which was coded by Russell Lang (dscparse.c and dscparse.h). The second
20
* piece is this module. These two are sufficient to parse DSC comments
21
* and make them available to a client written in PostScript. The third
22
* piece is a PostScript language module (gs_dscp.ps) that uses certain
23
* comments to affect the interpretation of the file.
25
* The .initialize_dsc_parser operator defined in this file creates an
26
* instance of Russell's parser, and puts it in a client-supplied dictionary
27
* under a known name (/DSC_struct).
29
* When the PostScript scanner sees a possible DSC comment (first characters
30
* in a line are %%), it calls the procedure that is the value of the user
31
* parameter ProcessDSCComments. This procedure should loads the dictionary
32
* that was passed to .initialize_dsc_parser, and then call the
33
* .parse_dsc_comments operator defined in this file.
35
* These two operators comprise the interface between PostScript and C code.
37
* There is a "feature" named usedsc that loads a PostScript file
38
* (gs_dscp.ps), which installs a simple framework for processing DSC
39
* comments and having them affect interpretation of the file (e.g., by
40
* setting page device parameters). See gs_dscp.ps for more information.
42
* .parse_dsc_comments pulls the comment string off of the stack and passes
43
* it to Russell's parser. That parser parses the comment and puts any
44
* parameter values into a DSC structure. That parser also returns a code
45
* which indicates which type of comment was found. .parse_dsc_comments
46
* looks at the return code and transfers any interesting parameters from
47
* the DSC structure into key value pairs in the dsc_dict dictionary. It
48
* also translates the comment type code into a key name (comment name).
49
* The key name is placed on the operand stack. Control then returns to
50
* PostScript code, which can pull the key name from the operand stack and
51
* use it to determine what further processing needs to be done at the PS
54
* To add support for new DSC comments:
56
* 1. Verify that Russell's parser supports the comment. If not, then add
57
* the required support.
59
* 2. Add an entry into DSCcmdlist. This table contains three values for
60
* each command that we support. The first is Russell's return code for
61
* the command. The second is the key name that we pass back on the
62
* operand stack. (Thus this table translates Russell's codes into key
63
* names for the PostScript client.) The third entry is a pointer to a
64
* local function for transferring values from Russell's DSC structure
65
* into key/value pairs in dsc_dict.
67
* 3. Create the local function described at the end of the last item.
68
* There are some support routines like dsc_put_integer() and
69
* dsc_put_string() to help implement these functions.
71
* 4. If the usedsc feature should recognize and process the new comments,
72
* add a processing routine into the dictionary DSCparserprocs in
73
* gs_dscp.ps. The keys in this dictionary are the key names described
83
#include "istack.h" /* for iparam.h */
94
* Declare the structure we use to represent an instance of the parser
95
* as a t_struct. Currently it just saves a pointer to Russell's
98
typedef struct dsc_data_s {
103
/* Structure descriptors */
104
static void dsc_finalize(void *vptr);
105
gs_private_st_simple_final(st_dsc_data_t, dsc_data_t, "dsc_data_struct", dsc_finalize);
107
/* Define the key name for storing the instance pointer in a dictionary. */
108
static const char * const dsc_dict_name = "DSC_struct";
110
/* ---------------- Initialization / finalization ---------------- */
113
* If we return CDSC_OK then Russell's parser will make it best guess when
114
* it encounters unexpected comment situations.
117
dsc_error_handler(void *caller_data, CDSC *dsc, unsigned int explanation,
118
const char *line, unsigned int line_len)
124
* This operator creates a new, initialized instance of the DSC parser.
126
/* <dict> .initialize_dsc_parser - */
128
zinitialize_dsc_parser(i_ctx_t *i_ctx_p)
132
os_ptr const op = osp;
133
dict * const pdict = op->value.pdict;
134
gs_memory_t * const mem = (gs_memory_t *)dict_memory(pdict);
135
dsc_data_t * const data =
136
gs_alloc_struct(mem, dsc_data_t, &st_dsc_data_t, "DSC parser init");
139
return_error(e_VMerror);
140
data->document_level = 0;
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);
153
* This routine will free the memory associated with Russell's parser.
156
dsc_finalize(void *vptr)
158
dsc_data_t * const st = vptr;
160
if (st->dsc_data_ptr)
161
dsc_free(st->dsc_data_ptr);
162
st->dsc_data_ptr = NULL;
166
/* ---------------- Parsing ---------------- */
168
/* ------ Utilities for returning values ------ */
170
/* Return an integer value. */
172
dsc_put_int(gs_param_list *plist, const char *keyname, int value)
174
return param_write_int(plist, keyname, &value);
177
/* Return a string value. */
179
dsc_put_string(gs_param_list *plist, const char *keyname,
184
param_string_from_transient_string(str, string);
185
return param_write_string(plist, keyname, &str);
188
/* Return a BoundingBox value. */
190
dsc_put_bounding_box(gs_param_list *plist, const char *keyname,
191
const CDSCBBOX *pbbox)
193
/* pbbox is NULL iff the bounding box values was "(atend)". */
195
gs_param_int_array va;
199
values[0] = pbbox->llx;
200
values[1] = pbbox->lly;
201
values[2] = pbbox->urx;
202
values[3] = pbbox->ury;
205
va.persistent = false;
206
return param_write_int_array(plist, keyname, &va);
209
/* ------ Return values for individual comment types ------ */
212
* These routines transfer data from the C structure into Postscript
213
* key/value pairs in a dictionary.
216
dsc_adobe_header(gs_param_list *plist, const CDSC *pData)
218
return dsc_put_int(plist, "EPSF", (int)(pData->epsf? 1: 0));
222
dsc_creator(gs_param_list *plist, const CDSC *pData)
224
return dsc_put_string(plist, "Creator", pData->dsc_creator );
228
dsc_creation_date(gs_param_list *plist, const CDSC *pData)
230
return dsc_put_string(plist, "CreationDate", pData->dsc_date );
234
dsc_title(gs_param_list *plist, const CDSC *pData)
236
return dsc_put_string(plist, "Title", pData->dsc_title );
240
dsc_for(gs_param_list *plist, const CDSC *pData)
242
return dsc_put_string(plist, "For", pData->dsc_for);
246
dsc_bounding_box(gs_param_list *plist, const CDSC *pData)
248
return dsc_put_bounding_box(plist, "BoundingBox", pData->bbox);
252
dsc_page(gs_param_list *plist, const CDSC *pData)
254
int page_num = pData->page_count;
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 );
264
dsc_pages(gs_param_list *plist, const CDSC *pData)
266
return dsc_put_int(plist, "NumPages", pData->page_pages);
270
dsc_page_bounding_box(gs_param_list *plist, const CDSC *pData)
272
return dsc_put_bounding_box(plist, "PageBoundingBox", pData->page_bbox);
276
* Translate Russell's defintions of orientation into Postscript's.
279
convert_orient(CDSC_ORIENTATION_ENUM 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;
291
dsc_page_orientation(gs_param_list *plist, const CDSC *pData)
293
int page_num = pData->page_count;
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.
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));
304
return dsc_put_int(plist, "Orientation",
305
convert_orient(pData->page_orientation));
309
dsc_orientation(gs_param_list *plist, const CDSC *pData)
311
return dsc_put_int(plist, "Orientation",
312
convert_orient(pData->page_orientation));
316
dsc_viewing_orientation(gs_param_list *plist, const CDSC *pData)
318
int page_num = pData->page_count;
322
gs_param_float_array va;
325
* As for PageOrientation, ViewingOrientation may be either in the
326
* 'defaults' section or in a page section.
328
if (page_num && pData->page[page_num - 1].viewing_orientation != NULL) {
329
key = "PageViewingOrientation";
330
pctm = pData->page[page_num - 1].viewing_orientation;
332
key = "ViewingOrientation";
333
pctm = pData->viewing_orientation;
335
values[0] = pctm->xx;
336
values[1] = pctm->xy;
337
values[2] = pctm->yx;
338
values[3] = pctm->yy;
341
va.persistent = false;
342
return param_write_float_array(plist, key, &va);
346
* This list is used to translate the commment code returned
347
* from Russell's DSC parser, define a name, and a parameter procedure.
349
typedef struct cmdlist_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
358
static 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 */
377
/* ------ Parser interface ------ */
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.
386
static const char * const BadCmdlist[] = {
391
NULL /* List terminator */
394
/* See comments at start of module for description. */
395
/* <dict> <string> .parse_dsc_comments <dict> <dsc code> */
397
zparse_dsc_comments(i_ctx_t *i_ctx_p)
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;
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;
408
dsc_data_t * dsc_state = NULL;
409
dict_param_list list;
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
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;
423
* Retrieve our state.
425
code = dict_find_string(opDict, dsc_dict_name, &pvalue);
428
dsc_state = r_ptr(pvalue, dsc_data_t);
430
* Pick up the comment string to be parsed.
432
memcpy(dsc_buffer, opString->value.bytes, ssize);
433
dsc_buffer[ssize] = 0x0d; /* Russell wants a 'line end' */
434
dsc_buffer[ssize + 1] = 0; /* Terminate string */
436
* Skip data block comments (see comments in front of BadCmdList).
438
while (*pBadList && strncmp(*pBadList, dsc_buffer, strlen(*pBadList)))
440
if (*pBadList) { /* If found in list, then skip comment */
441
comment_code = 0; /* Ignore */
442
if (dsc_buffer[2] == 'B') {
443
dsc_state->document_level++;
444
} else if (dsc_state->document_level > 0) {
445
dsc_state->document_level--;
448
else if (dsc_state->document_level > 0) {
449
comment_code = 0; /* Ignore */
452
* Parse comments - use Russell Lang's DSC parser. We need to get
453
* data area for Russell Lang's parser. Note: We have saved the
454
* location of the data area for the parser in our DSC dict.
456
comment_code = dsc_scan_data(dsc_state->dsc_data_ptr, dsc_buffer, ssize + 1);
457
if_debug1('%', "[%%].parse_dsc_comments: code = %d\n", comment_code);
459
* We ignore any errors from Russell's parser. The only value that
460
* it will return for an error is -1 so there is very little information.
461
* We also do not want bad DSC comments to abort processing of an
462
* otherwise valid PS file.
464
if (comment_code < 0)
468
* Transfer data from DSC structure to postscript variables.
469
* Look up proper handler in the local cmd decode list.
471
while (pCmdList->code && pCmdList->code != comment_code )
473
if (pCmdList->dsc_proc) {
474
code = dict_param_list_write(&list, opDict, NULL, iimemory);
477
code = (pCmdList->dsc_proc)((gs_param_list *)&list, dsc_state->dsc_data_ptr);
478
iparam_list_release(&list);
483
/* Put DSC comment name onto operand stack (replace string). */
485
return name_enter_string(imemory, pCmdList->comment_name, opString);
488
/* ------ Initialization procedure ------ */
490
const op_def zdscpars_op_defs[] = {
491
{"1.initialize_dsc_parser", zinitialize_dsc_parser},
492
{"2.parse_dsc_comments", zparse_dsc_comments},