/* * text.c */ /* * mpage: a program to reduce pages of print so that several pages * of output appear on one printed sheet. * * Copyright (c) 1994-2004 Marcel J.E. Mol, The Netherlands * Copyright (c) 1988 Mark P. Hahn, Herndon, Virginia * * Permission is granted to anyone to make or distribute verbatim * copies of this document as received, in any medium, provided * that this copyright notice is preserved, and that the * distributor grants the recipient permission for further * redistribution as permitted by this notice. * */ #include "mpage.h" #include /* Ultrix... Michael Fulbright msf@as.arizona.edu */ #include #include extern char *current_locale; /* * keeps track of the current location on the sheet. it is kept global * to while file printing process because of form feeds in particular. * form feeds change the vertical page location (line number) but not * the horizontal location (character column) */ struct pageloc { int pl_line; int pl_col; int pl_new_line; int pl_new_col; }; static struct pageloc loc; static char text[LINESIZE]; static char *file_name; static int file_pagenum; static char file_date[LINESIZE]; /* * Function Declarations */ static int do_text_sheet(); static int text_onepage(); static char *mp_get_text(); #ifdef KANJI int get_wc( FILE * ); #endif /* * do_text_doc processes an input stream fd, reducing output to fit on * a printed page as decribed by asheet, and prints this on outfd. */ void do_text_doc(fd, asheet, outfd, fname) FILE *fd; struct sheet *asheet; FILE *outfd; char * fname; { struct stat file_stat; /* * initalize the postion on the first printed page */ loc.pl_line = 1; loc.pl_col = opt_indent; file_name = fname; file_pagenum = 0; fstat(fileno(fd), &file_stat); strftime(file_date, LINESIZE, dateformat, localtime(&file_stat.st_mtime)); /* * while we have input, print a page */ do_sheets(do_text_sheet, fd, asheet, outfd ); return; } /* do_text_doc */ /* * do_text_sheet creates one printed sheet consisting of several reduced pages */ static int do_text_sheet(fd, asheet, outfd) FILE *fd; struct sheet *asheet; FILE *outfd; { int rtn_val = FILE_MORE; int peekc; if ((peekc = getc(fd)) == EOF) return FILE_EOF; ungetc(peekc, fd); if (((points->pp_origin_x == 0) && !points->skip) || opt_file) { /* * keep track of the pages printed */ ps_pagenum++; fprintf(outfd, "%%%%Page: %d %d\n", ps_pagenum, ps_pagenum); # ifdef DEBUG if (Debug_flag & DB_PSMPAGE) fprintf(outfd, "(Page: %d\\n) print flush\n", ps_pagenum); # endif /* DEBUG */ fprintf(outfd, "save\n"); /* for better memory usage */ /* * Now is the time to print a sheet header... * for now, this has to be done before outline... */ sheetheader(outfd, file_name); /* * print the page outline, which draws lines and such */ mp_outline(outfd, asheet); /* * run through the list of base points for putting reduced pages * on the printed page */ points = asheet->sh_pagepoints; } /* while (points->pp_origin_x != 0 && rtn_val == FILE_MORE) {} */ while ((points->pp_origin_x != 0 || points->skip) && rtn_val == FILE_MORE) { /* * print one reduced page by moving to the proper point, * turning to the proper aspect, scaling to the proper * size, and setting up a clip path to prevent overwriting; * then` print a reduced page of output. */ int pheight; file_pagenum++; if (points->skip) { rtn_val = text_onepage(fd, asheet, outfd); points++; continue; } fprintf(outfd, "gsave\n"); # ifdef DEBUG if (Debug_flag & DB_PSMPAGE) { fprintf(outfd, "( %d %d translate %d rotate\\n)", points->pp_origin_x(), points->pp_origin_y(), asheet->sh_rotate); fprintf(outfd, " print flush\n"); } # endif /* DEBUG */ /* * Take position on paper */ fprintf(outfd, "%d %d translate\n", points->pp_origin_x(), points->pp_origin_y()); if (asheet->sh_rotate) fprintf(outfd, "%d rotate\n", asheet->sh_rotate); /* * Clip to logical page */ fprintf(outfd, "0 0 moveto 0 %d rlineto %d 0 rlineto 0 %d rlineto closepath clip\n", (*asheet->sh_height)(), (*asheet->sh_width)(), -(*asheet->sh_height)()); pheight = asheet->sh_plength * fsize + (opt_mp_header ? HSIZE + 2 : 0); /* * Scale to logical page */ fprintf(outfd, "%d %d mp_a_x mul div %d %d div scale\n", (*asheet->sh_width)(), asheet->sh_cwidth, (*asheet->sh_height)(), pheight); /* * Draw header bar and print header when needed */ if (opt_mp_header) { int pos = (asheet->sh_plength) * fsize; fprintf(outfd, "newpath 0 %d moveto %d mp_a_x mul 0 rlineto stroke\n", pos, asheet->sh_cwidth); pos += 4; fprintf(outfd, "headerfont setfont\n"); if (opt_header != NULL) fprintf(outfd, "3 %d moveto (%s) show\n", pos, opt_header); else { fprintf(outfd, "3 %d moveto (%s) show\n", pos, file_date); fprintf(outfd, "%d mp_a_x mul dup (Page %d) stringwidth pop " "sub 3 sub %d moveto", asheet->sh_cwidth, file_pagenum, pos); fprintf(outfd, " (Page %d) show\n", file_pagenum); fprintf(outfd, "fnamefont setfont\n"); #if 0 /* seems stringvals cannot be dup-ed... */ fprintf(outfd, "(%s) dup stringwidth pop sub 2 div %d moveto show\n", file_name, pos); #else fprintf(outfd, "(%s) stringwidth pop sub 2 div %d moveto\n", file_name, pos); fprintf(outfd, "(%s) show\n", file_name); #endif } } /* * Take pagemargin and font descenders (fsize/4) into account * and scale again */ fprintf(outfd, "%d %d translate %d %d div %d %d div scale\n", pagemargin_left, pagemargin_bottom + fsize/4, (*asheet->sh_width)() - pagemargin_left - pagemargin_right, (*asheet->sh_width)(), asheet->sh_plength * fsize - pagemargin_top - pagemargin_bottom, asheet->sh_plength * fsize); /* * not sure, selection of correct font is difficult. * so guessing it from locale, and always use it for UTF-8 */ if (use_utf8 && current_locale) { if (!strncmp (current_locale, "ja_JP", 5)) fprintf (outfd, "unijis setfont\n"); else if (!strncmp (current_locale, "ko_KR", 5)) fprintf (outfd, "uniks setfont\n"); else if (!strncmp (current_locale, "zh_CN", 5)) fprintf (outfd, "unigb setfont\n"); else if (!strncmp (current_locale, "zh_TW", 5)) fprintf (outfd, "unicns setfont\n"); else fprintf (outfd, "textfont setfont\n"); } else fprintf(outfd, "textfont setfont\n"); /* * place one reduced page on the printed page */ rtn_val = text_onepage(fd, asheet, outfd); /* * clean up this page and move to the next */ fprintf(outfd, "grestore\n"); points++; } /* * release PS vm used, and eject the sheet */ if (points->pp_origin_x == 0 || (rtn_val == FILE_EOF && opt_file)) { fprintf(outfd, "restore\n"); if (had_ps) fprintf(outfd, "showsheet\n"); else fprintf(outfd, "showpage\n"); } /* * let the upper level know about the status of possible EOF */ return rtn_val; } /* do_text_sheet */ /* * text_onepage places on page of reduced output on the printed page * all scaling, translation, and rotation has already been done before */ static int text_onepage(file, asheet, outfd) FILE *file; struct sheet *asheet; FILE *outfd; { char *text; #ifdef KANJI int kcode; int loadfont = 0; int i; #endif /* * Start off with printing any wanted annotation */ if (opt_textbox) { fprintf(outfd, "%d setlinewidth\n", textbox.thick); fprintf(outfd, "%d mp_a_x mul %d moveto 0 %d rlineto\n", textbox.over, textbox.lift*fsize, textbox.high*fsize); fprintf(outfd, "%d mp_a_x mul 0 rlineto 0 %d rlineto closepath stroke\n", textbox.wide, -textbox.high*fsize); } /* * as we move from one page to the next, restart printing text at * the head of the page. horziontal location is not reset because * form feeds leave the column the same from page to page. */ Debug(DB_ONEPAGE, "%% reseting line to top of page\n", 0); loc.pl_line = 1; /* * keep getting lines of input, until we have filled a page */ while (loc.pl_line <= asheet->sh_plength) { #ifndef KANJI text = mp_get_text(file, &loc, asheet); #else text = mp_get_text(file, &loc, asheet, &kcode ); if (kcode) { if (kcode < 0x0100) { /* hankaku */ if (loadfont != 2) { fprintf(outfd, "han setfont\n"); loadfont = 2; } } else { /* zenkaku */ if (loadfont != 1) { fprintf(outfd, "kanj setfont\n"); loadfont = 1; } } i = loc.pl_col; if (kcode >= 0x80 && kcode < 0x100) i--; switch (i) { /* fprintf(outfd, "(%s\\n) print flush\n", text); */ case 0: putc('0', outfd); break; case 1: fprintf(outfd, "mp_a_x"); break; default: fprintf(outfd, "%d mp_a_x mul", i); break; } fprintf(outfd, " %d moveto <%04x> show\n", (asheet->sh_plength - loc.pl_line) * fsize, kcode); text[0] = 0; } else if (loadfont) { fprintf(outfd, "textfont setfont\n"); loadfont = 0; } #endif Debug(DB_ONEPAGE, "%% text = %d\n", text); if (text == 0) { return FILE_EOF; } Debug(DB_ONEPAGE, "%% text = (%s)\n", text); Debug(DB_ONEPAGE, "%% loc.pl_line = %d\n", loc.pl_line); Debug(DB_ONEPAGE, "%% loc.pl_col = %d\n", loc.pl_col); Debug(DB_ONEPAGE, "%% loc.pl_new_line = %d\n",loc.pl_new_line); Debug(DB_ONEPAGE, "%% loc.pl_new_col = %d\n", loc.pl_new_col); if (text[0] != 0 && !points->skip) { switch (loc.pl_col) { /* fprintf(outfd, "(%s\\n) print flush\n", text); */ case 0: putc('0', outfd); break; case 1: fprintf(outfd, "mp_a_x"); break; default: fprintf(outfd, "%d mp_a_x mul", loc.pl_col); break; } fprintf(outfd, " %d moveto (%s) show\n", (asheet->sh_plength - loc.pl_line) * fsize, text); } if (loc.pl_new_line == -1) { loc.pl_col = loc.pl_new_col; return FILE_MORE; } loc.pl_line = loc.pl_new_line; loc.pl_col = loc.pl_new_col; } return FILE_MORE; } /* text_onepage */ static char * #ifndef KANJI mp_get_text(infile, locp, asheet) #else mp_get_text(infile, locp, asheet, kp) int *kp; #endif FILE *infile; struct pageloc *locp; struct sheet *asheet; { int gathering; int tabcnt; int ichr; static int prevchar = 0; char *textp; #ifdef KANJI static int prevkanj = 0; #endif textp = text; locp->pl_new_line = locp->pl_line; locp->pl_new_col = locp->pl_col; #ifdef KANJI *kp = -1; #endif gathering = 1; /* * Make sure there is still enough space in text array (we * may need to put 5 characters plus NULL in it. */ while (gathering && textp - text < LINESIZE - 5) { if (use_utf8) { int c, i; size_t len; char uni[7]; #ifdef KANJI *kp = 0; #endif if (prevchar) { c = prevchar; prevchar = 0; } else c = fgetc (infile); if (c < 128) len = 1; else if ((c & 0xe0) == 0xc0) len = 2; else if ((c & 0xf0) == 0xe0) len = 3; else if ((c & 0xf8) == 0xf0) len = 4; else if ((c & 0xfc) == 0xf8) len = 5; else if ((c & 0xfe) == 0xfc) len = 6; else { fprintf (stderr, "Invalid UTF-8\n"); len = 1; } if (len > 1) { uni[0] = c; for (i = 1; i < len; i++) { c = fgetc (infile); if ((c & 0xc0) != 0x80) { fprintf (stderr, "Invalid UTF-8\n"); break; } uni[i] = c; } uni[i] = 0; /* FIXME: need to decide a width of glyph */ locp->pl_new_col += 2; for (i = 0; uni[i]; i++) *textp++ = uni[i]; continue; } else { ichr = c; } } else { #ifdef KANJI if (current_locale && !strncmp(current_locale,"ja_JP",5)) { if (prevkanj) { *kp = prevkanj; if (prevkanj >= 0x0100) locp->pl_new_col++; locp->pl_new_col++; prevkanj = 0; gathering = 0; return textp; } if (prevchar) { ichr = prevchar; prevchar = 0; } else ichr = get_wc( infile ); if (ichr >= 0x0080) { if (*kp < 0) { *kp = ichr; gathering = 0; if (ichr >= 0x0100) locp->pl_new_col++; locp->pl_new_col++; if (opt_fold && (locp->pl_new_col >= asheet->sh_cwidth)) { gathering = 1; locp->pl_new_line += 1; locp->pl_new_col = opt_indent; } return textp; } else { prevkanj = ichr; gathering = 0; goto loopend; } } else *kp = 0; } else { *kp = 0; prevkanj = 0; #endif if (prevchar) { ichr = prevchar; prevchar = 0; } else ichr = fgetc(infile); #ifdef KANJI } #endif } Debug(DB_GETLINE, "%%called fgetc ichr = %d", ichr); /* * this prevents nulls in the input from confusing the * program logic with truncated strings */ if (ichr == 0) { ichr = 1; } switch (ichr) { case EOF: Debug(DB_GETLINE, "(%d)\n", EOF); gathering = 0; if (text == textp) return 0; break; case '\n': locp->pl_new_line++; locp->pl_new_col = opt_indent; gathering = 0; break; case '\r': locp->pl_new_col = opt_indent; gathering = 0; break; case '\b': if (--locp->pl_new_col < opt_indent) { locp->pl_new_col = opt_indent; } gathering = 0; break; case '\f': locp->pl_new_line = -1; gathering = 0; /* * Ignore newline in "\f\n" sequence */ prevchar = fgetc(infile); if (prevchar == '\n') { prevchar = 0; } break; case '\t': tabcnt = opt_tabstop - ((locp->pl_new_col - opt_indent) % opt_tabstop); locp->pl_new_col += tabcnt; gathering = 0; break; /* * case ' ': * locp->pl_new_col++; * gathering = 0; * break; */ default: /* keep on gathering if it fits on the line ... */ if (opt_fold && (locp->pl_new_col >= asheet->sh_cwidth)) { prevchar = ichr; gathering = 0; locp->pl_new_line++; locp->pl_new_col = opt_indent; break; } if (ichr == ')' || ichr == '(' || ichr == '\\') { *textp++ = '\\'; *textp++ = ichr; } /* else if (ichr >= ' ' && ichr <= '~') */ else if (ichr >= first_encoding && ichr <= last_encoding) *textp++ = ichr; else { *textp++ = '\\'; *textp++ = '2'; *textp++ = '7'; *textp++ = '7'; } locp->pl_new_col++; break; } } #ifdef KANJI loopend: *kp = 0; #endif *textp = 0; /* * remove any spaces at the front of the text string by * "converting" it to a position change */ textp = text; while (*textp && *textp == ' ') { /* * this affects the starting position of this text string * (not the next) */ locp->pl_col++; textp++; } return textp; } /* mp_get_text */ #ifdef KANJI #define ESC 0x1b unsigned short sjtojis(unsigned short, unsigned short); int issjkanji(int ); #define UNKOWN 0 #define SJIS 1 #define EUC 2 int get_wc( FILE *infile ) { static int ext[3]; static unsigned int rem = 0; static int in_kanji = 0; static int coding = UNKOWN; int c; if (!rem) { while(1) { c = fgetc(infile); ext[rem++] = c; if (c == EOF) break; if (ext[0] == ESC) { if (rem >= 3) { if (ext[1] == '$' /*&& ext[2] == 'B' */) in_kanji = 1; else if (ext[1] == '(' /*&& ext[2] == 'B' */) in_kanji = 0; else goto loopout; rem = 0; continue; } } else { if (in_kanji) { if (rem >= 2) { rem = 0; return (ext[0]<<8) + ext[1]; } } else if (coding != SJIS && c >= 0xa1 && c <= 0xf4) { /* EUC */ rem = 0; coding = EUC; return ((c & 0x7f) << 8) + (fgetc(infile) & 0x7f); } else if (issjkanji(c)) { rem = 0; if (coding != EUC) coding = SJIS; return sjtojis(c, fgetc(infile)); } else goto loopout; } } } loopout: if (rem) { c = ext[0]; ext[0] = ext[1]; ext[1] = ext[2]; rem--; } return c; } /* get_wc */ int issjkanji(int c) { c &= 0377; return ((c > 0x80 && c < 0xa0) || (c > 0xdf && c < 0xfd)); } /* issjkanji */ #ifdef JIS7 iskana(int c) { c &= 0xff; return ((c >= 0xa0) && (c <= df)); } /* iskana */ #endif unsigned short sjtojis(unsigned short byte1, unsigned short byte2) { unsigned short c; byte1 -= (byte1 >= 0xa0) ? 0xc1 : 0x81; c = ((byte1 << 1) + 0x21) << 8; if (byte2 >= 0x9f) { c += 0x0100; c |= (byte2 - 0x7e) & 0xff; } else { c |= (byte2 - ((byte2<=0x7e) ? 0x1f : 0x20)) & 0xff; } return (c); } /* sjtojis */ #endif