/*
* 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 <sys/types.h> /* Ultrix... Michael Fulbright msf@as.arizona.edu */
#include <time.h>
#include <sys/stat.h>
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
|