/* 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 
 *              (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
 *
 * 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
 *
 * 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.
 */

#include <varargs.h>

#include <stdio.h>
#include <ctype.h>
#include <math.h>

#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include "HTMLP.h"

#define CR '\015'
#define LF '\012'

extern int SwapElements ();

/* the next page sizes are a compromise between letter sized paper
 * (215.9 x 279.4 mm) and european standard A4 sized paper (210.0 x 297.0 mm).
 * Note that PAGE_WIDTH is not the actual width of the paper
 */
#define TOP_MARGIN	(10*72)
#define BOT_MARGIN	(0.7*72)
#define LEFT_MARGIN	(0.6*72)
#define PAGE_HEIGHT	(TOP_MARGIN - BOT_MARGIN)
#define PAGE_WIDTH	(8*72)

#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'

#ifdef _NO_PROTO
#define ARG0(v0)			()
#define ARG1(t1,v1)			(v1) t1 v1;
#define ARG2(t1,v1,t2,v2)		(v1,v2) t1 v1;t2 v2;
#define ARG3(t1,v1,t2,v2,t3,v3)	(v1,v2,v3) t1 v1;t2 v2;t3 v3;
#define ARG4(t1,v1,t2,v2,t3,v3,t4,v4)	(v1,v2,v3,v4) t1 v1;t2 v2;t3 v3;t4 v4;
#define ARG5(t1,v1,t2,v2,t3,v3,t4,v4,t5,v5)	(v1,v2,v3,v4,v5) t1 v1;t2 v2;t3 v3;t4 v4; t5 v5;
#define ARG1V(t1,v1,e2)		(v1) t1 v1;
#else
#define ARG0(v0)			(v0)
#define ARG1(t1,v1)			(t1 v1)
#define ARG2(t1,v1,t2,v2)		(t1 v1, t2 v2)
#define ARG3(t1,v1,t2,v2,t3,v3)	(t1 v1, t2 v2, t3 v3)
#define ARG4(t1,v1,t2,v2,t3,v3,t4,v4)	(t1 v1, t2 v2, t3 v3, t4 v4)
#define ARG5(t1,v1,t2,v2,t3,v3,t4,v4,t5,v5)	(t1 v1, t2 v2, t3 v3, t4 v4, t5 v5)
#define ARG1V(t1,v1,e2)		(t1 v1, e2)
#endif /* _NO_PROTO */

/* 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; i<n; i++) { \
			PSprintf("%s\n", txt[i]) ; \
		} \
}


/* for regular-font, bold-font, italic-font, fixed-font */
typedef enum
{
  RF, BF, IF, FF, FB, FI
}
PS_fontstyle;

static int PS_size, PS_len, PS_offset, PS_curr_page, PS_start_y, PS_hexi;
static int PS_page_offset;
static char *PS_string;
static float Points_Pixel;
static int Pixels_Page;
static PS_fontstyle PS_oldfn = RF;
static int PS_fontascent = 0;
static int PS_oldfs = 0;

static XColor fg_color, bg_color;


/*__________________________________________________________________________
 | 
 | GetDpi - return Dots-per-inch of the screen
 |
 | calculate the pixel density in dots per inch on the current widget
 | screen
 |
*/

static float GetDpi 
ARG1 (HTMLWidget, hw)
{
  Screen *s = XtScreen (hw);
  float dpi;

  dpi = 25.4 * WidthOfScreen (s) / WidthMMOfScreen (s);
  /* no earthly monitor does this */
  if (dpi < 1.0 || dpi > 10000.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)
 |
*/

#ifdef BROKEN_SOLARIS_COMPILER_STDARG
/* "Looks like there's a bug in Sun's C compiler and the stdarg.h use
   of va_start() in HTML-PSformat.c. Until the SunPro folks can take a
   look at the problem, the following pre-ANSI code should workaround
   the problem." */
static int PSprintf 
ARG1V (char *, format,...)
{
  va_dcl
    va_list args;
  int len;
  char *s;

  if (PS_size - PS_len < 1024)
  {
    PS_size += 1024;
    if ((s = (char *) XtRealloc (PS_string, PS_size)) == NULL)
    {
      fprintf (stderr, "PSprintf malloc failed\n");
      return (EOF);
    }
    PS_string = s;
  }
  va_start (args);
  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 != NULL)
    PS_len += strlen (PS_string + PS_len);
  va_end (args);
  return (len);
}
#else /* not BROKEN_SOLARIS_COMPILER_STDARG */
static int
PSprintf (format, va_alist)
char *format;
va_dcl
{
  int len;
  char *s;
  va_list args;

  if (PS_size - PS_len < 1024)
  {
    PS_size += 1024;
    if ((s = (char *) XtRealloc (PS_string, PS_size)) == NULL)
    {
      fprintf (stderr, "PSprintf malloc failed\n");
      return (EOF);
    }
    PS_string = s;
  }
  va_start (args);
  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);
}
#endif /* not BROKEN_SOLARIS_COMPILER_STDARG */

/*__________________________________________________________________________
 |
 | 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 
ARG2 (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 
ARG3 (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
 | 
*/

static void PSshowpage 
ARG0 (void)
{

  PSprintf ("showpage restore\n");
}


/*__________________________________________________________________________
 |
 | PSnewpage - begin a fresh page
 |
 | increment the page count and handle the structured comment
 | conventions
 |
*/

static void PSnewpage 
ARG0 (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\nNP\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 
ARG0 (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 
ARG0 (void)
{
  PS_size = PS_len = PS_offset = PS_hexi = PS_page_offset = 0;
  PS_start_y = 0;
  PS_string = (char *) XtMalloc (1);
  PS_oldfs = 0;
  PS_oldfn = RF;
  PS_curr_page = 0;
}


/*__________________________________________________________________________
 |
 | PSheader - initialize Postscript output
 |
 | prints out the prolog 
 |
*/

static void PSheader 
ARG2 (char *, title, int, font)
{

  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, becaus emost printers don't have Lucida font
   */
    "Times-Roman", "Times-Bold", "Times-Italic"
  /* "Lucida", "Lucida-Bold", "Lucida-Italic" */
  };

  static char *txt[] =
  {

    "%%Creator: NCSA HTML widget, Postscript by Ameet Raval & Frans van Hoesel",
    "%%Pages: (atend)",
    "%%EndComments",
    "save",
    "/D {def} def /E {exch} D",
    "/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",
    "/NP {xmargin topmargin translate scalfac dup scale } D",
    "/HR {/l E D gsave l 0 RL  stroke grestore } D",
    "/SF {E findfont E scalefont setfont } D",
    "/FF {/Courier } D",
    "/FB {/Courier-Bold } D",
    "/FI {/Courier-Oblique } D"
  };


  PSprintf ("%%!PS-Adobe-1.0\n");
  if (title)
  {
    char *tmp;
    for (tmp = title; *tmp; tmp++)
      if (*tmp == CR || *tmp == LF)
	*tmp = ' ';
    PSprintf ("%%%%Title: %s\n", title);
  }
  PSprintf ("%%%%DocumentFonts: %s %s %s Courier Courier-Bold Courier-Oblique\n",
	fontname[font * 3], fontname[font * 3 + 1], fontname[font * 3 + 2]);
  PSconst_out (txt);
  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]);

  PSinit_latin1 ();

  PSprintf ("/xmargin %d D\n", (int) LEFT_MARGIN);
  PSprintf ("/topmargin %d D\n", (int) TOP_MARGIN);
  PSprintf ("/scalfac %.5f D\n", Points_Pixel);
  PSprintf ("%%%%EndProlog\n");

}


/*__________________________________________________________________________
 |
 | PStrailer - write postscript trailer
 |
*/

static void PStrailer 
ARG0 (void)
{

  PSprintf ("%%%%Trailer\n");
  PSprintf ("restore\n");
  PSprintf ("%%%%Pages: %d\n", PS_curr_page);
}


/*__________________________________________________________________________
 |
 | PStext - output text
 |
 | show text "t", and protect special characters if needed
 | if Underline is non-zero, the text is underlined.
 |
*/

static void PStext 
ARG2 (String, t, int, underline)
{
  String tp, t2;
  int nspecial = 0, nisochar = 0;

  tp = t;
  /* count # of special char's in text */
  while (*tp != '\0')
  {
    if (*tp == L_PAREN || *tp == R_PAREN || *tp == B_SLASH)
      nspecial++;
    else if (*(unsigned char *) tp > (unsigned char) MAX_ASCII)
      nisochar++;
    tp++;
  }

  if (nspecial == 0 && nisochar == 0)
  {
    /*  no special char's, send out original string */
    PSprintf ("(%s)%c\n", t, (underline) ? 'U' : 'S');
    return;
  }
  /*  create t2 to hold original text + "\"'s */
  t2 = (String) XtMalloc ((tp - t) + nspecial + 3 * nisochar + 1);

  if (t2 == NULL)
  {
    fprintf (stderr, "PStext malloc failed\n");
    return;
  }

  /*  for each char in t, if it is a special char, insert "\"
   *  into the new string t2, then insert the actual char
   */
  tp = t2;
  while (*t != '\0')
  {
    if (*t == L_PAREN || *t == R_PAREN || *t == B_SLASH)
    {
      *(tp++) = B_SLASH;
      *(tp++) = *t;
    }
    else if (*(unsigned char *) t > (unsigned char) MAX_ASCII)
    {
      /*  convert to octal */
      *(tp++) = B_SLASH;
      *(tp++) = ((int) (*(unsigned char *) t) >> 6 & 007) + '0';
      *(tp++) = ((int) (*(unsigned char *) t) >> 3 & 007) + '0';
      *(tp++) = ((int) (*(unsigned char *) t) & 007) + '0';
    }
    else
    {
      *(tp++) = *t;
    }
    t++;
  }
  *(tp) = '\0';
  PSprintf ("(%s)%c\n", t2, (underline) ? 'U' : 'S');

  XtFree(t2);
}


/*__________________________________________________________________________
 |
 | 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 
ARG2 (int, level, int, size)
{

  if (size < 6)
    size = 6;

  if (level < 2)
    PSprintf (" %f B\n", size / 5.5);
  else if (level == 2)
    PSprintf (" %f OB\n", size / 5.5);
  else
    PSprintf (" %f B\n", size / 7.5);
}

/*__________________________________________________________________________
 |
 | 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 
ARG1 (int, length)
{

  PSprintf ("%d HR\n", length);
}


/*__________________________________________________________________________
 |
 | 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 
ARG2 (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 
ARG1 (int, offset)
{

  if (offset != PS_offset)
  {
    PSprintf ("0 %d R\n", PS_offset - offset);
    PS_offset = offset;
  }
}


/*__________________________________________________________________________
 |
 | 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:
 |  <count> <value>			# 'run' of count+1 equal pixels
 |  <count | 0x80> <count+1 data bytes>	# count+1 non-equal pixels
 | count can range between 0 and 127
 |
 | returns length of the rleline vector
 |
*/

static int PSrle_encode 
ARG3 (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
	{
	  isrun = 0;
	  /*  blocklen<=1, turn into non-run */
	  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; j < blocklen; j++)
	    rleline[rlen++] = block[j];
	  /*  start new run block with pix */
	  block[0] = pix;
	  blocklen = isrun = 1;
	}
	else
	{
	  isrun = 1;
	  /*  blocklen<=1 turn into a run */
	  block[blocklen++] = pix;
	}
      }
      else
      {
	/* case 4:  non-run, prev!=cur */
	block[blocklen++] = pix;
      }
    }

    if (blocklen == 128)
    {				/* max block length.  flush */
      if (isrun)
      {
	rleline[rlen++] = blocklen - 1;
	rleline[rlen++] = block[0];
      }
      else
      {
	rleline[rlen++] = (blocklen - 1) | 0x80;
	for (j = 0; j < blocklen; j++)
	  rleline[rlen++] = block[j];
      }
      blocklen = 0;
    }
  }

  if (blocklen)
  {				/* flush last block */
    if (isrun)
    {
      rleline[rlen++] = blocklen - 1;
      rleline[rlen++] = block[0];
    }
    else
    {
      rleline[rlen++] = (blocklen - 1) | 0x80;
      for (j = 0; j < blocklen; j++)
	rleline[rlen++] = block[j];
    }
  }

  return rlen;
}


/*__________________________________________________________________________
 | 
 | PScolor_image - created postscript colorimage operator 
 |
 | spits out code that checks if the PostScript device in question
 | knows about the 'colorimage' operator.  If it doesn't, it defines
 | 'colorimage' in terms of image (ie, generates a greyscale image from
 | RGB data)
 |
*/

static void PScolor_image 
ARG0 (void)
{

  static char *txt[] =
  {

    "% define 'colorimage' if it isn't defined",
    "%   ('colortogray' and 'mergeprocs' come from xwd2ps",
    "%	 via xgrab)",
    "/colorimage where   % do we know about 'colorimage'?",
    "  { pop }		   % yes: pop off the 'dict' returned",
    "  {				 % no:  define one",
    "	/colortogray {  % define an RGB->I 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 
ARG5 (int, color,
      int, nc,
      unsigned short *, rmap,
      unsigned short *, gmap,
      unsigned short *, 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 < nc; i++)
  {
    if (color)
      PSprintf ("%02x%02x%02x ", rmap[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 
ARG1 (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 
ARG4 (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; i < h && err != EOF; i++)
  {
    for (j = 0; j < w && err != EOF; j++)
    {
      bit = *(pic++);
      outbyte = (outbyte << 1) | ((bit) & 0x01);
      bitnum++;

      if (bitnum == 8)
      {
	if (flipbw)
	  outbyte = ~outbyte & 0xff;
	err = PShex (outbyte, False);
	outbyte = bitnum = 0;
      }
    }
    if (bitnum)
    {				/*  few bits left over in this row */
      outbyte <<= 8 - bitnum;
      if (flipbw)
	outbyte = ~outbyte & 0xff;
      err = PShex (outbyte, False);
      outbyte = bitnum = 0;
    }
  }
  err = PShex ('\0', True);	/*  Flush the hex buffer if needed */

  return err;
}


/*__________________________________________________________________________
 |
 | PSimage - generate image Postscript code
 |
 | Draw the image, unless there was no image, in which case an empty grey
 | rectangle is shown.
 | If anchor is set, a black border is shown around the image.
 | Positioning is not exactly that of Xmosaic's screen, but close enough.
 | 
*/

static void PSimage 
ARG2 (ImageInfo *, img, int, anchor)
{

  int ncolors = img->num_colors;
  int i, j;
  int w = img->width;
  int h = img->height;
  unsigned char *imgp;
  int slen, colorps, colortype, bits;
  int err = 0;
  int extra = 0;

  imgp = img->image_data;

  /* 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)


  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; i < ncolors; i++)
    {
      if (!Isgray (img, i))
      {
	colortype = F_REDUCED;
	slen = w * 3;
	bits = 8;
	colorps = 1;
	break;
      }
    }
  }

  /*  build a temporary dictionary */
  PSprintf ("20 dict begin\n\n");

  /*  define string to hold a scanline's worth of data */
  PSprintf ("/pix %d string def\n\n", slen);

  /*  position and scaling */
  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);

  if (colortype == F_BWDITHER)
  {
    /*  1-bit dither code uses 'image' */
    int flipbw = 0;

    /*  set if color#0 is 'white' */
    if ((ncolors == 2 &&
	 MONO (img->reds[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 *) XtMalloc (w * 2);
    if (!rleline)
    {
      fprintf (stderr, "failed to malloc space for rleline\n");
      return;
    }

    for (i = 0; i < h && err != EOF; i++)
    {
      rlen = PSrle_encode (imgp, rleline, w);
      imgp += w;
      for (j = 0; j < rlen && err != EOF; j++)
	err = PShex (rleline[j], False);
      err = PShex ('\0', True);	/*  Flush the hex buffer */
    }
    XtFree(rleline);
  }

  /*  stop using temporary dictionary */
  PSprintf ("end\n");
  PSprintf ("grestore\n");

  /*  move currentpoint just right of image */
  PSprintf ("%d 0 R\n", w + extra);

  /* 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.
 |
*/

String 
ParseTextToPSString (hw, elist, startp, endp, start_pos, end_pos,
		     space_width, lmargin, fontfamily)
HTMLWidget hw;
struct ele_rec *elist;
struct ele_rec *startp;
struct ele_rec *endp;
int start_pos, end_pos;
int space_width;
int lmargin;
int fontfamily;
{

  /* the fontfamily parameter is new
   * The font is encoded as:
   *      0: times (default for now)
   *      1: helvetica
   *      2: new century schoolbook
   *      3: lucida
   */

  int xpos, ypos, epos;
  int height;
  int pagewidth;
  int line = -1;
  struct ele_rec *eptr;
  struct ele_rec *start;
  struct ele_rec *end;
  struct ele_rec *last;
  struct ele_rec *tmpptr;

  if (startp == NULL)
    return (NULL);


  /*
   * Get the foreground and background colors so we can check later
   * for black&white documents
   */
  {
    unsigned long fg_pixel, bg_pixel;

    XtVaGetValues (hw->html.view,
		   XtNbackground, &bg_pixel,
		   NULL);
    XtVaGetValues ((Widget) hw,
		   XtNforeground, &fg_pixel,
		   NULL);
    fg_color.pixel = fg_pixel;
    bg_color.pixel = bg_pixel;
    XQueryColor (XtDisplay (hw->html.view),
		 DefaultColormap (XtDisplay (hw->html.view),
				  DefaultScreen (XtDisplay (hw->html.view))),
		 &fg_color);
    XQueryColor (XtDisplay (hw->html.view),
		 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;
  }

  /* 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;	/* seems more reasonable */
#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_WIDTH)
    Points_Pixel = Points_Pixel * (float) PAGE_WIDTH / pagewidth;
  Pixels_Page = (int) (PAGE_HEIGHT / Points_Pixel);


  PSinit ();
  PSheader (hw->html.title, fontfamily);
  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;
      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;
      }
      ypos = eptr->y - PS_page_offset;
      xpos = eptr->x - lmargin;
      if (xpos < 0)
	xpos = 0;

      /* check if line fits completly on page */
      if (ypos + height > PS_start_y + Pixels_Page)
      {
	PS_start_y = ypos;
	PSshowpage ();
	PSnewpage ();
      }
      PSmoveto (xpos, ypos);
    }

    switch (eptr->type)
    {

    case E_TEXT:
      {
	String tptr;
	int ascent;

	if (eptr == start)
	  tptr = (String) (eptr->edata + start_pos);
	else
	  tptr = (String) eptr->edata;

	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);
	PStext (tptr, eptr->underline_number);	/* insert text */
	break;
      }

    case E_BULLET:
      {
	int width;
	int offset;

	PSfont (hw, eptr->font, fontfamily);
	width = eptr->font->max_bounds.lbearing +
	  eptr->font->max_bounds.rbearing;
	/* 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 +
	    eptr->y_offset +
	    eptr->next->font->ascent;
	else
	  offset = eptr->y_offset + eptr->font->ascent;

	PSmove_offset (offset - width / 4);
	PSbullet (eptr->indent_level, eptr->line_height);
	break;
      }

    case E_IMAGE:
      {

	PSmove_offset (eptr->y_offset);
	PSimage (eptr->pic_data, (eptr->anchorHRef != NULL));
	break;
      }

    case E_LINEFEED:
      {
	break;
      }
    case E_HRULE:
      {
	PSmove_offset (eptr->y_offset);
	PShrule (hw->html.doc_width);
	break;
      }
    }
    last = eptr;
    eptr = eptr->next;
  }

  PSshowpage ();
  PStrailer ();

  return (PS_string);
}
