/* Copyright (C) 1994 Aladdin Enterprises. All rights reserved. */ /* dsc.c */ /* Parse DSC comments from a PostScript file. */ #include "stdpre.h" #include #include #include #ifndef SEEK_SET # define SEEK_SET 0 #endif /* Define the maximum length of a line in a DSC-conforming file. */ /* This is 255 characters + 2 for newline + 1 for null. */ #define line_size 258 /* Test whether a line begins with %%. */ #define is_dsc_comment(line) ((line)[0] == '%' && (line)[1] == '%') /* Test whether a string begins with a given prefix. */ #define has_prefix(str, pre) !strncmp(str, pre, strlen(pre)) /* Faster test if prefix is a literal string. */ #define has_lit_prefix(str, litpre) !strncmp(str, litpre, sizeof(litpre) - 1) /* Test whether a line is a specific DSC comment. */ #define has_dsc_prefix(line, litpre)\ (is_dsc_comment(line) && has_lit_prefix(line, litpre)) /* Define a bounding box structure. */ #define LLX 0 #define LLY 1 #define URX 2 #define URY 3 /* ------ Internal routines ------ */ /* Allocate a block with malloc, exiting on failure. */ private void * dsc_malloc(size_t size) { void *blk = malloc(size); if ( blk == 0 ) { fprintf(stderr, "Unable to allocate object; giving up.\n"); exit(255); } return blk; } /* Copy a given amount of data from one file to another (to != NULL) or */ /* skip a given amount of data in a file without using fseek (to == NULL). */ #define fcpy_size 5000 private void fcpy(FILE *to, FILE *from, long len) { char buf[fcpy_size]; size_t count; for ( ; len > 0; len -= count ) { count = (len > fcpy_size ? fcpy_size : len); fread(buf, sizeof(char), count, from); if ( to != NULL ) fwrite(buf, sizeof(char), count, to); } } #undef fcpy_size /* * Copy (or skip) a %%BeginBinary/%%EndBinary section. * Return the number of bytes copied or skipped. */ private long copy_binary(FILE *to, FILE *from, const char *line) { long count; if ( sscanf(line + 14, "%ld", &count) == 1 ) fcpy(to, from, count); else count = 0; return count; } /* * Copy (or skip) a %%BeginData/%%EndData section. * Return the number of bytes copied or skipped. */ private long copy_data(FILE *to, FILE *from, const char *line) { long count, amount = 0; char str[line_size]; str[0] = '\0'; if ( sscanf(line + 12, "%ld %*s %s", &count, str) >= 1 ) { if ( !strcmp(str, "Lines") ) for ( ; count > 0; --count ) { if ( !fgets(str, line_size, from) ) break; amount += strlen(str); if ( to != NULL ) fputs(str, to); } else { amount = count; fcpy(to, from, count); } } return amount; } /* * Read the next line from the input. Skip over embedded data, and also * skip sections (such as procsets, fonts, and included documents) that * are not part of the main document flow. * Return a pointer to the input line, or NULL if we reached EOF. */ private void skip_region(P4(char *, FILE *, long *, const char *)); private char * next_line(char *line, FILE *in, long *pstart, long *plen) { if ( pstart ) *pstart = ftell(in); *plen = 0; for ( ; ; ) { if ( !fgets(line, line_size, in) ) /* EOF */ { line[0] = 0; return 0; } *plen += strlen(line); if ( !has_dsc_comment(line, "Begin") ) break; else if ( has_lit_prefix(line+7, "Binary:") ) { *plen += copy_binary(NULL, in, line); skip_region(line, in, plen, "EndBinary"); } else if ( has_lit_prefix(line+7, "Data:") ) { *plen += copy_data(NULL, in, line); skip_region(line, in, plen, "EndData"); } else if ( has_lit_prefix(line+7, "Feature:") ) skip_region(line, in, plen, "EndFeature"); else if ( has_lit_prefix(line+7, "File:") ) skip_region(line, in, plen, "EndFile"); else if ( has_lit_prefix(line+7, "Font:") ) skip_region(line, in, plen, "EndFont"); else if ( has_lit_prefix(line+7, "ProcSet:") ) skip_region(line, in, plen, "EndProcSet"); else if ( has_lit_prefix(line+7, "Resource:") ) skip_region(line, in, plen, "EndResource"); } return line; } /* * Skip a region of the PostScript file up to a given %%End comment. */ private void skip_region(char *line, FILE *in, long *plen, const char *end_comment) { do { long len; if ( next_line(line, in, NULL, &len) == NULL ) break; *plen += len; } while ( !has_dsc_prefix(line, end_comment) ); } /* * Scan a bounding box argument. Return 1 iff we found one. */ private int scan_bbox(const char *line, int bbox[4]) { float fx0, fy0, fx1, fy1; if ( sscanf(line, "%d %d %d %d", &bbox[LLX], &bbox[LLY], &bbox[URX], &bbox[URY]) == 4 ) return 1; if ( sscanf(line, "%f %f %f %f", &fx0, &fy0, &fx1, &fy1) != 4 ) return 0; bbox[LLX] = (int)floor(fx0); bbox[LLY] = (int)floor(fy0); bbox[URX] = (int)ceil(fx1); bbox[URY] = (int)ceil(fy1); return 1; } /* * Scan a text argument, recognizing escapes if it is a parenthesized string. * If the string is not parenthesized then if rest = 1, take the rest of * the line as the argument; if rest = 0, only take up to the next whitespace. */ #define scan_text_arg(line, endp) scan_text(line, endp, 0) #define scan_line_arg(line, endp) scan_text(line, endp, 1) private const char * scan_text(const char *line, const char **endp, int rest) { char buf[line_size]; const char *lp = line; char *bp = buf; char *arg; while ( *lp == ' ' || *lp == '\t' ) lp++; if ( *lp == '(' ) { int level = 1; lp++; for ( ; ; ) switch ( *bp++ = *lp++ ) { case '\\': switch ( *lp++ ) { case 'n': bp[-1] = '\n'; break; case 'r': bp[-1] = '\r'; break; case 't': bp[-1] = '\t'; break; case 'b': bp[-1] = '\b'; break; case 'f': bp[-1] = '\f'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { int c = lp[-1] - '0'; int d = *lp; if ( d >= '0' && d <= '7' ) { c = (c << 3) + d - '0'; d = *++lp; if ( d >= '0' && d <= '7' ) { c = (c << 3) + d - '0'; lp++; } } bp[-1] = c; break; } case 0: /* unexpected EOF */ lp--; goto out; default: bp[-1] = lp[-1]; } break; case '(': ++level; break; case ')': if ( --level ) break; case 0: goto out; default: ; } out: --bp; } else { /* Not quoted. */ while ( !(*lp == 0 || *lp == '\n' || (!rest && (*lp == ' ' || *lp == '\t'))) ) *bp++ = *lp++; if ( bp == buf ) /* empty */ return NULL; } *bp = 0; if ( endp ) *endp = lp; arg = dsc_malloc(bp - buf + 1); strcpy(arg, buf); return arg; } /* ------ Public routines ------ */ /* * Copy a section of a DSC-conforming PostScript file. * Detect %%(Begin|End)(Binary|Data) comments and copy the intervening * data as binary if necessary. If a sentinel is specified, * stop copying when we reach a line that begins with the sentinel. * If start < 0, don't seek before copying. */ private int dsc_copy_section(P6(FILE *from, FILE *to, long start, long end, char *line, const char *sentinel)); void dsc_copy(FILE *from, FILE *to, long start, long end) { char line[line_size]; dsc_copy_section(from, to, start, end, line, NULL); } char * dsc_copy_until(FILE *from, FILE *to, long start, long end, const char *sentinel) { char line[line_size]; int found = dsc_copy_section(from, to, start, end, line, sentinel); if ( found ) { char *ret = dsc_malloc(strlen(line) + 1); strcpy(ret, line); return ret; } return NULL; } private int dsc_copy_section(FILE *from, FILE *to, long start, long end, char *line, const char *sentinel) { int sent_len = (sentinel == 0 ? 0 : strlen(sentinel)); if ( start >= 0 ) fseek(from, start, SEEK_SET); while ( ftell(from) < end ) { long count; fgets(line, line_size, from); if ( sent_len ) { if ( !strncmp(line, sentinel, sent_len) ) return 1; } fputs(line, to); if ( !has_dsc_prefix(line, "Begin") ) ; else if ( has_lit_prefix(line + 7, "Binary:") ) copy_binary(to, from, line); else if ( has_lit_prefix(line + 7, "Data:") ) copy_data(to, from, line); } return 0; }