1 /* HTML-PSformat.c -  Module for NCSA's Mosaic software
2  *
3  * Purpose:	to parse Hypertext widget contents into appropriate PostScript
4  *
5  * Author:	Ameet A. Raval & Frans van Hoesel
6  *		(aar@gfdl.gov & hoesel@chem.rug.nl).
7  *		send bugreports to hoesel@chem.rug.nl
8  *
9  * Institution: for Ameet A. Raval:
10  *			Geophysical Fluid Dynamics Laboratory,
11  *			National Oceanic and Atmospheric Administration,
12  *			U.S. Department of Commerce
13  *			P.O. Box 308
14  *			Princeton, NJ 08542
15  *		for Frans van Hoesel:
16  *			Xtreme graphics software
17  *			Herepoortenmolendrift 36
18  *			9711 DH  Groningen
19  *			The Netherlands
20  *
21  * Date:		1 aug 1993
22  * Modification:	8 nov 1993
23  *				o added support for bold/italics courier
24  *				o removed unused or no longer needed stuff
25  *				o fixed the font alignment problem
26  *		 	23 nov 1993
27  *				o added support for horizontal ruler
28  *				o on request of Ameet, added a specific
29  *					line about whome to send bugreports to
30  *
31  * Copyright:   This work is the product of the United States Government,
32  *		and is precluded from copyright protection.  It is hereby
33  *		released into the public domain.
34  *
35  * WE MAKE NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
36  * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED
37  * WARRANTY. WE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE
38  * USERS OF THIS SOFTWARE.
39  *
40  *		pieces of code are taken from xvps by kind
41  *		permission of John Bradley.
42  */
43 
44 #include <varargs.h>
45 
46 #include <string.h>
47 #include <stdio.h>
48 #include <ctype.h>
49 #include <math.h>
50 #include <stdlib.h>
51 #include "HTMLP.h"
52 
53 /* Fix thanks to robm. */
54 #ifdef __alpha
55 #include <Xm/VaSimpleP.h>
56 #endif
57 
58 #define CR '\015'
59 #define LF '\012'
60 
61 extern int SwapElements();
62 
63 /* the next page sizes are a compromise between letter sized paper
64  * (215.9 x 279.4 mm) and european standard A4 sized paper (210.0 x 297.0 mm).
65  * Note that PAGE_WIDTH is not the actual width of the paper
66  */
67 #define TOP_MARGIN	(10*72)
68 #define BOT_MARGIN	(0.7*72)
69 #define LEFT_MARGIN	(0.6*72)
70 #define PAGE_HEIGHT	(TOP_MARGIN - BOT_MARGIN)
71 #define PAGE_WIDTH	(8*72)
72 
73 #define F_FULLCOLOR	0
74 #define F_GREYSCALE	1
75 #define F_BWDITHER 	2
76 #define F_REDUCED  	3
77 
78 #define L_PAREN		'('
79 #define R_PAREN		')'
80 #define B_SLASH		'\\'
81 #define MAX_ASCII	'\177'
82 
83 #ifdef _NO_PROTO
84 # define ARG0(v0)			()
85 # define ARG1(t1,v1)			(v1) t1 v1;
86 # define ARG2(t1,v1,t2,v2)		(v1,v2) t1 v1;t2 v2;
87 # define ARG3(t1,v1,t2,v2,t3,v3)	(v1,v2,v3) t1 v1;t2 v2;t3 v3;
88 # define ARG4(t1,v1,t2,v2,t3,v3,t4,v4)	(v1,v2,v3,v4) t1 v1;t2 v2;t3 v3;t4 v4;
89 # 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;
90 # define ARG1V(t1,v1,e2)		(v1) t1 v1;
91 #else
92 # define ARG0(v0)			(v0)
93 # define ARG1(t1,v1)			(t1 v1)
94 # define ARG2(t1,v1,t2,v2)		(t1 v1, t2 v2)
95 # define ARG3(t1,v1,t2,v2,t3,v3)	(t1 v1, t2 v2, t3 v3)
96 # define ARG4(t1,v1,t2,v2,t3,v3,t4,v4)	(t1 v1, t2 v2, t3 v3, t4 v4)
97 # define ARG5(t1,v1,t2,v2,t3,v3,t4,v4,t5,v5)	(t1 v1, t2 v2, t3 v3, t4 v4, t5 v5)
98 # define ARG1V(t1,v1,e2)		(t1 v1, e2)
99 #endif /* _NO_PROTO */
100 
101 /* MONO returns total intensity of r,g,b components .33R+ .5G+ .17B */
102 #define MONO(rd,gn,bl) (((rd)*11 + (gn)*16 + (bl)*5) >> 13)
103 
104 /* PSconst_out outputs to the postscript buffer an array of constant
105  * strings
106  */
107 #define PSconst_out(txt) {\
108 	int n=(sizeof txt)/(sizeof txt[0]); \
109 	  	int i; \
110 	  	for (i=0; i<n; i++) { \
111 			PSprintf("%s\n", txt[i]) ; \
112 		} \
113 }
114 
115 
116 /* for regular-font, bold-font, italic-font, fixed-font */
117 typedef enum { RF, BF, IF, FF, FB, FI } PS_fontstyle;
118 
119 static int PS_size, PS_len, PS_offset, PS_curr_page, PS_start_y, PS_hexi;
120 static int PS_page_offset;
121 static char *PS_string;
122 static float Points_Pixel;
123 static int Pixels_Page;
124 static PS_fontstyle PS_oldfn = RF;
125 static int PS_fontascent = 0;
126 static int PS_oldfs = 0;
127 
128 static XColor fg_color, bg_color;
129 
130 
131 /*__________________________________________________________________________
132  |
133  | GetDpi - return Dots-per-inch of the screen
134  |
135  | calculate the pixel density in dots per inch on the current widget
136  | screen
137  |
138 */
139 
ARG1(HTMLWidget,hw)140 static float GetDpi ARG1(HTMLWidget, hw) {
141 	Screen *s = XtScreen(hw);
142 	float dpi;
143 
144 	dpi = 25.4 * WidthOfScreen(s) / WidthMMOfScreen(s);
145 	/* no earthly monitor does this */
146 	if (dpi<1.0 || dpi>10000.0)
147 		dpi = 72.0;
148 	return dpi;
149 }
150 
151 
152 /*__________________________________________________________________________
153  |
154  | PSprintf - dynamic string concatenation function.
155  |
156  |  In successive calls, the formatted string will be appended to the global
157  |  output string Sp.
158  |  It assumes that on each call, the length of the text appended to Sp
159  |  is less than 1024.
160  |  The format string is used as in printf, so you can use additional
161  |  arguments.
162  |
163  |  When successful, PSprintf returns the number of characters printed
164  |  in this call, otherwise it returns EOF (just as printf does)
165  |
166 */
167 
168 #ifdef BROKEN_SOLARIS_COMPILER_STDARG
169 /* "Looks like there's a bug in Sun's C compiler and the stdarg.h use
170    of va_start() in HTML-PSformat.c. Until the SunPro folks can take a
171    look at the problem, the following pre-ANSI code should workaround
172    the problem." */
ARG1V(char *,format,...)173 static int PSprintf ARG1V(char *,format, ...) {
174         va_dcl
175         va_list args;
176         int     len;
177         char    *s;
178 
179         if (PS_size - PS_len < 1024) {
180                 PS_size += 1024;
181                 if ((s = (char *) realloc(PS_string, PS_size)) == NULL) {
182                         fprintf(stderr, "PSprintf malloc failed\n");
183                         return(EOF);
184                 }
185                 PS_string = s;
186         }
187         va_start(args);
188         len = vsprintf(PS_string+PS_len, format, args);
189         /* this is a hack to make it work on systems were vsprintf(s,.)
190          * returns s, instead of the len.
191          */
192         if (len != EOF && len != NULL)
193                 PS_len += strlen(PS_string+PS_len);
194         va_end(args);
195         return(len);
196 }
197 #else /* not BROKEN_SOLARIS_COMPILER_STDARG */
198 static int
PSprintf(format,va_alist)199 PSprintf (format, va_alist)
200 char* format;
201 va_dcl
202 {
203 	int 	len;
204 	char 	*s;
205 	va_list	args;
206 
207 	if (PS_size - PS_len < 1024) {
208 		PS_size += 1024;
209 		if ((s = (char *) realloc(PS_string, PS_size)) == NULL) {
210 			fprintf(stderr, "PSprintf malloc failed\n");
211 			return(EOF);
212 		}
213 		PS_string = s;
214 	}
215 	va_start(args);
216 	len = vsprintf(PS_string+PS_len, format, args);
217 	/* this is a hack to make it work on systems were vsprintf(s,...)
218 	 * returns s, instead of the len.
219 	 */
220 	if (len != EOF && len != 0)
221 		PS_len += strlen(PS_string+PS_len);
222 	va_end(args);
223 	return(len);
224 }
225 #endif /* not BROKEN_SOLARIS_COMPILER_STDARG */
226 
227 /*__________________________________________________________________________
228  |
229  | PShex - output hex byte
230  |
231  | Append the byte "val" to an internal string buffer in hexadecimal
232  | format.  If the argument "flush" is True, or if the buffer has filled
233  | up, flush the buffer to the larger postscript output buffer (using
234  | PSprintf).
235  |
236 */
237 
ARG2(unsigned char,val,int,flush)238 static int PShex ARG2(unsigned char,val, int,flush) {
239 
240 	static unsigned char hexline[80];
241 	static char digit[] = "0123456789abcdef";
242 
243 	if (!flush) {
244 		hexline[PS_hexi++] = (char) digit[((unsigned) val >>
245 			(unsigned) 4) & (unsigned) 0x0f];
246 		hexline[PS_hexi++] = (char) digit[(unsigned) val &
247 			(unsigned) 0x0f];
248 	}
249 
250         /* Changed from ">78" to ">77" on advice of
251            debra@info.win.tue.nl (Paul De Bra). */
252 	if ((flush && PS_hexi) || (PS_hexi>77)) {
253 		hexline[PS_hexi] = '\0';
254 		PS_hexi=0;
255 		return (PSprintf("%s\n", hexline));
256 	}
257 	return (0);
258 }
259 
260 
261 /*__________________________________________________________________________
262  | PSfont - change font
263  |
264  | change local font in buf to "font"
265  | fontfamily indicates if the overall style is times, helvetica, century
266  | schoolbook or lucida.
267  |
268 */
269 
ARG3(HTMLWidget,hw,XFontStruct *,font,int,fontfamily)270 static void PSfont ARG3( HTMLWidget,hw, XFontStruct *,font, int,fontfamily) {
271 
272 	PS_fontstyle fn;
273 	int style, size;
274 	int fs;
275 
276 	static PS_fontstyle fontstyle[17] = {
277 		RF, IF, BF, FF, BF, BF, BF, BF, BF,
278 		BF, IF, FF, FF, FB, FI, FB, FI
279 	};
280 
281 	static char fnchar[6][3] = {"RF", "BF", "IF", "FF", "FB", "FI"};
282 
283 	/* fontsizes as set in gui.c and in HTML.c (listing font is only
284 	 * defined in HTML.c)
285 	 */
286 	static int fontsizes[4][3][17] = {
287 		/* times font sizes */
288 		14, 14, 14, 14, 18, 17, 14, 12, 10, 8, 14, 12, 12, 14, 14, 12, 12,
289 		17, 17, 17, 17, 24, 18, 17, 14, 12, 10, 17, 14, 12, 17, 17, 14, 14,
290 		20, 20, 20, 20, 25, 24, 20, 18, 17, 14, 20, 18, 12, 20, 20, 18, 18,
291 		/* helvetica sizes */
292 		14, 14, 14, 14, 18, 17, 14, 12, 10, 8,
293 			14, 12, 12, 14, 14, 12, 12,
294 		17, 17, 17, 17, 24, 18, 17, 14, 12, 10,
295 			17, 14, 12, 17, 17, 14, 14,
296 		20, 20, 20, 20, 25, 24, 20, 18, 17, 14,
297 			20, 18, 12, 20, 20, 18, 18,
298 		/* new century schoolbook sizes */
299 		14, 14, 14, 14, 18, 17, 14, 12, 10, 8,
300 			14, 12, 12, 14, 14, 12, 12,
301 		18, 18, 18, 18, 24, 18, 17, 14, 12, 10,
302 			18, 14, 12, 18, 18, 14, 14,
303 		20, 20, 20, 20, 25, 24, 20, 18, 17, 14,
304 			20, 18, 12, 20, 20, 18, 18,
305 		/* lucida sizes */
306 		14, 14, 14, 14, 18, 17, 14, 12, 11, 10,
307 			14, 12, 12, 14, 14, 12, 12,
308 		17, 17, 17, 17, 24, 18, 17, 14, 12, 10,
309 			17, 14, 12, 17, 17, 14, 14,
310 		20, 20, 20, 20, 25, 24, 20, 18, 17, 14,
311 			20, 18, 12, 20, 20, 18, 18
312 	};
313 
314 	/* next is for each fontfamily the ascent value as given by the
315 	 * medium sized bold x-font (the regular font has the same
316 	 * ascent value for both the medium and the large size Century
317 	 * font).
318 	 * it is use in the check for the fontsize (small, medium, large)
319 	 */
320 	static int medium_fontascent[4] = {
321 		14, 14, 16, 15
322 	};
323 
324 	/* for each fontfamily, for each fontsize, and for each font style
325 	 * give the ascent value, so the output from Postscript is correct.
326 	 * If the value is given between parenthesis, then it is different
327 	 * from the value as stored in the x-font.
328 	 * Note that this is a fix, and need to be changed, if the browser
329 	 * is fixed (in the current version 1.2 the baseline of various fonts
330 	 * is not aligned very well).
331 	 */
332 	static int fontascent[4][3][17] = {
333 		/*rg, itl, bld, fix,  h1,  h2,  h3,  h4,  h5,  h6,
334 		add, pla, lis, fixbold, fixital, plabold, plaital, */
335 		/* times */
336 		 12 ,(11), 12 ,(10),(15),(14), 12 ,(10), (8), (6),
337 		      11 , (9), 10, 10 , 10 , (9), (9),
338 		(13),(13),(14),(12),(20),(15),(14), 12 ,(10), (8),
339 		      13 ,(10), 10 ,(12),(12),(10),(10),
340 		(16),(15),(15),(13),(21),(20),(15),(15),(14), 12 ,
341 		      15 ,(13), 10,(13),(13),(13),(13),
342 		/* helvetica */
343 		(12),(12),(12),(10),(15),(14),(12),(10), (9), (7),
344 		     (12), (9), 10 ,(10),(10), (9), (9),
345 	 	(14),(14),(14),(12),(22),(15),(14),(12),(10), (9),
346 		     (14),(10), 10 ,(12),(12),(10),(10),
347 		(16),(16),(16),(13),(22),(22),(16),(15),(14),(12),
348 		     (16),(13), 10 ,(13),(13),(13),(13),
349 		/* new century schoolbook */
350 		(12),(12), 13 ,(10),(16), 14 , 13 ,(10), (9), (7),
351 		     (12), (9), 10 ,(10),(10), (9), (9),
352 		(16),(14),(16),(13),(22),(16), 14 , 13 ,(10), (9),
353 		     (14),(10), 10 ,(13),(13),(10),(10),
354 		(17),(16),(17),(13),(22),(22),(17),(16), 14 , 13 ,
355 		     (16),(13), 10 ,(13),(13),(13),(13),
356 		/* lucida bright */
357 		 11 ,(11), 11 ,(11),(15),(14), 11 ,(10), (9), (7),
358 		      11 , (9), 10, 11 , 10 , (9), (9),
359 		(14),(15),(14),(13),(20),(15),(14), 11 ,(10), (7),
360 		      15 ,(11), 10 ,(13),(13),(11),(10),
361 		(17),(17),(17),(16),(21),(20),(17),(15),(14), 11 ,
362 		      17 ,(14), 10,(16),(13),(14),(13)
363 	};
364 
365 	/* NULL case - reflush old font or the builtin default: */
366 	if (hw==NULL || font==NULL) {
367 		if (PS_oldfs != 0)
368 			PSprintf( "%2s %d SF\n", fnchar[PS_oldfn], PS_oldfs);
369 		return;
370 	}
371 	/* added the next line in case xmosaic version 199.4 has more fonts */
372 	style = 3;
373 
374 	if (font == hw->html.font) {
375 		style = 0;
376 	} else if (font == hw->html.italic_font) {
377 		style = 1;
378 	} else if (font == hw->html.bold_font) {
379 		style = 2;
380 	} else if (font == hw->html.fixed_font) {
381 		style = 3;
382 	} else if (font == hw->html.header1_font) {
383 		style = 4;
384 	} else if (font == hw->html.header2_font) {
385 		style = 5;
386 	} else if (font == hw->html.header3_font) {
387 		style = 6;
388 	} else if (font == hw->html.header4_font) {
389 		style = 7;
390 	} else if (font == hw->html.header5_font) {
391 		style = 8;
392 	} else if (font == hw->html.header6_font) {
393 		style = 9;
394 	} else if (font == hw->html.address_font) {
395 		style = 10;
396 	} else if (font == hw->html.plain_font) {
397 		style = 11;
398 	} else if (font == hw->html.listing_font) {
399 		style = 12;
400 	} else if (font == hw->html.fixedbold_font) {
401 		style = 13;
402 	} else if (font == hw->html.fixeditalic_font) {
403 		style = 14;
404 	} else if (font == hw->html.plainbold_font) {
405 		style = 15;
406 	} else if (font == hw->html.plainitalic_font) {
407 		style = 16;
408 	}
409 
410 	/* check size, by looking at the size of the regular font */
411 	size = 1;
412 	if (hw->html.bold_font->ascent > medium_fontascent[fontfamily]) {
413 		/* large font */
414 		size = 2;
415 	} else if (hw->html.bold_font->ascent < medium_fontascent[fontfamily]) {
416 		/* small font */
417 		size = 0;
418 	}
419 	fn = fontstyle[style];
420 	fs = fontsizes[fontfamily][size][style];
421 	PS_fontascent = fontascent[fontfamily][size][style];
422 
423 	if (fn != PS_oldfn || fs != PS_oldfs) {
424 		PSprintf( "%2s %d SF\n", fnchar[fn], fs);
425 		PS_oldfn=fn, PS_oldfs=fs;
426 	}
427 }
428 
429 
430 /*__________________________________________________________________________
431  |
432  | PSshowpage - end of page function
433  |
434  | show the current page and restore any changes to the printer state
435  |
436 */
437 
ARG0(void)438 static void PSshowpage ARG0(void) {
439 
440 	PSprintf("showpage restore\n");
441 }
442 
443 
444 /*__________________________________________________________________________
445  |
446  | PSnewpage - begin a fresh page
447  |
448  | increment the page count and handle the structured comment
449  | conventions
450  |
451 */
452 
ARG0(void)453 static void PSnewpage ARG0(void) {
454 
455 	PS_curr_page++;
456 
457 	/* the PostScript reference Manual states that the Page: Tag
458            should have a label and a ordinal; otherwise programs like
459            psutils fail    -gustaf */
460 	PSprintf("%%%%Page: %d %d\n", PS_curr_page, PS_curr_page);
461 	PSprintf("save\nNP\n");
462 	PSfont( NULL, NULL, 0);	/* force re-flush of last font used */
463 }
464 
465 
466 /*__________________________________________________________________________
467  |
468  | PSinit_latin1 - handle ISO encoding
469  |
470  | print out initializing PostScript text for ISO Latin1 font encoding
471  | This code is copied from the Idraw program (from Stanford's InterViews
472  | package), courtesy of Steinar Kjaernsr|d, steinar@ifi.uio.no
473  |
474 */
475 
ARG0(void)476 static void PSinit_latin1 ARG0(void) {
477 
478 	static char *txt[] = {
479 
480 	"/reencodeISO {",
481 	"dup dup findfont dup length dict begin",
482 	"{ 1 index /FID ne { def }{ pop pop } ifelse } forall",
483 	"/Encoding ISOLatin1Encoding D",
484 	"currentdict end definefont",
485 	"} D",
486 	"/ISOLatin1Encoding [",
487 	"/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef",
488 	"/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef",
489 	"/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef",
490 	"/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef",
491 	"/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright",
492 	"/parenleft/parenright/asterisk/plus/comma/minus/period/slash",
493 	"/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon",
494 	"/less/equal/greater/question/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N",
495 	"/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash/bracketright",
496 	"/asciicircum/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m",
497 	"/n/o/p/q/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/asciitilde",
498 	"/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef",
499 	"/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef",
500 	"/.notdef/dotlessi/grave/acute/circumflex/tilde/macron/breve",
501 	"/dotaccent/dieresis/.notdef/ring/cedilla/.notdef/hungarumlaut",
502 	"/ogonek/caron/space/exclamdown/cent/sterling/currency/yen/brokenbar",
503 	"/section/dieresis/copyright/ordfeminine/guillemotleft/logicalnot",
504 	"/hyphen/registered/macron/degree/plusminus/twosuperior/threesuperior",
505 	"/acute/mu/paragraph/periodcentered/cedilla/onesuperior/ordmasculine",
506 	"/guillemotright/onequarter/onehalf/threequarters/questiondown",
507 	"/Agrave/Aacute/Acircumflex/Atilde/Adieresis/Aring/AE/Ccedilla",
508 	"/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute/Icircumflex",
509 	"/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis",
510 	"/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute",
511 	"/Thorn/germandbls/agrave/aacute/acircumflex/atilde/adieresis",
512 	"/aring/ae/ccedilla/egrave/eacute/ecircumflex/edieresis/igrave",
513 	"/iacute/icircumflex/idieresis/eth/ntilde/ograve/oacute/ocircumflex",
514 	"/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex/udieresis",
515 	"/yacute/thorn/ydieresis",
516 	"] D",
517 	"[RF BF IF FF FB FI] {reencodeISO D} forall"
518 	};
519 
520 	PSconst_out(txt);
521 }
522 
523 
524 /*__________________________________________________________________________
525  |
526  | PSinit - initialize Postscript output
527  |
528  | does the initialization per html document
529  |
530 */
531 
ARG0(void)532 static void PSinit ARG0(void) {
533 	PS_size = PS_len = PS_offset = PS_hexi = PS_page_offset = 0;
534 	PS_start_y = 0;
535 	PS_string = (char *) malloc(1);
536 	PS_oldfs = 0;
537 	PS_oldfn = RF;
538 	PS_curr_page = 0 ;
539 }
540 
541 
542 /*__________________________________________________________________________
543  |
544  | PSheader - initialize Postscript output
545  |
546  | prints out the prolog
547  |
548 */
549 
ARG2(char *,title,int,font)550 static void PSheader ARG2(char *,title, int,font) {
551 
552 	static char *fontname[] = {
553 		/* in order: regular, bold, italic */
554 		"Times-Roman", "Times-Bold", "Times-Italic",
555 		"Helvetica", "Helvetica-Bold", "Helvetica-Oblique",
556 		"NewCenturySchlbk-Roman", "NewCenturySchlbk-Bold",
557 			"NewCenturySchlbk-Italic",
558 		/* this is a nasty trick, I have put Times in place of
559 		 * Lucida, becaus emost printers don't have Lucida font
560 		 */
561 		"Times-Roman", "Times-Bold", "Times-Italic"
562 		/* "Lucida", "Lucida-Bold", "Lucida-Italic" */
563 	};
564 
565 	static char *txt[] = {
566 
567 	"%%Creator: NCSA Mosaic, Postscript by Ameet Raval & Frans van Hoesel",
568 	"%%Pages: (atend)",
569 	"%%EndComments",
570 	"save",
571 	"/D {def} def /E {exch} D",
572 	"/M {moveto} D",
573 	"/S {show} D",
574 	"/R {rmoveto} D",
575 	"/L {lineto} D",
576 	"/RL {rlineto} D",
577 	"/SQ {newpath 0 0 M 0 1 L 1 1 L 1 0 L closepath} D",
578 	"/U {gsave currentpoint currentfont /FontInfo get /UnderlinePosition get",
579 	" 0 E currentfont /FontMatrix get dtransform E pop add newpath moveto",
580 	" dup stringwidth rlineto stroke grestore S } D",
581 	"/B {/r E D gsave -13 0  R currentpoint ",
582 	"  newpath r 0 360 arc closepath fill grestore } D",
583 	"/OB {/r E D gsave -13 0  R currentpoint ",
584 	"  newpath r 0 360 arc closepath stroke grestore } D",
585 	"/NP {xmargin topmargin translate scalfac dup scale } D",
586 	"/HR {/l E D gsave l 0 RL  stroke grestore } D",
587 	"/SF {E findfont E scalefont setfont } D",
588 	"/FF {/Courier } D",
589 	"/FB {/Courier-Bold } D",
590 	"/FI {/Courier-Oblique } D"
591 	};
592 
593 
594 	PSprintf("%%!PS-Adobe-1.0\n");
595         if (title)
596           {
597             char *tmp;
598             for (tmp = title; *tmp; tmp++)
599               if (*tmp == CR || *tmp == LF)
600                 *tmp = ' ';
601             PSprintf("%%%%Title: %s\n", title);
602           }
603 	PSprintf("%%%%DocumentFonts: %s %s %s Courier Courier-Bold Courier-Oblique\n",
604 		fontname[font*3], fontname[font*3+1], fontname[font*3+2]);
605 	PSconst_out(txt);
606 	PSprintf("/RF {/%s} D\n", fontname[font*3]);
607 	PSprintf("/BF {/%s} D\n", fontname[font*3+1]);
608 	PSprintf("/IF {/%s} D\n", fontname[font*3+2]);
609 
610 	PSinit_latin1();
611 
612 	PSprintf("/xmargin %d D\n", (int) LEFT_MARGIN);
613 	PSprintf("/topmargin %d D\n", (int) TOP_MARGIN);
614 	PSprintf("/scalfac %.5f D\n", Points_Pixel);
615 	PSprintf("%%%%EndProlog\n");
616 
617 }
618 
619 
620 /*__________________________________________________________________________
621  |
622  | PStrailer - write postscript trailer
623  |
624 */
625 
ARG0(void)626 static void PStrailer ARG0(void) {
627 
628 	PSprintf("%%%%Trailer\n");
629 	PSprintf("restore\n");
630 	PSprintf("%%%%Pages: %d\n", PS_curr_page);
631 }
632 
633 
634 /*__________________________________________________________________________
635  |
636  | PStext - output text
637  |
638  | show text "t", and protect special characters if needed
639  | if Underline is non-zero, the text is underlined.
640  |
641 */
642 
ARG2(String,t,int,underline)643 static void PStext ARG2(String,t, int,underline) {
644 	String	tp, t2;
645 	int	nspecial=0, nisochar=0;
646 
647 	tp=t;
648 	/* count # of special char's in text */
649 	while (*tp != '\0') {
650 		if (*tp == L_PAREN || *tp == R_PAREN || *tp == B_SLASH)
651 			nspecial++;
652 		else if (*(unsigned char *)tp > (unsigned char )MAX_ASCII)
653 			nisochar++;
654 		tp++;
655 	}
656 
657 	if (nspecial == 0 && nisochar == 0) {
658 		/*  no special char's, send out original string */
659 		PSprintf("(%s)%c\n", t, (underline)?'U':'S');
660 		return;
661 	}
662 	/*  create t2 to hold original text + "\"'s */
663 	t2 = (String) malloc((tp-t) + nspecial + 3*nisochar + 1);
664 
665 	if (t2==NULL) {
666 		fprintf(stderr, "PStext malloc failed\n");
667 		return;
668 	}
669 
670 	/*  for each char in t, if it is a special char, insert "\"
671 	 *  into the new string t2, then insert the actual char
672 	 */
673 	tp = t2;
674 	while (*t != '\0') {
675 		if (*t == L_PAREN || *t == R_PAREN || *t == B_SLASH) {
676 			*(tp++) = B_SLASH;
677 			*(tp++) = *t;
678 		} else if (*(unsigned char *)t > (unsigned char) MAX_ASCII) {
679 			 /*  convert to octal */
680 			*(tp++) = B_SLASH;
681 			*(tp++) = ((int)(*(unsigned char *)t)>>6 & 007) + '0';
682 			*(tp++) = ((int)(*(unsigned char *)t)>>3 & 007) + '0';
683 			*(tp++) = ((int)(*(unsigned char *)t) & 007) + '0';
684 		} else {
685 			*(tp++) = *t;
686 		}
687 		t++;
688 	}
689 	*(tp) = '\0';
690 	PSprintf("(%s)%c\n", t2, (underline)?'U':'S');
691 
692 	free(t2);
693 }
694 
695 
696 /*__________________________________________________________________________
697  |
698  | PSbullet - output a bullet
699  |
700  | the bullet is normally filled, except for a bullet with an indent level
701  | of two. The size of the higher level bullets is just somewhat smaller
702  |
703 */
704 
ARG2(int,level,int,size)705 static void PSbullet ARG2( int, level, int, size) {
706 
707 	if (size < 6) size = 6;
708 
709 	if (level <2 )
710 		PSprintf( " %f B\n", size/5.5);
711 	else if (level == 2)
712 		PSprintf( " %f OB\n", size/5.5);
713 	else
714 		PSprintf(" %f B\n", size/7.5);
715 }
716 
717 /*__________________________________________________________________________
718  |
719  | PShrule - draw a horizontal line with the given width
720  |
721  | nothing special, just draw a line, from the current position to
722  | the right side of the paper.
723  |
724 */
725 
ARG1(int,length)726 static void PShrule ARG1(int, length) {
727 
728 	PSprintf("%d HR\n", length);
729 }
730 
731 
732 /*__________________________________________________________________________
733  |
734  | PSmoveto - move to new x,y location
735  |
736  | if the Y value does not fit on the current page, begin a new page
737  | (I think in the current implementation, this never happens)
738  |
739 */
740 
ARG2(int,x,int,y)741 static void PSmoveto ARG2( int,x, int,y) {
742 
743 	if (y > PS_start_y + Pixels_Page) {
744 		PS_start_y = y;
745 		PSshowpage();
746 		PSnewpage();
747 	}
748 	PS_offset = 0;
749 	PSprintf( "%d %d M\n", x, -(y - PS_start_y));
750 }
751 
752 
753 /*__________________________________________________________________________
754  |
755  | PSmove_offset - set Y-offset
756  |
757  | do a relative vertical move, whenever the offset changes
758  |
759 */
760 
ARG1(int,offset)761 static void PSmove_offset ARG1( int, offset) {
762 
763 	if (offset != PS_offset) {
764 		PSprintf("0 %d R\n", PS_offset - offset );
765 		PS_offset = offset;
766 	}
767 }
768 
769 
770 /*__________________________________________________________________________
771  |
772  | PSrle_encode - perform run length encoding
773  |
774  | does the run-length encoding. This is done to reduce the file size and
775  | therefore the time to send the file to the printer. You get longer
776  | processing time instead.
777  |
778  | rle is encoded as such:
779  |  <count> <value>			# 'run' of count+1 equal pixels
780  |  <count | 0x80> <count+1 data bytes>	# count+1 non-equal pixels
781  | count can range between 0 and 127
782  |
783  | returns length of the rleline vector
784  |
785 */
786 
ARG3(unsigned char *,scanline,unsigned char *,rleline,int,wide)787 static int PSrle_encode ARG3(unsigned char *, scanline,
788 			     unsigned char *,rleline,
789 			     int,wide)
790 {
791 	int  i, j, blocklen, isrun, rlen;
792 	unsigned char block[256], pix;
793 
794 	blocklen = isrun = rlen = 0;
795 
796 	for (i=0; i<wide; i++) {
797 		/*  there are 5 possible states:
798 		 *   0: block empty.
799 		 *   1: block is a run, current pix == previous pix
800 		 *   2: block is a run, current pix != previous pix
801 		 *   3: block not a run, current pix == previous pix
802 		 *   4: block not a run, current pix != previous pix
803 		 */
804 
805 		pix = scanline[i];
806 
807 		if (!blocklen) {
808 			/* case 0:  empty */
809 	  		block[blocklen++] = pix;
810 	  		isrun = 1;
811 		} else if (isrun) {
812 	 	 	if (pix == block[blocklen-1]) {
813 				/*  case 1:  isrun, prev==cur */
814 				block[blocklen++] = pix;
815 	 		} else {
816 				/*  case 2:  isrun, prev!=cur */
817 				if (blocklen>1) {
818 					/*  we have a run block to flush */
819 		  			rleline[rlen++] = blocklen-1;
820 		  			rleline[rlen++] = block[0];
821 		  			/*  start new run block with pix */
822 					block[0] = pix;
823 		  			blocklen = 1;
824 				}  else {
825 		  			isrun = 0;
826 					/*  blocklen<=1, turn into non-run */
827 		  			block[blocklen++] = pix;
828 				}
829 			}
830 		} else {
831 			/* not a run */
832 	  		if (pix == block[blocklen-1]) {
833 				/* case 3: non-run, prev==cur */
834 				if (blocklen>1) {
835 					/*  have a non-run block to flush */
836 		  			rleline[rlen++] = (blocklen-1) | 0x80;
837 		  			for (j=0; j<blocklen; j++)
838 						rleline[rlen++] = block[j];
839 					/*  start new run block with pix */
840 		  			block[0] = pix;
841 					blocklen = isrun = 1;
842 				} else {
843 		  			isrun = 1;
844 					/*  blocklen<=1 turn into a run */
845 					block[blocklen++] = pix;
846 				}
847 	  		} else {
848 				/* case 4:  non-run, prev!=cur */
849 				block[blocklen++] = pix;
850 	  		}
851 		}
852 
853 		if (blocklen == 128) {   /* max block length.  flush */
854 	  		if (isrun) {
855 				rleline[rlen++] = blocklen-1;
856 				rleline[rlen++] = block[0];
857 	  		} else {
858 				rleline[rlen++] = (blocklen-1) | 0x80;
859 				for (j=0; j<blocklen; j++)
860 		  			rleline[rlen++] = block[j];
861 	  		}
862 	  		blocklen = 0;
863 		}
864 	}
865 
866 	if (blocklen) {   /* flush last block */
867 		if (isrun) {
868 	  		rleline[rlen++] = blocklen-1;
869 	  		rleline[rlen++] = block[0];
870 		} else {
871 	  		rleline[rlen++] = (blocklen-1) | 0x80;
872 	  		for (j=0; j<blocklen; j++)
873 				rleline[rlen++] = block[j];
874 		}
875   	}
876 
877 	return rlen;
878 }
879 
880 
881 /*__________________________________________________________________________
882  |
883  | PScolor_image - created postscript colorimage operator
884  |
885  | spits out code that checks if the PostScript device in question
886  | knows about the 'colorimage' operator.  If it doesn't, it defines
887  | 'colorimage' in terms of image (ie, generates a greyscale image from
888  | RGB data)
889  |
890 */
891 
ARG0(void)892 static void PScolor_image ARG0(void) {
893 
894 	static char *txt[] = {
895 
896 	"% define 'colorimage' if it isn't defined",
897 	"%   ('colortogray' and 'mergeprocs' come from xwd2ps",
898 	"%	 via xgrab)",
899 	"/colorimage where   % do we know about 'colorimage'?",
900 	"  { pop }		   % yes: pop off the 'dict' returned",
901 	"  {				 % no:  define one",
902 	"	/colortogray {  % define an RGB->I function",
903 	"	  /rgbdata exch store	% call input 'rgbdata'",
904 	"	  rgbdata length 3 idiv",
905 	"	  /npixls exch store",
906 	"	  /rgbindx 0 store",
907 	"	  /grays npixls string store  % str to hold the result",
908 	"	  0 1 npixls 1 sub {",
909 	"		grays exch",
910 	"		rgbdata rgbindx	   get 20 mul	% Red",
911 	"		rgbdata rgbindx 1 add get 32 mul	% Green",
912 	"		rgbdata rgbindx 2 add get 12 mul	% Blue",
913 	"		add add 64 idiv	  % I = .5G + .31R + .18B",
914 	"		put",
915 	"		/rgbindx rgbindx 3 add store",
916 	"	  } for",
917 	"	  grays",
918 	"	} bind def\n",
919 		/* Utility procedure for colorimage operator.
920 		 * This procedure takes two procedures off the
921 		 * stack and merges them into a single procedure
922 		*/
923 	"	/mergeprocs { % def",
924 	"	  dup length",
925 	"	  3 -1 roll dup length dup 5 1 roll",
926 	"	  3 -1 roll add array cvx dup",
927 	"	  3 -1 roll 0 exch putinterval",
928 	"	  dup 4 2 roll putinterval",
929 	"	} bind def\n",
930 	"	/colorimage { % def",
931 		/* remove 'false 3' operands */
932 	"	  pop pop",
933 	"	  {colortogray} mergeprocs",
934 	"	  image",
935 	"	} bind def",
936 		/* end of 'false' case */
937 	"  } ifelse"
938 	};
939 
940 	PSconst_out(txt);
941 }
942 
943 /*__________________________________________________________________________
944  |
945  | PScolormap - write colormap
946  |
947  | spits out code for the colormap of the following image
948  | if !color, it spits out a mono-ized graymap
949  |
950 */
951 
ARG5(int,color,int,nc,int *,rmap,int *,gmap,int *,bmap)952 static void PScolormap ARG5(int,color,
953 			    int,nc,
954 			    int *,rmap,
955 			    int *,gmap,
956 			    int *,bmap) {
957 
958 	int i;
959 
960 	/*  define the colormap */
961 	PSprintf("/cmap %d string def\n\n\n", nc * ((color) ? 3 : 1));
962 
963 	/*  load up the colormap */
964 	PSprintf("currentfile cmap readhexstring\n");
965 
966 	for (i=0; i<nc; i++) {
967 		if (color)
968 			PSprintf("%02x%02x%02x ", rmap[i]>>8,
969 				gmap[i]>>8, bmap[i]>>8);
970 		else
971 			PSprintf("%02x ", MONO(rmap[i], gmap[i], bmap[i]));
972 		if ((i%10) == 9)
973 			PSprintf("\n");
974 	}
975 	PSprintf("\n");
976 	PSprintf("pop pop\n"); /* lose return values from readhexstring */
977 }
978 
979 
980 /*__________________________________________________________________________
981  |
982  | PSrle_cmapimage - define rlecmapimage operator
983  |
984 */
985 
ARG1(int,color)986 static void PSrle_cmapimage ARG1(int,color) {
987 
988 	static char *txt[] = {
989 
990 	/* rlecmapimage expects to have 'w h bits matrix' on stack */
991 	"/rlecmapimage {",
992 	"  /buffer 1 string def",
993 	"  /rgbval 3 string def",
994 	"  /block  384 string def",
995 	"  { currentfile buffer readhexstring pop",
996 	"	/bcount exch 0 get store",
997 	"	bcount 128 ge",
998 	"	{ ",
999 	"	  0 1 bcount 128 sub",
1000 	"	{ currentfile buffer readhexstring pop pop"
1001 	};
1002 
1003 	static char *txt_color[] = {
1004 	"		/rgbval cmap buffer 0 get 3 mul 3 getinterval store",
1005 	"		block exch 3 mul rgbval putinterval",
1006 	"	  } for",
1007 	"	  block  0  bcount 127 sub 3 mul  getinterval",
1008 	"	}",
1009 	"	{ ",
1010 	"	  currentfile buffer readhexstring pop pop",
1011 	"	  /rgbval cmap buffer 0 get 3 mul 3 getinterval store",
1012 	"	  0 1 bcount { block exch 3 mul rgbval putinterval } for",
1013 	"	  block 0 bcount 1 add 3 mul getinterval",
1014 	"	} ifelse",
1015 	"  }",
1016 	"  false 3 colorimage",
1017 	"} bind def"
1018 	};
1019 
1020 	static char *txt_gray[] = {
1021 	"		/rgbval cmap buffer 0 get 1 getinterval store",
1022 	"		block exch rgbval putinterval",
1023 	"	  } for",
1024 	"	  block  0  bcount 127 sub  getinterval",
1025 	"	}",
1026 	"	{ ",
1027 	"	  currentfile buffer readhexstring pop pop",
1028 	"	  /rgbval cmap buffer 0 get 1 getinterval store",
1029 	"	  0 1 bcount { block exch rgbval putinterval } for",
1030 	"	  block 0 bcount 1 add getinterval",
1031 	"	} ifelse",
1032 	"  }",
1033 	"  image",
1034 	"} bind def"
1035 	};
1036 
1037 	PSconst_out(txt);
1038 	if (color) {
1039 		PSconst_out(txt_color);
1040 	} else {
1041 		PSconst_out(txt_gray);
1042 	}
1043 }
1044 
1045 
1046 /*__________________________________________________________________________
1047  |
1048  | PSwrite_bw - write B&W image
1049  |
1050  | Write the given image array 'pic' (B/W stippled, 1 byte per pixel,
1051  | 0=blk,1=wht) out as hexadecimal, max of 72 hex chars per line.  If
1052  | 'flipbw', then 0=white, 1=black.  Returns '0' if everythings fine,
1053  | 'EOF' if writing failed.
1054  |
1055 */
1056 
ARG4(unsigned char *,pic,int,w,int,h,int,flipbw)1057 static int PSwrite_bw ARG4(unsigned char *,pic, int,w, int,h, int,flipbw) {
1058 
1059 	int	i, j;
1060 	int	err=0;
1061 	unsigned char outbyte, bitnum, bit;
1062 
1063 	outbyte = bitnum = 0;
1064 	for (i=0; i<h && err != EOF; i++) {
1065 		for (j=0; j<w && err != EOF; j++) {
1066 			bit = *(pic++);
1067 			outbyte = (outbyte<<1) | ((bit)&0x01);
1068 			bitnum++;
1069 
1070 			if (bitnum==8) {
1071 				if (flipbw)
1072 					outbyte = ~outbyte & 0xff;
1073 				err=PShex(outbyte, False);
1074 				outbyte = bitnum = 0;
1075 			}
1076 		}
1077 		if (bitnum) {	/*  few bits left over in this row */
1078 			outbyte <<= 8-bitnum;
1079 			if (flipbw)
1080 				outbyte = ~outbyte & 0xff;
1081 			err=PShex(outbyte, False);
1082 			outbyte = bitnum = 0;
1083 		}
1084 	}
1085 	err=PShex('\0', True);	/*  Flush the hex buffer if needed */
1086 
1087 	return err;
1088 }
1089 
1090 
1091 /*__________________________________________________________________________
1092  |
1093  | PSimage - generate image Postscript code
1094  |
1095  | Draw the image, unless there was no image, in which case an empty grey
1096  | rectangle is shown.
1097  | If anchor is set, a black border is shown around the image.
1098  | Positioning is not exactly that of Xmosaic's screen, but close enough.
1099  |
1100 */
1101 
ARG2(ImageInfo *,img,int,anchor)1102 static void PSimage ARG2( ImageInfo *,img , int, anchor) {
1103 
1104 	int ncolors = img->num_colors;
1105 	int i, j;
1106 	int w = img->width;
1107 	int h = img->height;
1108 	unsigned char *imgp;
1109 	int slen, colorps, colortype, bits;
1110 	int err=0;
1111 	int extra = 0;
1112 
1113 	imgp = img->image_data;
1114 
1115 	/* Isgray returns true if the nth color index is a gray value */
1116 #	define Isgray(i,n) (i->reds[n]==i->greens[n] && i->reds[n]==i->blues[n])
1117 	/* Is_bg returns true if the nth color index is the screen background */
1118 #	define Is_bg(i,n) (i->reds[n]==bg_color.red &&			\
1119 		i->greens[n]==bg_color.green && i->blues[n]==bg_color.blue)
1120 	/* Is_fg returns true if the nth color index is the screen foreground */
1121 #	define Is_fg(i,n) (i->reds[n]==fg_color.red &&			\
1122 		i->greens[n]==fg_color.green && i->blues[n]==fg_color.blue)
1123 
1124 
1125 	if (anchor) {
1126 		/*  draw an outline by drawing a slightly larger black square
1127 	 	 *  below the actual image
1128 	 	 */
1129 		PSprintf("gsave currentpoint %d sub translate ", h);
1130 		PSprintf("0 -2 translate %d %d scale\n", w+4, h+4);
1131 		PSprintf("SQ fill\n");
1132 		PSprintf("grestore\n");
1133 		extra = 4;
1134 	}
1135 
1136 	if (imgp == NULL) {
1137 		/*  image was not available... do something instead
1138 		 *  draw an empty square for example
1139 		 */
1140 		PSprintf("gsave currentpoint %d sub translate", h);
1141 		if (anchor)
1142 			PSprintf(" 2 0 translate");
1143 		else
1144 			PSprintf(" 0 2 translate");
1145 		PSprintf(" %d %d scale\n", w, h);
1146 		PSprintf("0.9 setgray SQ fill\n");
1147 		PSprintf("grestore\n");
1148 		/*  move currentpoint just right of image */
1149 		PSprintf("%d 0 R\n", w+extra);
1150 		return;
1151 	}
1152 
1153 	/*  this is a hack to see if the image is Black & White,
1154 	 *  Greyscale or 8 bit color
1155 	 *  assume it's bw if it has only one or two colors, both some grey's
1156 	 *  assume it's greyscale if all the colors (>2) are grey
1157 	 *  Images with only one color do occur too.
1158 	 */
1159 
1160 	if ((ncolors == 2 && (	(Isgray(img,0) && Isgray(img,1)) ||
1161 			      	(Is_bg(img,0) && Is_fg(img,1)) ||
1162 				(Is_fg(img,0) && Is_bg(img,1)) )) ||
1163 			ncolors == 1 && (Isgray(img,0) || Is_bg(img,0) ||
1164 					Is_fg(img,0))) {
1165 		colortype = F_BWDITHER;
1166 		slen = (w+7)/8;
1167 		bits = 1;
1168 		colorps = 0;
1169 	} else {
1170 		colortype = F_GREYSCALE;
1171 		slen = w;
1172 		bits = 8;
1173 		colorps = 0;
1174 		for (i=0; i<ncolors; i++) {
1175 			if (!Isgray(img,i)) {
1176 				colortype = F_REDUCED;
1177 				slen = w*3;
1178 				bits = 8;
1179 				colorps = 1;
1180 				break;
1181 			}
1182 		}
1183 	}
1184 
1185 	/*  build a temporary dictionary */
1186 	PSprintf("20 dict begin\n\n");
1187 
1188 	/*  define string to hold a scanline's worth of data */
1189 	PSprintf("/pix %d string def\n\n", slen);
1190 
1191 	/*  position and scaling */
1192 	PSprintf("gsave currentpoint %d sub translate", h);
1193 	if (anchor)
1194 		PSprintf(" 2 0 translate");
1195 	else
1196 		PSprintf(" 0 2 translate");
1197 	PSprintf(" %d %d scale\n", w, h);
1198 
1199 	if (colortype == F_BWDITHER) {
1200 		/*  1-bit dither code uses 'image' */
1201 		int flipbw = 0;
1202 
1203 		/*  set if color#0 is 'white' */
1204 		if ((ncolors == 2 &&
1205 			MONO(img->reds[0], img->greens[0],img->blues[0]) >
1206 			MONO(img->reds[1], img->greens[1], img->blues[1])) ||
1207 			(ncolors == 1 &&
1208 			MONO(img->reds[0], img->greens[0],img->blues[0]) >
1209 			MONO(127, 127, 127) )) {
1210 			flipbw=1;
1211 		}
1212 
1213 		/*  dimensions of data */
1214 		PSprintf("%d %d %d\n", w, h, bits);
1215 
1216 		/*  mapping matrix */
1217 		PSprintf("[%d 0 0 %d 0 %d]\n\n", w, -h, h);
1218 
1219 		PSprintf("{currentfile pix readhexstring pop}\n");
1220 		PSprintf("image\n");
1221 
1222 		/*  write the actual image data */
1223 		err = PSwrite_bw(imgp, w, h, flipbw);
1224 	} else {
1225 		/*  all other formats */
1226 		unsigned char *rleline = (unsigned char *) NULL;
1227 		int rlen;
1228 
1229 		/*  if we're using color, make sure 'colorimage' is defined */
1230 		if (colorps)
1231 			PScolor_image();
1232 
1233 		PScolormap(colorps, ncolors, img->reds, img->greens, img->blues);
1234 		PSrle_cmapimage(colorps);
1235 
1236 		/*  dimensions of data */
1237 		PSprintf("%d %d %d\n", w, h, bits);
1238 		/*  mapping matrix */
1239 		PSprintf("[%d 0 0 %d 0 %d]\n", w, -h, h);
1240 		PSprintf("rlecmapimage\n");
1241 
1242 	  	rleline = (unsigned char *) malloc(w * 2);
1243 	  	if (!rleline) {
1244 			fprintf(stderr,"failed to malloc space for rleline\n");
1245 			return;
1246 		}
1247 
1248 		for (i=0; i<h && err != EOF; i++) {
1249 			rlen = PSrle_encode(imgp, rleline, w);
1250 			imgp += w;
1251 			for (j=0; j<rlen && err != EOF; j++)
1252 				err=PShex(rleline[j], False);
1253 			err=PShex('\0', True);	/*  Flush the hex buffer */
1254 		}
1255 	  	free(rleline);
1256 	}
1257 
1258 	/*  stop using temporary dictionary */
1259 	PSprintf("end\n");
1260 	PSprintf("grestore\n");
1261 
1262 	/*  move currentpoint just right of image */
1263 	PSprintf("%d 0 R\n", w + extra);
1264 
1265 	/* forget about the macro's */
1266 #	undef Isgray
1267 #	undef Is_fg
1268 #	undef Is_bg
1269 }
1270 
1271 /*__________________________________________________________________________
1272  |
1273  | ParseTextToPSString - entry point for postscript output
1274  |
1275  | Parse all the formatted text elements from start to end
1276  | into an ascii text string, and return it.
1277  | Very like ParseTextToString() except the text is prettied up
1278  | into Postscript to show headers and the like.
1279  | space_width and lmargin tell us how many spaces
1280  | to indent lines.
1281  | Because this routine is only used to print whole documents,
1282  | some parameters are not needed at all!
1283  | Also it assumes that you are indeed printing the whole document, and
1284  | not just a selected portion of it. It therefore can assume that
1285  | only for the first page the initialization is needed, and only
1286  | the last page has the trailers. You cannot use ParseTextToPSString()
1287  | as you can use ParseTextToString() because of this initialization code.
1288  |
1289 */
1290 
ParseTextToPSString(hw,elist,startp,endp,start_pos,end_pos,space_width,lmargin,fontfamily)1291 String ParseTextToPSString(hw, elist, startp, endp, start_pos, end_pos,
1292 					   space_width, lmargin, fontfamily)
1293 	HTMLWidget	hw;
1294 	struct	ele_rec	*elist;
1295 	struct	ele_rec	*startp;
1296 	struct	ele_rec	*endp;
1297 	int	start_pos, end_pos;
1298 	int	space_width;
1299 	int	lmargin;
1300 	int	fontfamily;
1301 	{
1302 
1303 	/* the fontfamily parameter is new
1304 	 * The font is encoded as:
1305 	 *	0: times (default for now)
1306 	 *	1: helvetica
1307 	 *	2: new century schoolbook
1308 	 *	3: lucida
1309 	 */
1310 
1311 	int	xpos, ypos, epos;
1312 	int	height;
1313 	int	pagewidth;
1314 	int	line = -1;
1315 	struct	ele_rec	*eptr;
1316 	struct	ele_rec	*start;
1317 	struct	ele_rec	*end;
1318 	struct	ele_rec	*last;
1319 	struct	ele_rec	*tmpptr;
1320 
1321 	if (startp == NULL)
1322 		return(NULL);
1323 
1324 
1325 	/*
1326 	 * Get the foreground and background colors so we can check later
1327 	 * for black&white documents
1328 	 */
1329 	{
1330 		unsigned long fg_pixel, bg_pixel;
1331 
1332 
1333 		XtVaGetValues (hw->html.view,
1334 #ifdef MOTIF
1335 			       XtNforeground, &fg_pixel,
1336 #endif
1337 			       XtNbackground, &bg_pixel,
1338 			       NULL);
1339 #ifndef MOTIF
1340 		XtVaGetValues ((Widget)hw,
1341 			       XtNforeground, &fg_pixel,
1342 			       NULL);
1343 #endif
1344 		fg_color.pixel = fg_pixel;
1345 		bg_color.pixel = bg_pixel;
1346 		XQueryColor (XtDisplay (hw->html.view),
1347 			DefaultColormap (XtDisplay (hw->html.view),
1348 			DefaultScreen (XtDisplay (hw->html.view))),
1349 			&fg_color);
1350 		XQueryColor (XtDisplay (hw->html.view),
1351 			DefaultColormap (XtDisplay (hw->html.view),
1352 			DefaultScreen (XtDisplay (hw->html.view))),
1353 			&bg_color);
1354 	}
1355 
1356 	/*  this piece of code is needed if the user selects a portion
1357 	 *  of the document with the mouse.
1358 	 *  I think it will never be used, but I left it in anyway. F.
1359 	 */
1360 	if (SwapElements(startp, endp, start_pos, end_pos)) {
1361 		start = endp;
1362 		end = startp;
1363 		epos = start_pos;
1364 		start_pos = end_pos;
1365 		end_pos = epos;
1366 	} else {
1367 		start = startp;
1368 		end = endp;
1369 	}
1370 
1371 	/* Calculate the number of Postscript points per pixel of current
1372 	 * screen, and the height of the page in pixels (used in figuring
1373 	 * when we've hit the bottom of the page).
1374 	 */
1375 	Points_Pixel = 72.0 / GetDpi(hw);
1376 #ifdef OLD
1377 	pagewidth = hw->html.doc_width;
1378 #else /* gustaf fix */
1379         pagewidth = hw->html.view_width;  /* seems more reasonable */
1380 #endif /* gustaf fix */
1381 
1382 	/* reduce the scaling if the width used for formatting is greater
1383 	 * than 8 * 72 pixels (8 inch)
1384 	 * In theory, this is not what you want for A4 paper (only 8.27 inch
1385 	 * wide), but I guess that the hw->html.doc_width includes some
1386 	 * left and right margins, so it seems to work in practice.
1387 	 */
1388 	if (pagewidth > PAGE_WIDTH)
1389 		Points_Pixel = Points_Pixel * (float) PAGE_WIDTH / pagewidth;
1390 	Pixels_Page = (int) (PAGE_HEIGHT / Points_Pixel);
1391 
1392 
1393 	PSinit();
1394 	PSheader(hw->html.title, fontfamily);
1395 	PSnewpage();
1396 
1397 	last = start;
1398 	eptr = start;
1399 
1400 	while ((eptr != NULL) && (eptr != end)) {
1401 		/* Skip the special internal text added for multi-page
1402 		 * documents.
1403 		 */
1404 		if (eptr->internal == True) {
1405 			if (eptr->type == E_LINEFEED) {
1406 				PS_page_offset += eptr->line_height;
1407 			}
1408 			eptr = eptr->next;
1409 			continue;
1410 		}
1411 		/* check if this is a newline */
1412 		if (line != eptr->line_number) {
1413 			/* calculate max height */
1414 			height = 0;
1415 			line = eptr->line_number;
1416 			tmpptr = eptr;
1417 			while (tmpptr != NULL && tmpptr->line_number == line) {
1418 				if (tmpptr->line_height > height)
1419 					height = tmpptr->line_height;
1420 				tmpptr = tmpptr->next;
1421 			}
1422 			ypos = eptr->y - PS_page_offset ;
1423 			xpos = eptr->x - lmargin;
1424 			if (xpos < 0)
1425 				xpos = 0;
1426 
1427 			/* check if line fits completly on page */
1428 			if (ypos + height > PS_start_y + Pixels_Page) {
1429 				PS_start_y = ypos;
1430 				PSshowpage();
1431 				PSnewpage();
1432 			}
1433 			PSmoveto( xpos, ypos);
1434 		}
1435 
1436 		switch(eptr->type) {
1437 
1438 		  case E_TEXT: {
1439 			String tptr;
1440 			int ascent;
1441 
1442 			if (eptr == start)
1443 				tptr = (String)(eptr->edata + start_pos);
1444 			else
1445 				tptr = (String)eptr->edata;
1446 
1447 			PSfont(hw, eptr->font, fontfamily);	/* set font */
1448 			if (PS_fontascent == 0)
1449 				ascent = eptr->font->ascent;
1450 			else
1451 				ascent = PS_fontascent;
1452 			PSmove_offset(eptr->y_offset + ascent);
1453 		 	PStext(tptr, eptr->underline_number); /* insert text */
1454 			break;
1455 			}
1456 
1457 		  case E_BULLET: {
1458 			int width;
1459 			int offset;
1460 
1461 			PSfont(hw, eptr->font, fontfamily);
1462 			width = eptr->font->max_bounds.lbearing +
1463 					eptr->font->max_bounds.rbearing;
1464 			/* the next line is a hack to get a good position of the
1465 			 * bullet in most practical cases, otherwise the
1466 			 * bullet may appear just a bit too low (for large fonts)
1467 			 * What is does is to compare the lineheight with
1468 			 * the lineheight of the next element, to correct
1469 			 * for the possibly too large y_offset
1470 			 */
1471 			if (eptr->next != NULL && (eptr->next->type == E_TEXT
1472 					|| eptr->next->type == E_IMAGE))
1473 				offset = eptr->line_height -
1474 					eptr->next->line_height +
1475 					eptr->y_offset +
1476 					eptr->next->font->ascent;
1477 			else
1478 				offset = eptr->y_offset + eptr->font->ascent;
1479 
1480 		  	PSmove_offset(offset - width/4);
1481 		  	PSbullet(eptr->indent_level, eptr->line_height);
1482 			break;
1483 		  	}
1484 
1485 		  case E_IMAGE: {
1486 
1487 			PSmove_offset(eptr->y_offset);
1488 		 	PSimage(eptr->pic_data ,(eptr->anchorHRef != NULL));
1489 		  	break;
1490 	  	  	}
1491 
1492 		  case E_LINEFEED: {
1493 			break;
1494 			}
1495 		  case E_HRULE: {
1496 			PSmove_offset(eptr->y_offset);
1497 			PShrule(hw->html.doc_width);
1498 			break;
1499 			}
1500 		}
1501 		last = eptr;
1502 		eptr = eptr->next;
1503 	}
1504 
1505 	PSshowpage();
1506 	PStrailer();
1507 
1508 	return( PS_string);
1509 }
1510 
1511