/* HTML-PSformat.c - Module for NCSA's Mosaic software * * Purpose: to parse Hypertext widget contents into appropriate PostScript * * Author: Ameet A. Raval & Frans van Hoesel & Andrew Ford * (aar@gfdl.gov & hoesel@chem.rug.nl). * send bugreports to hoesel@chem.rug.nl * * Institution: for Ameet A. Raval: * Geophysical Fluid Dynamics Laboratory, * National Oceanic and Atmospheric Administration, * U.S. Department of Commerce * P.O. Box 308 * Princeton, NJ 08542 * for Frans van Hoesel: * Xtreme graphics software * Herepoortenmolendrift 36 * 9711 DH Groningen * The Netherlands * also: * Andrew Ford * Independent Software Consultant * 30 Upper Cheltenham Place, * Montpelier, Bristol, BS6 5HR, GB * E-mail: andrew@icarus.demon.co.uk * * Date: 1 aug 1993 * Modification: 8 nov 1993 * o added support for bold/italics courier * o removed unused or no longer needed stuff * o fixed the font alignment problem * 23 nov 1993 * o added support for horizontal ruler * o on request of Ameet, added a specific * line about whome to send bugreports to * 15 jun 1994 * o add headers, footers and footnotes to convey * title, page number, url of document, date (of * printing) and urls of links. * o use A4 or US Letter size paper (currently hard coded) * * 9 may 1995 (Andrew Ford) * o general overhaul * * Copyright: This work is the product of the United States Government, * and is precluded from copyright protection. It is hereby * released into the public domain. * * WE MAKE NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR * ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED * WARRANTY. WE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE * USERS OF THIS SOFTWARE. * * pieces of code are taken from xvps by kind * permission of John Bradley. * * To-do: * * printing of tables and forms fields (and any new features that appear) * */ #include "config.h" #include #include #include #include #include #ifdef __bsdi__ #include #else #ifndef VAXC #include #endif #endif #include #include #include "HTMLP.h" /* Fix thanks to robm. */ #ifdef __alpha #include #endif #define CR '\015' #define LF '\012' #ifndef DISABLE_TRACE extern int htmlwTrace; #endif extern int SwapElements(); #define USLETTER 0 #define A4 1 #if !defined(DEFAULT_PAGE_SIZE) #define DEFAULT_PAGE_SIZE USLETTER #endif #define F_FULLCOLOR 0 #define F_GREYSCALE 1 #define F_BWDITHER 2 #define F_REDUCED 3 #define L_PAREN '(' #define R_PAREN ')' #define B_SLASH '\\' #define MAX_ASCII '\177' /* MONO returns total intensity of r,g,b components .33R+ .5G+ .17B */ #define MONO(rd,gn,bl) (((rd)*11 + (gn)*16 + (bl)*5) >> 13) /* PSconst_out outputs to the postscript buffer an array of constant * strings */ #define PSconst_out(txt) { \ int n=(sizeof txt)/(sizeof txt[0]); \ int i; \ for (i=0; i10000.0) dpi = 72.0; return dpi; } /* * PSprintf - dynamic string concatenation function. * * In successive calls, the formatted string will be appended to the global * output string Sp. * It assumes that on each call, the length of the text appended to Sp * is less than 1024. * The format string is used as in printf, so you can use additional * arguments. * * When successful, PSprintf returns the number of characters printed * in this call, otherwise it returns EOF (just as printf does) * */ static int PSprintf(char *format, ...) { int len; char *s; va_list args; if (PS_size - PS_len < 1024) { PS_size += 1024; if ((s = (char *) realloc(PS_string, PS_size)) == NULL) { #ifndef DISABLE_TRACE if (htmlwTrace) { fprintf(stderr, "PSprintf malloc failed\n"); } #endif return(EOF); } PS_string = s; } va_start(args, format); len = vsprintf(PS_string+PS_len, format, args); /* this is a hack to make it work on systems were vsprintf(s,...) * returns s, instead of the len. */ if (len != EOF && len != 0) PS_len += strlen(PS_string+PS_len); va_end(args); return(len); } /* * PShex - output hex byte * * Append the byte "val" to an internal string buffer in hexadecimal * format. If the argument "flush" is True, or if the buffer has filled * up, flush the buffer to the larger postscript output buffer (using * PSprintf). * */ static int PShex(unsigned char val, int flush) { static unsigned char hexline[80]; static char digit[] = "0123456789abcdef"; if (!flush) { hexline[PS_hexi++] = (char) digit[((unsigned) val >> (unsigned) 4) & (unsigned) 0x0f]; hexline[PS_hexi++] = (char) digit[(unsigned) val & (unsigned) 0x0f]; } /* Changed from ">78" to ">77" on advice of debra@info.win.tue.nl (Paul De Bra). */ if ((flush && PS_hexi) || (PS_hexi>77)) { hexline[PS_hexi] = '\0'; PS_hexi=0; return (PSprintf("%s\n", hexline)); } return (0); } /* * PSfont - change font * * change local font in buf to "font" * fontfamily indicates if the overall style is times, helvetica, century * schoolbook or lucida. * */ static void PSfont(HTMLWidget hw, XFontStruct *font, int fontfamily) { PS_fontstyle fn; int style, size; int fs; static PS_fontstyle fontstyle[17] = { RF, IF, BF, FF, BF, BF, BF, BF, BF, BF, IF, FF, FF, FB, FI, FB, FI }; static char fnchar[6][3] = {"RF", "BF", "IF", "FF", "FB", "FI"}; /* fontsizes as set in gui.c and in HTML.c (listing font is only * defined in HTML.c) */ static int fontsizes[4][3][17] = { /* times font sizes */ { {14, 14, 14, 14, 18, 17, 14, 12, 10, 8, 14, 12, 12, 14, 14, 12, 12}, {17, 17, 17, 17, 24, 18, 17, 14, 12, 10, 17, 14, 12, 17, 17, 14, 14}, {20, 20, 20, 20, 25, 24, 20, 18, 17, 14, 20, 18, 12, 20, 20, 18, 18} }, /* helvetica sizes */ { {14, 14, 14, 14, 18, 17, 14, 12, 10, 8, 14, 12, 12, 14, 14, 12, 12}, {17, 17, 17, 17, 24, 18, 17, 14, 12, 10, 17, 14, 12, 17, 17, 14, 14}, {20, 20, 20, 20, 25, 24, 20, 18, 17, 14, 20, 18, 12, 20, 20, 18, 18} }, /* new century schoolbook sizes */ { {14, 14, 14, 14, 18, 17, 14, 12, 10, 8, 14, 12, 12, 14, 14, 12, 12}, {18, 18, 18, 18, 24, 18, 17, 14, 12, 10, 18, 14, 12, 18, 18, 14, 14}, {20, 20, 20, 20, 25, 24, 20, 18, 17, 14, 20, 18, 12, 20, 20, 18, 18} }, /* lucida sizes */ { {14, 14, 14, 14, 18, 17, 14, 12, 11, 10, 14, 12, 12, 14, 14, 12, 12}, {17, 17, 17, 17, 24, 18, 17, 14, 12, 10, 17, 14, 12, 17, 17, 14, 14}, {20, 20, 20, 20, 25, 24, 20, 18, 17, 14, 20, 18, 12, 20, 20, 18, 18} } }; /* next is for each fontfamily the ascent value as given by the * medium sized bold x-font (the regular font has the same * ascent value for both the medium and the large size Century * font). * it is use in the check for the fontsize (small, medium, large) */ static int medium_fontascent[4] = { 14, 14, 16, 15 }; /* for each fontfamily, for each fontsize, and for each font style * give the ascent value, so the output from Postscript is correct. * If the value is given between parenthesis, then it is different * from the value as stored in the x-font. * Note that this is a fix, and need to be changed, if the browser * is fixed (in the current version 1.2 the baseline of various fonts * is not aligned very well). */ static int fontascent[4][3][17] = { /*rg, itl, bld, fix, h1, h2, h3, h4, h5, h6, add, pla, lis, fixbold, fixital, plabold, plaital, */ /* times */ { {12, 11, 12, 10, 15, 14, 12, 10, 8, 6, 11, 9, 10, 10, 10, 9, 9}, {13, 13, 14, 12, 20, 15, 14, 12, 10, 8, 13, 10, 10, 12, 12, 10, 10}, {16, 15, 15, 13, 21, 20, 15, 15, 14, 12, 15, 13, 10, 13, 13, 13, 13} }, /* helvetica */ { {12, 12, 12, 10, 15, 14, 12, 10, 9, 7, 12, 9, 10, 10, 10, 9, 9}, {14, 14, 14, 12, 22, 15, 14, 12, 10, 9, 14, 10, 10, 12, 12, 10, 10}, {16, 16, 16, 13, 22, 22, 16, 15, 14, 12, 16, 13, 10, 13, 13, 13, 13} }, /* new century schoolbook */ { {12, 12, 13, 10, 16, 14, 13, 10, 9, 7, 12, 9, 10, 10, 10, 9, 9}, {16, 14, 16, 13, 22, 16, 14, 13, 10, 9, 14, 10, 10, 13, 13, 10, 10}, {17, 16, 17, 13, 22, 22, 17, 16, 14, 13, 16, 13, 10, 13, 13, 13, 13} }, /* lucida bright */ { {11, 11, 11, 11, 15, 14, 11, 10, 9, 7, 11, 9, 10, 11, 10, 9, 9}, {14, 15, 14, 13, 20, 15, 14, 11, 10, 7, 15, 11, 10, 13, 13, 11, 10}, {17, 17, 17, 16, 21, 20, 17, 15, 14, 11, 17, 14, 10, 16, 13, 14, 13} } }; /* NULL case - reflush old font or the builtin default: */ if ((hw == NULL) || (font == NULL)) { if (PS_oldfs != 0) PSprintf( "%2s %d SF\n", fnchar[PS_oldfn], PS_oldfs); return; } /* added the next line in case xmosaic version 199.4 has more fonts */ style = 3; if (font == hw->html.font) { style = 0; } else if (font == hw->html.italic_font) { style = 1; } else if (font == hw->html.bold_font) { style = 2; } else if (font == hw->html.fixed_font) { style = 3; } else if (font == hw->html.header1_font) { style = 4; } else if (font == hw->html.header2_font) { style = 5; } else if (font == hw->html.header3_font) { style = 6; } else if (font == hw->html.header4_font) { style = 7; } else if (font == hw->html.header5_font) { style = 8; } else if (font == hw->html.header6_font) { style = 9; } else if (font == hw->html.address_font) { style = 10; } else if (font == hw->html.plain_font) { style = 11; } else if (font == hw->html.listing_font) { style = 12; } else if (font == hw->html.fixedbold_font) { style = 13; } else if (font == hw->html.fixeditalic_font) { style = 14; } else if (font == hw->html.plainbold_font) { style = 15; } else if (font == hw->html.plainitalic_font) { style = 16; } /* check size, by looking at the size of the regular font */ size = 1; if (hw->html.bold_font->ascent > medium_fontascent[fontfamily]) { /* large font */ size = 2; } else if (hw->html.bold_font->ascent < medium_fontascent[fontfamily]) { /* small font */ size = 0; } fn = fontstyle[style]; fs = fontsizes[fontfamily][size][style]; PS_fontascent = fontascent[fontfamily][size][style]; if (fn != PS_oldfn || fs != PS_oldfs) { PSprintf( "%2s %d SF\n", fnchar[fn], fs); PS_oldfn=fn, PS_oldfs=fs; } } /* * PSshowpage - end of page function * * show the current page and restore any changes to the printer state. * Any accumulated footnotes are output and the outstanding footnote count * reset to zero. Footnotes are preceded by a footnote rule and each footnote * is consists of a raised mark and the footnote text (i.e. the url). The mark * is in a smaller font than the text. The ideas are filched from LaTeX. */ static void PSshowpage(void) { PSprintf("restore\n"); if (n_saved_ftns > 0) { int i; PSprintf("gsave 0.2 setlinewidth newpath %.2f %.2f M %.2f 0 RL stroke\n", page_dimens.left_margin, (page_dimens.bot_margin + (footnote_ptsize * n_saved_ftns) + 4), (page_dimens.text_width * 0.4)); for (i = 0; n_saved_ftns; n_saved_ftns--, i++) { PSprintf("newpath %.2f %.2f M RF %.2f SF (%d) S 3 -2 R RF %d SF (%s) S\n", page_dimens.left_margin, page_dimens.bot_margin + 5 + (n_saved_ftns - 1) * footnote_ptsize, (0.7 * footnote_ptsize), cur_ftn_no - n_saved_ftns, footnote_ptsize, footnotes[i]); } PSprintf("grestore\n"); } PSprintf("showpage\n"); } /* * PSnewpage - begin a fresh page * * increment the page count and handle the structured comment * conventions * */ static void PSnewpage(void) { PS_curr_page++; /* the PostScript reference Manual states that the Page: Tag should have a label and a ordinal; otherwise programs like psutils fail -gustaf */ PSprintf("%%%%Page: %d %d\n", PS_curr_page, PS_curr_page); PSprintf("save\n"); if (HTML_Print_Headers) PSprintf("%d ", PS_curr_page); PSprintf("NP\n"); PSfont( NULL, NULL, 0); /* force re-flush of last font used */ } /* * PSinit_latin1 - handle ISO encoding * * print out initializing PostScript text for ISO Latin1 font encoding * This code is copied from the Idraw program (from Stanford's InterViews * package), courtesy of Steinar Kjaernsr|d, steinar@ifi.uio.no * */ static void PSinit_latin1(void) { static char *txt[] = { "/reencodeISO {", "dup dup findfont dup length dict begin", "{ 1 index /FID ne { def }{ pop pop } ifelse } forall", "/Encoding ISOLatin1Encoding D", "currentdict end definefont", "} D", "/ISOLatin1Encoding [", "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef", "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef", "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef", "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef", "/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright", "/parenleft/parenright/asterisk/plus/comma/minus/period/slash", "/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon", "/less/equal/greater/question/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N", "/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash/bracketright", "/asciicircum/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m", "/n/o/p/q/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/asciitilde", "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef", "/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef", "/.notdef/dotlessi/grave/acute/circumflex/tilde/macron/breve", "/dotaccent/dieresis/.notdef/ring/cedilla/.notdef/hungarumlaut", "/ogonek/caron/space/exclamdown/cent/sterling/currency/yen/brokenbar", "/section/dieresis/copyright/ordfeminine/guillemotleft/logicalnot", "/hyphen/registered/macron/degree/plusminus/twosuperior/threesuperior", "/acute/mu/paragraph/periodcentered/cedilla/onesuperior/ordmasculine", "/guillemotright/onequarter/onehalf/threequarters/questiondown", "/Agrave/Aacute/Acircumflex/Atilde/Adieresis/Aring/AE/Ccedilla", "/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute/Icircumflex", "/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis", "/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute", "/Thorn/germandbls/agrave/aacute/acircumflex/atilde/adieresis", "/aring/ae/ccedilla/egrave/eacute/ecircumflex/edieresis/igrave", "/iacute/icircumflex/idieresis/eth/ntilde/ograve/oacute/ocircumflex", "/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex/udieresis", "/yacute/thorn/ydieresis", "] D", "[RF BF IF FF FB FI] {reencodeISO D} forall" }; PSconst_out(txt); } /* * PSinit - initialize Postscript output * * does the initialization per html document * */ static void PSinit(void) { PS_size = PS_len = PS_offset = PS_hexi = PS_page_offset = 0; PS_start_y = 0; PS_string = (char *) malloc(1); PS_oldfs = 0; PS_oldfn = RF; PS_curr_page = 0 ; n_saved_ftns = 0; cur_ftn_no = 1; } /* * PSheader - initialize Postscript output * * Prints out the prolog. The following PostScript macros are defined * D def - define a macro * E exch - exhange parameters * M moveto * R rmoveto * L lineto * RL rlineto * SQ draw a unit square * U underline a string * B draw a bullet * OB draw an open bullet * HR draw a horizontal rule * SF set font * RF roman font (dependent on font family) * BF bold font (dependent on font family) * IF italic font (dependent on font family) * FF fixed font (courier) * FB fixed bold font (courier bold) * FI fixed italic font (courier oblique) * nstr buffer for creating page number string * pageno literal "Page " * url URL of document * title title of document * date date modified/printed */ static void PSheader(char *title, int font, char *url, char *time_str) { static char *notitle="Untitled"; int set_to_null=0; static char *fontname[] = { /* in order: regular, bold, italic */ "Times-Roman", "Times-Bold", "Times-Italic", "Helvetica", "Helvetica-Bold", "Helvetica-Oblique", "NewCenturySchlbk-Roman", "NewCenturySchlbk-Bold", "NewCenturySchlbk-Italic", /* this is a nasty trick, I have put Times in place of * Lucida, because most printers don't have Lucida font */ "Times-Roman", "Times-Bold", "Times-Italic" /* "Lucida", "Lucida-Bold", "Lucida-Italic" */ }; static char *txt[] = { "/M {moveto} D", "/S {show} D", "/R {rmoveto} D", "/L {lineto} D", "/RL {rlineto} D", "/SQ {newpath 0 0 M 0 1 L 1 1 L 1 0 L closepath} D", "/U {gsave currentpoint currentfont /FontInfo get /UnderlinePosition get", " 0 E currentfont /FontMatrix get dtransform E pop add newpath moveto", " dup stringwidth rlineto stroke grestore S } D", "/B {/r E D gsave -13 0 R currentpoint ", " newpath r 0 360 arc closepath fill grestore } D", "/OB {/r E D gsave -13 0 R currentpoint ", " newpath r 0 360 arc closepath stroke grestore } D", "/HR {/l E D gsave 0 1 RL l 0 RL 0 -1 RL l neg 0 RL stroke grestore } D", "/SF {E findfont E scalefont setfont } D", "/FF {/Courier } D", "/FB {/Courier-Bold } D", "/FI {/Courier-Oblique } D" }; char time_buf[40]; time_t clock = time(NULL); #if !defined(VMS) || defined (__DECC) strftime(time_buf, sizeof(time_buf), "Printed %a %b %e %T %Y", localtime(&clock)); #else sprintf(time_buf,"Printed %s",asctime(localtime(&clock))); #endif /* Always show the print date -- SWP */ time_str = time_buf; /* if (!time_str || (time_str[0] == '\0')) { time_str = time_buf; } */ PSprintf("%%!PS-Adobe-1.0\n"); PSprintf("%%%%Creator: NCSA Mosaic, Postscript by Ameet Raval, Frans van Hoesel\n"); PSprintf("%%%% and Andrew Ford\n"); if (!title) { title=notitle; set_to_null=1; } { char *tmp; for (tmp = title; *tmp; tmp++) if (*tmp == CR || *tmp == LF) *tmp = ' '; PSprintf("%%%%Title: %s\n", title); } PSprintf("%%%%CreationDate: %s\n", time_buf + 8); PSprintf("%%%%Pages: (atend)\n"); PSprintf("%%%%PageOrder: Ascend\n"); PSprintf("%%%%BoundingBox: %d %d %d %d\n", (int)page_dimens.left_margin, (int)(page_dimens.bot_margin - 12), (int)(page_dimens.left_margin + page_dimens.text_width + 0.5), (int)(page_dimens.bot_margin + page_dimens.text_height + 12.5)); PSprintf("%%%%DocumentFonts: %s %s %s Courier Courier-Bold Courier-Oblique\n", fontname[font*3], fontname[font*3+1], fontname[font*3+2]); PSprintf("%%%%EndComments\n"); PSprintf("save /D {def} def /E {exch} D\n"); PSprintf("/RF {/%s} D\n", fontname[font*3]); PSprintf("/BF {/%s} D\n", fontname[font*3+1]); PSprintf("/IF {/%s} D\n", fontname[font*3+2]); PSprintf("/nstr 6 string D /pgno (Page ) D\n"); PSprintf("/url (%s) D\n", url); PSprintf("/title (%s) D\n", title); PSprintf("/date (%s) D\n", time_str); PSconst_out(txt); /* Output the newpage definition. */ PSprintf("/NP {"); if (HTML_Print_Headers) { PSprintf("gsave 0.4 setlinewidth\n"); PSprintf(" newpath %.2f %.2f M %.2f 0 RL stroke", page_dimens.left_margin, (page_dimens.bot_margin + page_dimens.text_height), page_dimens.text_width); PSprintf(" newpath %.2f %.2f M %.2f 0 RL stroke\n", page_dimens.left_margin, page_dimens.bot_margin, page_dimens.text_width); PSprintf(" BF 12 SF %.2f %.2f M (%s) S\n", page_dimens.left_margin, (page_dimens.bot_margin + page_dimens.text_height + 6), title); PSprintf(" nstr cvs dup stringwidth pop pgno stringwidth pop add\n"); PSprintf(" %.2f E sub %.2f M pgno S S\n", (page_dimens.left_margin + page_dimens.text_width), (page_dimens.bot_margin + page_dimens.text_height + 6)); PSprintf(" BF 10 SF %.2f %.2f M (%s) S\n", page_dimens.left_margin, page_dimens.bot_margin - 12, url); PSprintf(" (%s) dup stringwidth pop %.2f E sub %.2f M S grestore\n", time_str, page_dimens.left_margin + page_dimens.text_width, page_dimens.bot_margin - 12); } PSprintf(" %.2f %.2f translate %.5f %.5f scale } D\n", page_dimens.left_margin, page_dimens.bot_margin + page_dimens.text_height, Points_Pixel, Points_Pixel); PSinit_latin1(); PSprintf("%%%%EndProlog\n"); if (set_to_null) { title=NULL; } } /* * PStrailer - write postscript trailer * */ static void PStrailer(void) { PSprintf("%%%%Trailer\n"); PSprintf("restore\n"); PSprintf("%%%%Pages: %d\n", PS_curr_page); if (footnotes) { free(footnotes); footnotes = NULL; ftn_array_size = 0; } } /* * PSmoveto - move to new x,y location * * if the Y value does not fit on the current page, begin a new page * (I think in the current implementation, this never happens) * */ static void PSmoveto(int x, int y) { if (y > PS_start_y + Pixels_Page) { PS_start_y = y; PSshowpage(); PSnewpage(); } PS_offset = 0; PSprintf( "%d %d M\n", x, -(y - PS_start_y)); } /* * PSmove_offset - set Y-offset * * do a relative vertical move, whenever the offset changes * */ static void PSmove_offset(int offset) { if (offset != PS_offset) { PSprintf("0 %d R\n", PS_offset - offset ); PS_offset = offset; } } /* * Return an indication of whether or not the current element has a footnote. * * an element has a footnote if it is text or an image and its anchorHRef is not null. * If the element is a textual element with an anchorHRef, that has been split across * lines then it should be followed by a linefeed element and a text element with the * same anchorHRef. In this case say that the element doesn't have a footnote so as * to avoid duplicate footnotes. */ static int has_footnote(struct ele_rec *el) { int rc = 0; struct ele_rec *next; char *anchorHRef; if (el == NULL) { return 0; } anchorHRef = el->anchorHRef; if (anchorHRef != NULL) { switch (el->type) { case E_TEXT: case E_IMAGE: for (next = el->next; el; el = next, next = el->next) { if (next == NULL) { rc = 1; break; } else if (next->anchorHRef == NULL) { rc = 1; break; } else if (!STREQ(next->anchorHRef, anchorHRef)) { rc = 1; break; } else if ( (next->type == E_TEXT) || (next->type == E_IMAGE)) { rc = 0; break; } } break; default: break; } } return rc; } /* * PSfootnote - output a footnote mark and store the footnote * * the footnote mark is placed at the current point, enclosed in a gsave/grestore * pair so that the position of the following output is not affected. * The reference is stored in a malloced array (which may need to be expanded), to * be output at the end of the page. */ static void PSfootnote(char *href, double height) { PSprintf("gsave 0 %.2f R RF %d SF (%d) S grestore\n", height, footnote_ptsize, cur_ftn_no++); if (n_saved_ftns == ftn_array_size) { ftn_array_size += 16; if (!footnotes) { footnotes = (char **)calloc(ftn_array_size,sizeof(char *)); } else { footnotes = (char **)realloc((void *)footnotes, (ftn_array_size * sizeof(char *))); } if (footnotes == NULL) { #ifndef DISABLE_TRACE if (htmlwTrace) { fprintf(stderr, "PSfootnote realloc failed\n"); } #endif return; } } footnotes[n_saved_ftns++] = href; } /* * PStext - output text * * show text "t", and protect special characters if needed * if Underline is non-zero, the text is underlined. * */ static void PStext(HTMLWidget hw, struct ele_rec *eptr, int fontfamily, String s) { String s2; String stmp; unsigned char ch; int underline = eptr->underline_number; int ascent; PSfont(hw, eptr->font, fontfamily); /* set font */ if (PS_fontascent == 0) ascent = eptr->font->ascent; else ascent = PS_fontascent; PSmove_offset(eptr->y_offset + ascent); /* Allocate a string long enough to hold the original string with every character stored as an octal escape (worst case scenario). */ s2 = (String) malloc(strlen(s) * 4 + 1); if (s2 == NULL) { #ifndef DISABLE_TRACE if (htmlwTrace) { fprintf(stderr, "PStext malloc failed\n"); } #endif return; } /* For each char in s, if it is a special char, insert "\" * into the new string s2, then insert the actual char */ for (stmp = s2; (ch = *s++) != '\0';) { if ((ch == L_PAREN) || (ch == R_PAREN) || (ch == B_SLASH)) { *stmp++ = B_SLASH; *stmp++ = ch; } else if (ch > (unsigned char) MAX_ASCII) { /* convert to octal */ *stmp++ = B_SLASH; *stmp++ = ((ch >> 6) & 007) + '0'; *stmp++ = ((ch >> 3) & 007) + '0'; *stmp++ = (ch & 007) + '0'; } else { *stmp++ = ch; } } *(stmp) = '\0'; PSprintf("(%s)%c\n", s2, (underline)?'U':'S'); if (HTML_Print_Footers && has_footnote(eptr)) { PSfootnote(eptr->anchorHRef, 0.7 * ascent); } free(s2); } /* * PSbullet - output a bullet * * The bullet is normally filled, except for a bullet with an indent level * of two. The size of the higher level bullets is just somewhat smaller * */ static void PSbullet(HTMLWidget hw, struct ele_rec *eptr, int fontfamily) { int width = ( eptr->font->max_bounds.lbearing + eptr->font->max_bounds.rbearing); int offset = eptr->y_offset + eptr->font->ascent; int level = eptr->indent_level; double size = eptr->line_height / 5.5; if (size < 1.1) size = 1.1; if (level > 2) size /= 1.33333; /* the next line is a hack to get a good position of the * bullet in most practical cases, otherwise the * bullet may appear just a bit too low (for large fonts) * What is does is to compare the lineheight with * the lineheight of the next element, to correct * for the possibly too large y_offset */ if ( (eptr->next != NULL) && ( (eptr->next->type == E_TEXT) || (eptr->next->type == E_IMAGE))) { offset += (eptr->line_height - eptr->next->line_height); } PSfont(hw, eptr->font, fontfamily); PSmove_offset(offset - width/4); PSprintf(" %f %s\n", size, (level == 2) ? "OB" : "B"); } /* * PShrule - draw a horizontal line with the given width * * nothing special, just draw a line, from the current position to * the right side of the paper. * */ static void PShrule(HTMLWidget hw, struct ele_rec *eptr, int fontfamily) { int length = hw->html.doc_width; PSmove_offset(eptr->y_offset); PSprintf("%d HR\n", length); } /* * PStable - draw a table * * Currently just draw a box of the dimensions of the table. * */ static void PStable(HTMLWidget hw, struct ele_rec *eptr, int fontfamily) { struct table_rec *tptr = eptr->table_data; int width = tptr->width; int height = tptr->height; #if 0 PSmove_offset(eptr->y_offset); PSprintf("gsave currentpoint %d sub translate ", height); PSprintf("%d %d scale\n", width, height); PSprintf("SQ stroke\n"); PSprintf("grestore\n"); #endif } /* * PSwidget - draw a widget (form field) * * Currently just draw a grey box of the dimensions of the field. * This is nowhere near complete but is a first step. * The widget record type field gives the type of field: * 0 textfield * 1 checkbox * 2 radiobox * 3 pushbutton * 4 password * 5 option menu */ static void PSwidget(HTMLWidget hw, struct ele_rec *eptr, int fontfamily) { struct wid_rec *wptr = eptr->widget_data; int w = wptr->width; int h = wptr->height; #if 1 /* Comment out? */ PSmove_offset(eptr->y_offset); PSprintf("gsave currentpoint %d sub translate ", h); PSprintf("%d %d scale\n", w, h); PSprintf("SQ 0.9 setgray fill\n"); PSprintf("grestore\n"); #endif } /* * PSrle_encode - perform run length encoding * * does the run-length encoding. This is done to reduce the file size and * therefore the time to send the file to the printer. You get longer * processing time instead. * * rle is encoded as such: * # 'run' of count+1 equal pixels * # count+1 non-equal pixels * count can range between 0 and 127 * * returns length of the rleline vector * */ static int PSrle_encode(unsigned char *scanline, unsigned char *rleline, int wide) { int i, j, blocklen, isrun, rlen; unsigned char block[256], pix; blocklen = isrun = rlen = 0; for (i = 0; i < wide; i++) { /* there are 5 possible states: * 0: block empty. * 1: block is a run, current pix == previous pix * 2: block is a run, current pix != previous pix * 3: block not a run, current pix == previous pix * 4: block not a run, current pix != previous pix */ pix = scanline[i]; if (!blocklen) { /* case 0: empty */ block[blocklen++] = pix; isrun = 1; } else if (isrun) { if (pix == block[blocklen-1]) { /* case 1: isrun, prev==cur */ block[blocklen++] = pix; } else { /* case 2: isrun, prev!=cur */ if (blocklen>1) { /* we have a run block to flush */ rleline[rlen++] = blocklen-1; rleline[rlen++] = block[0]; /* start new run block with pix */ block[0] = pix; blocklen = 1; } else { /* blocklen<=1, turn into non-run */ isrun = 0; block[blocklen++] = pix; } } } else { /* not a run */ if (pix == block[blocklen-1]) { /* case 3: non-run, prev==cur */ if (blocklen>1) { /* have a non-run block to flush */ rleline[rlen++] = (blocklen-1) | 0x80; for (j=0; jI function", " /rgbdata exch store % call input 'rgbdata'", " rgbdata length 3 idiv", " /npixls exch store", " /rgbindx 0 store", " /grays npixls string store % str to hold the result", " 0 1 npixls 1 sub {", " grays exch", " rgbdata rgbindx get 20 mul % Red", " rgbdata rgbindx 1 add get 32 mul % Green", " rgbdata rgbindx 2 add get 12 mul % Blue", " add add 64 idiv % I = .5G + .31R + .18B", " put", " /rgbindx rgbindx 3 add store", " } for", " grays", " } bind def\n", /* Utility procedure for colorimage operator. * This procedure takes two procedures off the * stack and merges them into a single procedure */ " /mergeprocs { % def", " dup length", " 3 -1 roll dup length dup 5 1 roll", " 3 -1 roll add array cvx dup", " 3 -1 roll 0 exch putinterval", " dup 4 2 roll putinterval", " } bind def\n", " /colorimage { % def", /* remove 'false 3' operands */ " pop pop", " {colortogray} mergeprocs", " image", " } bind def", /* end of 'false' case */ " } ifelse" }; PSconst_out(txt); } /* * PScolormap - write colormap * * spits out code for the colormap of the following image * if !color, it spits out a mono-ized graymap * */ static void PScolormap(int color, int nc, int *rmap, int *gmap, int *bmap) { int i; /* define the colormap */ PSprintf("/cmap %d string def\n\n\n", nc * ((color) ? 3 : 1)); /* load up the colormap */ PSprintf("currentfile cmap readhexstring\n"); for (i=0; i>8, gmap[i]>>8, bmap[i]>>8); else PSprintf("%02x ", MONO(rmap[i], gmap[i], bmap[i])); if ((i%10) == 9) PSprintf("\n"); } PSprintf("\n"); PSprintf("pop pop\n"); /* lose return values from readhexstring */ } /* * PSrle_cmapimage - define rlecmapimage operator * */ static void PSrle_cmapimage(int color) { static char *txt[] = { /* rlecmapimage expects to have 'w h bits matrix' on stack */ "/rlecmapimage {", " /buffer 1 string def", " /rgbval 3 string def", " /block 384 string def", " { currentfile buffer readhexstring pop", " /bcount exch 0 get store", " bcount 128 ge", " { ", " 0 1 bcount 128 sub", " { currentfile buffer readhexstring pop pop" }; static char *txt_color[] = { " /rgbval cmap buffer 0 get 3 mul 3 getinterval store", " block exch 3 mul rgbval putinterval", " } for", " block 0 bcount 127 sub 3 mul getinterval", " }", " { ", " currentfile buffer readhexstring pop pop", " /rgbval cmap buffer 0 get 3 mul 3 getinterval store", " 0 1 bcount { block exch 3 mul rgbval putinterval } for", " block 0 bcount 1 add 3 mul getinterval", " } ifelse", " }", " false 3 colorimage", "} bind def" }; static char *txt_gray[] = { " /rgbval cmap buffer 0 get 1 getinterval store", " block exch rgbval putinterval", " } for", " block 0 bcount 127 sub getinterval", " }", " { ", " currentfile buffer readhexstring pop pop", " /rgbval cmap buffer 0 get 1 getinterval store", " 0 1 bcount { block exch rgbval putinterval } for", " block 0 bcount 1 add getinterval", " } ifelse", " }", " image", "} bind def" }; PSconst_out(txt); if (color) { PSconst_out(txt_color); } else { PSconst_out(txt_gray); } } /* * PSwrite_bw - write B&W image * * Write the given image array 'pic' (B/W stippled, 1 byte per pixel, * 0=blk,1=wht) out as hexadecimal, max of 72 hex chars per line. If * 'flipbw', then 0=white, 1=black. Returns '0' if everythings fine, * 'EOF' if writing failed. * */ static int PSwrite_bw(unsigned char *pic, int w, int h, int flipbw) { int i, j; int err=0; unsigned char outbyte, bitnum, bit; outbyte = bitnum = 0; for (i=0; ipic_data; unsigned char *imgp = img->image_data; int anchor = (eptr->anchorHRef != NULL); int ncolors = img->num_colors; int i, j; int w = img->width; int h = img->height; int slen, colorps, colortype, bits; int err=0; int extra = 0; /* Isgray returns true if the nth color index is a gray value */ # define Isgray(i,n) (i->reds[n]==i->greens[n] && i->reds[n]==i->blues[n]) /* Is_bg returns true if the nth color index is the screen background */ # define Is_bg(i,n) (i->reds[n]==bg_color.red && \ i->greens[n]==bg_color.green && i->blues[n]==bg_color.blue) /* Is_fg returns true if the nth color index is the screen foreground */ # define Is_fg(i,n) (i->reds[n]==fg_color.red && \ i->greens[n]==fg_color.green && i->blues[n]==fg_color.blue) PSmove_offset(eptr->y_offset); if (anchor) { /* draw an outline by drawing a slightly larger black square * below the actual image */ PSprintf("gsave currentpoint %d sub translate ", h); PSprintf("0 -2 translate %d %d scale\n", w+4, h+4); PSprintf("SQ fill\n"); PSprintf("grestore\n"); extra = 4; } if (imgp == NULL) { /* image was not available... do something instead * draw an empty square for example */ PSprintf("gsave currentpoint %d sub translate", h); if (anchor) PSprintf(" 2 0 translate"); else PSprintf(" 0 2 translate"); PSprintf(" %d %d scale\n", w, h); PSprintf("0.9 setgray SQ fill\n"); PSprintf("grestore\n"); /* move currentpoint just right of image */ PSprintf("%d 0 R\n", w+extra); return; } /* this is a hack to see if the image is Black & White, * Greyscale or 8 bit color * assume it's bw if it has only one or two colors, both some grey's * assume it's greyscale if all the colors (>2) are grey * Images with only one color do occur too. */ if ( ( (ncolors == 2) && ( (Isgray(img,0) && Isgray(img,1)) || (Is_bg(img,0) && Is_fg(img,1)) || (Is_fg(img,0) && Is_bg(img,1)) )) || ( (ncolors == 1) && (Isgray(img,0) || Is_bg(img,0) || Is_fg(img,0)))) { colortype = F_BWDITHER; slen = (w+7)/8; bits = 1; colorps = 0; } else { colortype = F_GREYSCALE; slen = w; bits = 8; colorps = 0; for (i=0; ireds[0], img->greens[0],img->blues[0]) > MONO(img->reds[1], img->greens[1], img->blues[1])) || (ncolors == 1 && MONO(img->reds[0], img->greens[0],img->blues[0]) > MONO(127, 127, 127) )) { flipbw=1; } /* dimensions of data */ PSprintf("%d %d %d\n", w, h, bits); /* mapping matrix */ PSprintf("[%d 0 0 %d 0 %d]\n\n", w, -h, h); PSprintf("{currentfile pix readhexstring pop}\n"); PSprintf("image\n"); /* write the actual image data */ err = PSwrite_bw(imgp, w, h, flipbw); } else { /* all other formats */ unsigned char *rleline = (unsigned char *) NULL; int rlen; /* if we're using color, make sure 'colorimage' is defined */ if (colorps) PScolor_image(); PScolormap(colorps, ncolors, img->reds, img->greens, img->blues); PSrle_cmapimage(colorps); /* dimensions of data */ PSprintf("%d %d %d\n", w, h, bits); /* mapping matrix */ PSprintf("[%d 0 0 %d 0 %d]\n", w, -h, h); PSprintf("rlecmapimage\n"); rleline = (unsigned char *) malloc(w * 2); if (!rleline) { #ifndef DISABLE_TRACE if (htmlwTrace) { fprintf(stderr,"failed to malloc space for rleline\n"); } #endif return; } for (i=0; ianchorHRef, 2.0); } /* forget about the macro's */ # undef Isgray # undef Is_fg # undef Is_bg } /* * ParseTextToPSString - entry point for postscript output * * Parse all the formatted text elements from start to end * into an ascii text string, and return it. * Very like ParseTextToString() except the text is prettied up * into Postscript to show headers and the like. * space_width and lmargin tell us how many spaces * to indent lines. * Because this routine is only used to print whole documents, * some parameters are not needed at all! * Also it assumes that you are indeed printing the whole document, and * not just a selected portion of it. It therefore can assume that * only for the first page the initialization is needed, and only * the last page has the trailers. You cannot use ParseTextToPSString() * as you can use ParseTextToString() because of this initialization code. * * the fontfamily parameter is new * The font is encoded as: * 0: times (default for now) * 1: helvetica * 2: new century schoolbook * 3: lucida */ String ParseTextToPSString(HTMLWidget hw, struct ele_rec *elist, struct ele_rec *startp, struct ele_rec *endp, int start_pos, int end_pos, int space_width, int lmargin, int fontfamily, char *url, char *time_str) { int xpos, ypos, epos; int height; double pagewidth; int line = -1; struct ele_rec *eptr; struct ele_rec *start; struct ele_rec *end; struct ele_rec *last; struct ele_rec *tmpptr; unsigned long fg_pixel, bg_pixel; int footnotes_this_page = 0; int footnotes_this_line; int reserved_space; if (startp == NULL) return(NULL); /* * Get the foreground and background colors so we can check later * for black&white documents */ XtVaGetValues (hw->html.view, #ifdef MOTIF XtNforeground, &fg_pixel, #endif XtNbackground, &bg_pixel, NULL); #ifndef MOTIF XtVaGetValues ((Widget)hw, XtNforeground, &fg_pixel, NULL); #endif fg_color.pixel = fg_pixel; bg_color.pixel = bg_pixel; XQueryColor(XtDisplay(hw->html.view), (installed_colormap ? installed_cmap : DefaultColormap(XtDisplay(hw->html.view), DefaultScreen(XtDisplay(hw->html.view)))), &fg_color); XQueryColor(XtDisplay(hw->html.view), (installed_colormap ? installed_cmap : DefaultColormap(XtDisplay(hw->html.view), DefaultScreen(XtDisplay(hw->html.view)))), &bg_color); /* this piece of code is needed if the user selects a portion * of the document with the mouse. * I think it will never be used, but I left it in anyway. F. */ if (SwapElements(startp, endp, start_pos, end_pos)) { start = endp; end = startp; epos = start_pos; start_pos = end_pos; end_pos = epos; } else { start = startp; end = endp; } /* Setup page size according to user preference. */ if (HTML_Print_Paper_Size_A4) page_dimens = a4_page_dimens; else page_dimens = us_letter_page_dimens; page_dimens.text_height = ( page_dimens.page_height - page_dimens.top_margin - page_dimens.bot_margin); page_dimens.text_width = ( page_dimens.page_width - page_dimens.left_margin - page_dimens.right_margin); /* Calculate the number of Postscript points per pixel of current * screen, and the height of the page in pixels (used in figuring * when we've hit the bottom of the page). */ Points_Pixel = 72.0 / GetDpi(hw); #ifdef OLD pagewidth = hw->html.doc_width; #else /* gustaf fix */ pagewidth = hw->html.view_width; #endif /* gustaf fix */ /* reduce the scaling if the width used for formatting is greater * than 8 * 72 pixels (8 inch) * In theory, this is not what you want for A4 paper (only 8.27 inch * wide), but I guess that the hw->html.doc_width includes some * left and right margins, so it seems to work in practice. */ if (pagewidth > page_dimens.text_width) Points_Pixel = Points_Pixel * page_dimens.text_width / pagewidth; Pixels_Page = (int) (page_dimens.text_height / Points_Pixel); PSinit(); PSheader(hw->html.title, fontfamily, url, time_str); PSnewpage(); last = start; eptr = start; while ((eptr != NULL) && (eptr != end)) { /* Skip the special internal text added for multi-page * documents. */ if (eptr->internal == True) { if (eptr->type == E_LINEFEED) { PS_page_offset += eptr->line_height; } eptr = eptr->next; continue; } /* check if this is a newline */ if (line != eptr->line_number) { /* calculate max height */ height = 0; footnotes_this_line = 0; line = eptr->line_number; tmpptr = eptr; while (tmpptr != NULL && tmpptr->line_number == line) { if (tmpptr->line_height > height) height = tmpptr->line_height; tmpptr = tmpptr->next; if (HTML_Print_Footers && has_footnote(tmpptr)) footnotes_this_line++; } ypos = eptr->y - PS_page_offset ; xpos = eptr->x - lmargin; if (xpos < 0) xpos = 0; /* check if line fits completly on page */ reserved_space = 0; if (footnotes_this_page || footnotes_this_line) { reserved_space = ( ( footnote_space + ( footnote_ptsize * ( footnotes_this_page + footnotes_this_line))) / Points_Pixel); } if (ypos + height + reserved_space > PS_start_y + Pixels_Page) { PS_start_y = ypos; PSshowpage(); PSnewpage(); footnotes_this_page = 0; } footnotes_this_page += footnotes_this_line; PSmoveto( xpos, ypos); } switch (eptr->type) { case E_TEXT: PStext(hw, eptr, fontfamily, (String)((eptr == start) ? (eptr->edata + start_pos) : eptr->edata)); break; case E_BULLET: PSbullet(hw, eptr, fontfamily); break; case E_IMAGE: PSimage(hw, eptr, fontfamily); break; case E_LINEFEED: break; case E_HRULE: PShrule(hw, eptr, fontfamily); break; case E_TABLE: PStable(hw, eptr, fontfamily); break; case E_WIDGET: PSwidget(hw, eptr, fontfamily); break; } last = eptr; eptr = eptr->next; } PSshowpage(); PStrailer(); return (PS_string); }