/* * postscript.c: Convert a videotext-page to an Encapsulated PostScript-file * * $Id: postscript.c,v 1.2 1996/09/22 23:29:45 mb Exp mb $ * * Copyright (c) 1995-96 Martin Buck * Read COPYING for more information * */ #include #include #include #include #include #include #include #include #include #include #include "safe_malloc.h" #include "vtxdecode.h" #include "misc.h" #include "postscript.h" #include "psprolog.h" #define VTXDSCVERSION 1 #define CHUNKSIZE 4096 #define MARGIN 4 #define BORDER 1 #define XYRATIO 0.8125 typedef enum { SB_TEXT, SB_SYMBOL, SB_GRAPH } sb_type; typedef struct { FILE *file; int len; unsigned char str[41]; sb_type type; } stringbuf_t; typedef struct { int l, r, t, b, paperwidth, paperheight; double xinc, yinc, xofs, yofs; } bb_t; const ps_paperdata_t ps_paperdata[] = { { "A4", 595, 842 }, { "A3", 842, 1190 }, { "Letter", 612, 792 }, { "Legal", 612, 1008 }, { NULL, 0, 0 } }; static bb_t bb; static FILE *last_file; static int last_xpages, last_ypages, last_landscape; static int xpos, ypos, vtxpagecount, pspagecount, newpage; static void swapint(int *i1, int *i2) { int tmp; tmp = *i1; *i1 = *i2; *i2 = tmp; } static void flush_string_buffer(stringbuf_t *stringbuf) { int pos; unsigned char chr; if (stringbuf->len) { putc('(', stringbuf->file); for (pos = 0; pos < stringbuf->len; pos++) { chr = stringbuf->str[pos]; if (chr < ' ' || chr >= 127) { fprintf(stringbuf->file, "\\%03o", chr); } else if (chr == '(' || chr == ')' || chr == '\\') { fprintf(stringbuf->file, "\\%c", chr); } else { putc(chr, stringbuf->file); } } fprintf(stringbuf->file, ") %c ", (stringbuf->type == SB_TEXT) ? 'T' : ((stringbuf->type == SB_SYMBOL) ? 'S' : 'G')); stringbuf->len = 0; } } static void add_string_buffer(stringbuf_t *stringbuf, unsigned char chr, sb_type type) { if (type != stringbuf->type) { flush_string_buffer(stringbuf); stringbuf->type = type; } stringbuf->str[stringbuf->len++] = chr; } static void bb_init(ps_papertype_t paper) { int xchars, ychars; double xyratio; /* This calculates the size of the page & characters and the offset of the videotext-pages * in PostScript-points. This algorithm must match the one in CalcDim, psheader.ps! */ bb.l = INT_MAX; bb.r = 0; bb.t = 0; bb.b = INT_MAX; if (last_landscape) { bb.paperwidth = ps_paperdata[paper].height; bb.paperheight = ps_paperdata[paper].width; } else { bb.paperwidth = ps_paperdata[paper].width; bb.paperheight = ps_paperdata[paper].height; } xchars = MARGIN * 2 + (41 + BORDER * 2) * last_xpages; ychars = MARGIN * 2 + (26 + BORDER * 2) * last_ypages; xyratio = ((double)bb.paperwidth / (double)xchars) / ((double)bb.paperheight / (double)ychars); if (xyratio > XYRATIO) { bb.yinc = (double)bb.paperheight / (double)ychars; bb.xinc = bb.yinc * XYRATIO; } else { bb.xinc = (double)bb.paperwidth / (double)xchars; bb.yinc = bb.xinc / XYRATIO; } bb.xofs = ((double)bb.paperwidth - ((double)(xchars - (MARGIN * 2)) * bb.xinc)) / 2.0; bb.yofs = ((double)bb.paperheight - ((double)(ychars - (MARGIN * 2)) * bb.yinc)) / 2.0; } static void bb_nextpage(void) { double xorg, yorg; /* This calulates the origin of the videotext-page in PostScript-points from the number of the * page. This algorithm must match the one in SetOrigin, psheader.ps! */ xorg = (double)((41 + 2 * BORDER) * xpos + BORDER) * bb.xinc + bb.xofs; yorg = (double)bb.paperheight - ((double)((26 + 2 * BORDER) * ypos + BORDER) * bb.yinc) - bb.yofs; bb.l = MIN(bb.l, xorg); bb.r = MAX(bb.r, xorg + 41 * bb.xinc + 1); bb.t = MAX(bb.t, yorg + 1); bb.b = MIN(bb.b, yorg - 26 * bb.yinc); } void ps_write_header(FILE *file, const char *filename, int epsf, int xpages, int ypages, ps_papertype_t paper, int landscape) { time_t curr_time; unsigned char **line; char *logname, hostname[256], *fullname, *tmpptr; struct passwd *pw; last_file = file; last_xpages = xpages; last_ypages = ypages; last_landscape = landscape; xpos = ypos = vtxpagecount = pspagecount = 0; newpage = TRUE; bb_init(paper); if ((tmpptr = strrchr(filename, '/'))) { filename = tmpptr + 1; } curr_time = time(NULL); if ((pw = getpwuid(getuid()))) { logname = pw->pw_name; fullname = sstrdup(pw->pw_gecos); if ((tmpptr = strchr(fullname, ','))) { *tmpptr = '\0'; } } else { logname = "nobody"; fullname = sstrdup("(Nobody)"); } gethostname(hostname, sizeof(hostname)); fprintf(last_file, "%%!PS-Adobe-3.0%s\n" "%%%%Title: %s\n" "%%%%Creator: VideoteXt V" VTXVERSION ", Copyright (C) 1994-96 by Martin Buck\n" "%%%%CreationDate: %s" "%%%%For: %s@%s (%s)\n" "%%%%Pages: (atend)\n" "%%%%BoundingBox: (atend)\n" "%%%%Orientation: %s\n" "%%%%DocumentMedia: %s %d %d 0 () ()\n" "%%%%DocumentNeededResources: font Courier Symbol\n" "%%%%EndComments\n\n" "%%%%BeginProlog\n", (epsf ? " EPSF-3.0" : ""), filename, asctime(localtime(&curr_time)), logname, hostname, fullname, (landscape ? "Landscape" : "Portrait"), ps_paperdata[paper].name, ps_paperdata[paper].width, ps_paperdata[paper].height); for (line = ps_prolog; *line; line++) { fprintf(last_file, "%s\n", *line); } fprintf(last_file, "%%%%EndProlog\n\n" "%%%%BeginDefaults\n" "%%%%PageMedia: %s\n" "%%%%EndDefaults\n\n" "%%%%BeginSetup\n" "$VTXDict begin\n" "/XNumPages %d def\n" "/YNumPages %d def\n" "/PaperSize { Paper%s } def\n" "/PaperLandscape %s def\n" "XNumPages YNumPages PaperSize PaperLandscape CalcDim\n" "MakeVTXFonts\n" "DefineVTXFonts\n" "%%%%EndSetup\n\n", ps_paperdata[paper].name, xpages, ypages, ps_paperdata[paper].name, (last_landscape ? "true" : "false")); free(fullname); } static void write_pageheader(void) { if (newpage) { newpage = FALSE; pspagecount++; fprintf(last_file, "%%%%Page: %d %d\n", pspagecount, pspagecount); } fprintf(last_file, "%d %d SetOrigin\n" "%% VTXPAGEBEGIN " STRINGIFY(VTXDSCVERSION) " %d\n", xpos, ypos, ++vtxpagecount); } static void write_pagetrailer(void) { fputs("% VTXPAGEEND\n\n", last_file); if (++xpos >= last_xpages) { xpos = 0; if (++ypos >= last_ypages) { ypos = 0; newpage = TRUE; } } if (newpage) { fputs("showpage\n\n", last_file); } } void ps_write_page(const vtxpage_t *page, const char *station, int show_hidden, int color, int invert) { unsigned char chr; int pos; attrib_t lastattr = 7; stringbuf_t stringbuf; bb_nextpage(); write_pageheader(); fprintf(last_file, "%% Station: %s, Page: %X.%X\n" "/UseColor %s def\n" "/InvertPage %s def\n" "( ) T NL\n", station, page->info.pagenum, page->info.minute, (color ? "true" : "false"), (invert ? "true" : "false")); stringbuf.file = last_file; stringbuf.len = 0; stringbuf.type = SB_TEXT; for (pos = 0; pos < VTX_PAGESIZE; pos++) { if ((lastattr & VTX_COLMASK) != (page->attrib[pos] & VTX_COLMASK)) { flush_string_buffer(&stringbuf); fprintf(last_file, "%d FG ", page->attrib[pos] & VTX_COLMASK); } if ((lastattr & VTX_BGMASK) != (page->attrib[pos] & VTX_BGMASK)) { flush_string_buffer(&stringbuf); fprintf(last_file, "%d BG ", (page->attrib[pos] >> 3) & VTX_COLMASK); } if ((lastattr & VTX_DOUBLE2) != (page->attrib[pos] & VTX_DOUBLE2)) { flush_string_buffer(&stringbuf); fprintf(last_file, "%cH ", ((page->attrib[pos] & VTX_DOUBLE2) ? 'D' : 'S')); } chr = page->chr[pos]; if (show_hidden || !(page->attrib[pos] & VTX_HIDDEN)) { if (chr >= 128 && chr <= 191) { add_string_buffer(&stringbuf, chr - 128 + (page->attrib[pos] & VTX_GRSEP ? 64 : 0), SB_GRAPH); } else if (chr == 91 || chr == 93 || chr == 94 || chr == 126) { add_string_buffer(&stringbuf, chr, SB_SYMBOL); } else { add_string_buffer(&stringbuf, chr, SB_TEXT); } } else { add_string_buffer(&stringbuf, ' ', SB_TEXT); } lastattr = page->attrib[pos]; if (pos % 40 == 39) { if (lastattr & VTX_BGMASK) { flush_string_buffer(&stringbuf); fputs("0 BG ", last_file); } add_string_buffer(&stringbuf, ' ', SB_TEXT); flush_string_buffer(&stringbuf); lastattr = 7; fputs("NL\n", last_file); } } fputs("( ) T NL\n", last_file); write_pagetrailer(); } void ps_write_trailer(void) { if (!newpage) { fputs("showpage\n\n", last_file); } if (last_landscape) { bb.b = bb.paperheight - bb.b; bb.t = bb.paperheight - bb.t; swapint(&bb.b, &bb.t); swapint(&bb.l, &bb.b); swapint(&bb.r, &bb.t); } fprintf(last_file, "%%%%Trailer\n" "end\n" "%%%%Pages: %d\n" "%%%%BoundingBox: %d %d %d %d\n" "%%%%EOF\n", pspagecount, bb.l, bb.b, bb.r, bb.t); } void ps_copy_page(const unsigned char *pagedata) { bb_nextpage(); write_pageheader(); fputs(pagedata, last_file); write_pagetrailer(); } int ps_seek_page(FILE *file, int reqnum) { int pagenum; long int last_offs; unsigned char line[2048]; while (1) { last_offs = ftell(file); if (!fgets(line, sizeof(line), file)) { if (feof(file)) { errno = ENOENT; } return -1; } if (sscanf(line, "%% VTXPAGEBEGIN " STRINGIFY(VTXDSCVERSION) " %d ", &pagenum) == 1 && pagenum == reqnum) { return (fseek(file, last_offs, SEEK_SET) < 0 ? -1 : 0); } } } int ps_read_page(FILE *file, int reqnum, unsigned char **destptr) { int len, maxlen, currofs = 0, linecount = 0; unsigned char line[2048]; if (ps_seek_page(file, reqnum) < 0) { return -1; } *destptr = smalloc(maxlen = CHUNKSIZE); while (1) { if (!fgets(line, sizeof(line), file)) { free(*destptr); if (feof(file)) { errno = ENOENT; } return -1; } if (!strncmp(line, "% VTXPAGEEND", sizeof("% VTXPAGEEND") - 1)) { *destptr = srealloc(*destptr, currofs + 1); return TRUE; } else if (++linecount > 100) { free(*destptr); errno = EFBIG; return -1; } else { len = strlen(line); if (maxlen <= currofs + len) { *destptr = srealloc(*destptr, maxlen += CHUNKSIZE); } strcpy(*destptr + currofs, line); currofs += len; } } }