1 #ifndef production
2 static char rcsId[]="$Header$";
3 #endif
4 /*****
5 * psoutput.c : XmHTML Postscript output routines
6 *
7 * This file Version	$Revision$
8 *
9 * Creation date:		Tue Nov 17 13:49:18 CET 1998
10 * Last modification: 	$Date$
11 * By:					$Author$
12 * Current State:		$State$
13 *
14 * Authors:				Scott Gregory, gregory@sccoast.net
15 *						Koen D'Hondt, ripley@xs4all.nl
16 *						Ameet A. Raval, aar@gfdl.gov
17 *						Frans van Hoesel, hoesel@chem.rug.nl
18 *
19 * Gratefully donated to XmHTML by Scott Gregory.
20 *
21 * Copyright (C) 1994-1998 by Ripley Software Development
22 * All Rights Reserved
23 *
24 * Portions Copyright by Ameet A. Raval & Frans van Hoesel.
25 * Portions Copyright by Robert C. Tatar & Craig A. McGowan.
26 * Portions Copyright by John Bradley.
27 *
28 * This file is part of the XmHTML Widget Library
29 *
30 * This library is free software; you can redistribute it and/or
31 * modify it under the terms of the GNU Library General Public
32 * License as published by the Free Software Foundation; either
33 * version 2 of the License, or (at your option) any later version.
34 *
35 * This library is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
38 * Library General Public License for more details.
39 *
40 * You should have received a copy of the GNU Library General Public
41 * License along with this library; if not, write to the Free
42 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
43 *
44 * Scott's Note:
45 *
46 * A hugely collective effort!
47 *
48 * The structure and portions of PS code uncerimoniously horked from code
49 * given to Mosaic by Ameet A. Raval & Frans van Hoesel. Their header reads:
50 *
51 * Institution: for Ameet A. Raval:
52 *					  Geophysical Fluid Dynamics Laboratory,
53 *					  National Oceanic and Atmospheric Administration,
54 *					  U.S. Department of Commerce
55 *					  P.O. Box 308
56 *					  Princeton, NJ 08542
57 *			  for Frans van Hoesel:
58 *					  Xtreme graphics software
59 *					  Herepoortenmolendrift 36
60 *					  9711 DH  Groningen
61 *					  The Netherlands
62 *
63 * Copyright
64 * This work is the product of the United States Government, and is precluded
65 * from copyright protection.  It is hereby released into the public domain.
66 *
67 * WE MAKE NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
68 * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED
69 * WARRANTY. WE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE
70 * USERS OF THIS SOFTWARE.
71 *
72 * Pieces of code are taken from xvps by kind permission of John Bradley.
73 *
74 * Extensive hacks from xwd2ps by Robert C. Tatar and Craig A. McGowan.
75 *
76 *****/
77 /*****
78 * ChangeLog
79 * $Log$
80 * Revision 1.2  2012/03/01 17:56:31  ziad
81 * Cput
82 *
83 * Revision 1.1  2011/06/30 16:10:38  rwcox
84 * Cadd
85 *
86 *****/
87 #ifdef HAVE_CONFIG_H
88 #include "config.h"
89 #endif
90 
91 #ifdef WITH_DMALLOC
92 #include <dmalloc.h>
93 #endif
94 
95 #include <stdlib.h>
96 #ifdef __STDC__
97 #include <stdarg.h>
98 #else
99 #include <varargs.h>
100 #endif
101 #include <stdio.h>		/* must follow stdarg or varargs on LynxOs */
102 #include <stdlib.h>
103 #include <string.h>
104 #include <ctype.h>
105 #include <time.h>
106 #include <math.h>
107 
108 /* Our private header files */
109 #include "toolkit.h"
110 #include XmHTMLPrivateHeader
111 #include "XmHTMLfuncs.h"
112 
113 /*** External Function Prototype Declarations ***/
114 
115 /*** Public Variable Declarations ***/
116 
117 /*** Private Datatype Declarations ****/
118 /* for regular-font, bold-font, italic-font, fixed-font */
119 typedef enum {
120 	RF, BF, IF, FR, FB, FI
121 }PSFontstyle;
122 
123 /* structure for holding all footnotes on a page (anchor data) */
124 typedef struct{
125 	int nmalloc;
126 	int count;
127 	char **items;
128 }PSFootnote;
129 
130 typedef struct _PSDisplay {
131 	Display *dpy;			/* must be the first field!				*/
132 
133 	XmHTMLWidget html;		/* the XmHTML widget id					*/
134 	Byte options;			/* options to XmHTMLFormatPostscript()	*/
135 
136 	/* Output area, Screen definition */
137 	XmHTMLPaperSize screen;	/* defined output area					*/
138 	float Points_Pixel;		/* Postscript Pointer Per Pixel			*/
139 	int Pixels_Page;		/* height of page in pixels				*/
140 	int Pixels_This_Page;	/* useable height of page in pixels		*/
141 
142 	/* Output area, positioning */
143 	int start_y;			/* current vertical pixel position		*/
144 	int offset;
145 	int curr_page;			/* current page counter					*/
146 
147 	/* Font data */
148 	XmHTMLfont *font;		/* latest font							*/
149 	char font_style[3];		/* PS font macro name, "RF", etc.		*/
150 	int font_size;			/* size of the font (ascent)			*/
151 
152 	/* Output buffer */
153 	char *string;			/* Output buffer						*/
154 	int size;				/* size of output buffer				*/
155 	int len;				/* used size of output buffer			*/
156 
157 	/* Footnotes */
158 	PSFootnote footnotes;
159 
160 	/* Hexadecimal conversion */
161 	Byte hexline[80];		/* accumulation buffer					*/
162 	int hexi;				/* current position						*/
163 
164 	/* document fore & background colors */
165 	unsigned short fg[3];	/* foreground color components			*/
166 	unsigned short bg[3];	/* background color components			*/
167 
168 	/* Following fields are unused. Might be used later	*/
169 	int page_offset;		/* unused								*/
170 	PSFontstyle oldfn;		/* unused								*/
171 	int fontascent;			/* unused								*/
172 	int oldfs;				/* unused								*/
173 }PSDisplay;
174 
175 /*** Private Function Prototype Declarations ****/
176 #define max(a,b)		(a>b ? a : b)
177 #define min(a,b)		(a<b ? a : b)
178 
179 /* MONO returns total intensity of r,g,b components .33R+ .5G+ .17B */
180 #define MONO(rd,gn,bl) (((rd)*11 + (gn)*16 + (bl)*5) >> 13)
181 
182 /* PSconst_out outputs to the postscript buffer an array of constant strings */
183 #define PSconst_out(dpy, txt) do{ \
184 	int i, n = (sizeof txt)/(sizeof txt[0]); \
185 	for(i = 0; i < n; i++) PSprintf(dpy, "%s\n", txt[i]); \
186 }while(0)
187 
188 static float GetDpi(XmHTMLWidget hw);
189 static void fnDestroy(PSFootnote footnote);
190 static int fnAdd(PSFootnote footnote, char *buf);
191 static int PSprintf(PSDisplay *dpy, char *format, ...);
192 static void PSfootnotes(PSDisplay *dpy);
193 static void PSfont(PSDisplay *dpy, XmHTMLfont *font, Boolean flush);
194 static void PSwidgetsOnPage(PSDisplay *dpy);
195 static void PSshowpage(PSDisplay *dpy);
196 static void PSnewpage(PSDisplay *dpy);
197 static void PSinit_latin1(PSDisplay *dpy);
198 static void PSinit(PSDisplay *dpy);
199 static void PStrailer(PSDisplay *dpy);
200 static void PSfootnotes(PSDisplay *dpy);
201 static void PStext(PSDisplay *dpy, String t, Boolean underline);
202 static void PScheckPage(PSDisplay *dpy, int x, int y);
203 static void PSshowpage(PSDisplay *dpy);
204 static void PSmoveto(PSDisplay *dpy, int x, int y);
205 static void PSmove_offset(PSDisplay *dpy, int offset);
206 static int PSencode(unsigned char *data, unsigned char *rle, int size);
207 static void PSfootnotes(PSDisplay *dpy);
208 static int PShex(PSDisplay *dpy, Byte val, int flush);
209 static void PSColorImage(PSDisplay *dpy);
210 static void PScolormap(PSDisplay *dpy, Boolean color, int nc,
211 	   Dimension *rmap, Dimension *gmap, Dimension *bmap);
212 static void PSrle_cmapimage(PSDisplay *dpy, int color);
213 static int PSImageBW(PSDisplay *dpy, Byte *data, int w, int h, Boolean inverse);
214 static void PSImage(PSDisplay *dpy, XmHTMLImage *image, int x, int y);
215 static void PSColorImage(PSDisplay *dpy);
216 
217 /* Postscript TKA functions */
218 static ToolkitAbstraction *_CreatePostscriptTka(XmHTMLWidget html);
219 
220 static int pstkSetForeground(Display *disp, XGC gc, unsigned long foreground);
221 static int pstkSetFont(Display *disp, XGC gc, XmHTMLfont *font);
222 static int pstkDrawString(Display *disp, DRAWABLE win,
223 	struct _XmHTMLFont *font, XGC gc, int x, int y, char *string, int length);
224 static void pstkDrawAnchorData(Display *disp, WINDOW win, XGC gc, int x,
225 	int y, XmHTMLObjectTableElement data);
226 static int pstkDrawLine(Display *disp, WINDOW win, XGC gc, int x1, int y1,
227 	int x2, int y2);
228 static int pstkDrawLines(Display *disp, DRAWABLE win, XGC gc, XPoint *points,
229 	int num_points, int mode);
230 static int pstkDrawRectangle(Display *disp, WINDOW win, XGC gc, int x, int y,
231 	unsigned int width, unsigned int height);
232 static int pstkFillRectangle(Display *disp, WINDOW win, XGC gc, int x, int y,
233 	unsigned int width, unsigned int height);
234 static int pstkDrawArc(Display *disp, WINDOW win, XGC gc, int x, int y,
235 	unsigned int width, unsigned int height, int angle1, int angle2);
236 static int pstkFillArc(Display *disp, WINDOW win, XGC gc, int x, int y,
237 	unsigned int width, unsigned int height, int angle1, int angle2);
238 static void pstkDrawImage(XmHTMLWidget html, XmHTMLImage *image, XGC gc,
239 	int src_x, int src_y, unsigned int width, unsigned int height,
240 	int dest_x, int dest_y);
241 static void pstkDrawShadows(Display *disp, DRAWABLE drawable,
242 	XGC top_shadow_GC, XGC bottom_shadow_GC,
243 #if NeedWidePrototypes
244 	int x, int y, int width, int height, int shadow_thickness,
245 #else
246 	Position x, Position y, Dimension width, Dimension height,
247 	Dimension shadow_thickness,
248 #endif
249 	unsigned int shadow_type);
250 
251 /* empty wrappers */
252 
253 
254 /*** Private Variable Declarations ***/
255 
256 #define CR '\015'
257 #define LF '\012'
258 
259 #define TOP_MARGIN		dpy->screen.top_margin
260 #define BOT_MARGIN		dpy->screen.bottom_margin
261 #define LEFT_MARGIN		dpy->screen.left_margin
262 #define PAGE_HEIGHT		dpy->screen.height
263 #define PAGE_WIDTH		dpy->screen.width
264 
265 #define F_FULLCOLOR		0
266 #define F_GREYSCALE		1
267 #define F_BWDITHER		2
268 #define F_REDUCED		3
269 
270 #define L_PAREN			'('
271 #define R_PAREN			')'
272 #define B_SLASH			'\\'
273 #define MAX_ASCII		'\177'
274 
275 #define FOOTNOTE_ROW_HEIGHT	8		/* helvetica 8 */
276 
277 /*****
278 * Name:			GetDpi
279 * Return Type: 	float
280 * Description: 	returns dots-per-inch of the screen. Calculates the pixel
281 *				density in dots per inch on the current Widget screen.
282 * In:
283 *	html:		XmHTMLWidget id
284 * Returns:
285 *	screen density.
286 *****/
287 static float
GetDpi(XmHTMLWidget html)288 GetDpi(XmHTMLWidget html)
289 {
290 	ToolkitAbstraction *tka = HTML_ATTR(tka);
291 	float dpi;
292 
293 	dpi = 25.4 * tka->width/tka->widthMM;
294 
295 	/* no earthly monitor does this */
296 	if(dpi < 1.0 || dpi > 10000.0)
297 		dpi = 72.0;
298 	return dpi;
299 }
300 
301 /**********
302 ***** Footnote functions. If requested, XmHTML prints the referring hyperlink
303 ***** of each anchor as a footnote.
304 **********/
305 
306 /*****
307 * Name:			fnDestroy
308 * Return Type: 	void
309 * Description: 	destroy all footnote data for this page.
310 * In:
311 *	footnote:	array of footnotes to be destroyed.
312 * Returns:
313 *	nothing.
314 *****/
315 static void
fnDestroy(PSFootnote footnote)316 fnDestroy(PSFootnote footnote)
317 {
318 	int i;
319 
320 	if(footnote.items)
321 	{
322 		for(i = 0; footnote.items[i] != NULL; ++i)
323 			free(footnote.items[i]);
324 		free(footnote.items);
325 		footnote.items = NULL;
326 	}
327 	footnote.count = footnote.nmalloc = 0;
328 }
329 
330 /*****
331 * Name: 		fnAdd
332 * Return Type: 	int
333 * Description:	add a footnote to the current page and return its number
334 * In:
335 *	buf:		footnote value
336 * Returns:
337 *	footnote number.
338 *****/
339 static int
fnAdd(PSFootnote footnote,char * buf)340 fnAdd(PSFootnote footnote, char *buf)
341 {
342 	int fnum;
343 
344 	if(!footnote.items)
345 	{
346 		/* initialize current page footnotes buffer */
347 		footnote.nmalloc = 10;
348 		footnote.count = 0;
349 		footnote.items = (char **)malloc(sizeof(char *)*footnote.nmalloc);
350 		memset(footnote.items, 0, sizeof(char *)*footnote.nmalloc);
351 	}
352 	else
353 	{
354 		/*****
355 		* We've already got a buffer, see if it's large enough and extend
356 		* if required.
357 		*****/
358 		if(footnote.count >= (footnote.nmalloc-1))
359 		{
360 			footnote.nmalloc += 10;
361 			footnote.items = (char **)realloc(footnote.items,
362 								sizeof(char *)*footnote.nmalloc);
363 		}
364 	}
365 
366 	/* Check if this footnote has already been added */
367 	for(fnum = 0; fnum < footnote.count; ++fnum)
368 		if(strcmp(footnote.items[fnum], buf) == 0)
369 			return(fnum);
370 
371 	/* this is a new footnote, store it */
372 	fnum = footnote.count;
373 	footnote.count++;
374 
375 	footnote.items[fnum] = strdup(buf);
376 	footnote.items[fnum+1] = NULL;
377 
378 	return(fnum);
379 }
380 
381 /*****
382 * Name: 		PSprintf
383 * Return Type: 	int
384 * Description: 	sprintf but can dynamically extend the destination buffer.
385 * In:
386 *
387 * Returns:
388 *
389 *****/
390 static int
391 #ifdef __STDC__
PSprintf(PSDisplay * dpy,char * format,...)392 PSprintf(PSDisplay *dpy, char *format, ...)
393 #else
394 PSprintf(dpy, format, va_alist)
395 	PSDisplay *dpy;
396 	String format;
397 	va_dcl
398 #endif
399 {
400 	int len;
401 	char *s;
402 #ifdef __STDC__
403 	va_list args;
404 #else
405 	va_alist args
406 #endif
407 
408 	if(dpy->size - dpy->len < 1024)
409 	{
410 		dpy->size += 1024;
411 		s = (char*)realloc(dpy->string, dpy->size);
412 		dpy->string = s;
413 	}
414 #ifdef __STDC__
415 	va_start(args, format);
416 #else
417 	va_start(args);
418 #endif
419 	len = vsprintf(dpy->string + dpy->len, format, args);
420 
421 	va_end(args);
422 
423 	/* update size of destination buffer */
424 	if(len != 0)
425 		dpy->len += strlen(dpy->string + dpy->len);
426 
427 	/* all done */
428 	return(len);
429 }
430 
431 /*****
432 * Name: 		PSfootnotes
433 * Return Type: 	void
434 * Description: Display the footer and all footnotes collected during this page
435 * In:
436 *	dpy:		current postscript output area
437 * Returns:
438 *	nothing.
439 *****/
440 static void
PSfootnotes(PSDisplay * dpy)441 PSfootnotes(PSDisplay *dpy)
442 {
443 	int x, y, i;
444 
445 	if(!(dpy->options & XmHTMLTEXT_ADDFOOTER))
446 		return;
447 
448 	x = dpy->screen.left_margin;
449 	y = dpy->Pixels_This_Page;
450 
451 	PSprintf(dpy, "%% PSfootnotes\n");
452 	PSprintf(dpy, "0 setgray\n");
453 
454 	PSprintf(dpy, "%d -%d M %d 0 RL stroke\n", x, y,
455 		dpy->screen.width - dpy->screen.left_margin -
456 		dpy->screen.right_margin);
457 
458 	/* set font */
459 	PSprintf(dpy, "\n/helvetica-bold %d SF\n", FOOTNOTE_ROW_HEIGHT);
460 
461 	/* show page number */
462 	PSprintf(dpy, "newpath %d -%d M 0 -%d RL ( Page %d ) stringwidth "
463 		"pop neg 0 RL 0 %d RL closepath stroke\n",
464 		dpy->screen.width - dpy->screen.right_margin, y,
465 		FOOTNOTE_ROW_HEIGHT + 2, dpy->curr_page, FOOTNOTE_ROW_HEIGHT+2);
466 	PSprintf(dpy, "%d -%d M ( Page %d ) stringwidth pop neg -%d R "
467 		"(Page %d ) S\n",
468 		dpy->screen.width - dpy->screen.right_margin, y,
469 		dpy->curr_page, FOOTNOTE_ROW_HEIGHT, dpy->curr_page);
470 
471 	/* print the footnotes if requested */
472 	if(!(dpy->options & XmHTMLTEXT_ANCHORFOOTNOTES) ||
473 		dpy->footnotes.count <= 0)
474 	{
475 		fnDestroy(dpy->footnotes);
476 		return;
477 	}
478 	for(i = 0; dpy->footnotes.items[i] != NULL; ++i)
479 	{
480 		y += 2 + FOOTNOTE_ROW_HEIGHT;
481 
482 		/* set footnote number font */
483 		PSprintf(dpy, "/helvetica-bold %d SF\n", FOOTNOTE_ROW_HEIGHT);
484 
485 		/* footnote number */
486 		PSprintf(dpy, "%d -%d M (%d. )S\n", x, y, i+1);
487 
488 		/* set footnote content font */
489 		PSprintf(dpy, "/helvetica %d SF\n", FOOTNOTE_ROW_HEIGHT);
490 
491 		/* footnote contents */
492 		PSprintf(dpy, "(%s)S\n", dpy->footnotes.items[i]);
493 	}
494 
495 	/* all done */
496 	fnDestroy(dpy->footnotes);
497 }
498 
499 /*****
500 * Name:			PSfont
501 * Return Type: 	void
502 * Description: 	change local font.
503 * In:
504 *	dpy:		current postscript output area;
505 *	font:		master font;
506 *	flush:		flush font settings at end of page.
507 * Returns:
508 *	nothing.
509 * Note:
510 *	This needs some work. Ultimatly, Postscript output should use the
511 *	specified document fonts instead of referring to a set of default
512 *	fonts. This will require a lot of work as it requires XmHTML to include
513 *	font definitions for each font that is used in this document.
514 *	Ideally, the Postscript ToolkitAbstraction should have an associated
515 *	font cache instead of basing font selection on the fonts residing
516 *	in the X server.
517 *****/
518 static void
PSfont(PSDisplay * dpy,XmHTMLfont * font,Boolean flush)519 PSfont(PSDisplay *dpy, XmHTMLfont *font, Boolean flush)
520 {
521 	static XmHTMLfont *last_font=NULL;
522 	char fstyle[3];
523 	static char fstr[25]="\0";
524 	int i;
525 
526 	/* no font change */
527 	if(font == last_font && font != NULL)
528 		return;
529 
530 	/* Forced font flush or no font provided */
531 	if(flush || font == NULL)
532 	{
533 		/* First time entry, set initial size */
534 		if(last_font == NULL || fstr[0]=='\0')
535 		{
536 			PSprintf(dpy, "RF 14 SF\n");
537 			return;
538 		}
539 		else
540 		{
541 			/* use current font */
542 			PSprintf(dpy, "%s\n", fstr);
543 			return;
544 		}
545 	}
546 
547 	/* initialize font style array */
548 	memset(fstyle, 0, sizeof(fstyle));
549 
550 	if(font->style & FONT_SCALABLE || strstr(font->font_family, "times"))
551 	{
552 		/* scalable font */
553 		fstyle[1]='F';
554 		i = 0;
555 	}
556 	else
557 	{
558 		/* fixed font */
559 		fstyle[0]='F';
560 		i = 1;
561 	}
562 
563 	/* set font styles */
564 	if(font->style & FONT_BOLD)
565 		fstyle[i]='B';
566 	else if(font->style & FONT_MEDIUM)
567 		fstyle[i] = 'R';
568 	else if(font->style & FONT_ITALIC)
569 		fstyle[i] = 'I';
570 	else
571 		fstyle[i] = 'R';
572 
573 	/* comment stating original fontname */
574 	PSprintf(dpy, "%%FontStyle=0x%x %s, size = %i points\n", (int)font->style,
575 		font->font_name, font->ptsize);
576 
577 	/* store & set size */
578 	sprintf(fstr, "%s %d SF", fstyle, font->ptsize);
579 	PSprintf(dpy, "%s\n", fstr);
580 
581 	/* store font data */
582 	dpy->font = font;
583 	strcpy(dpy->font_style, fstyle);
584 	dpy->font_size = font->m_ascent;
585 
586 	if(font)
587 		last_font = font;
588 }
589 
590 /*****
591 * Name:			PSwidgetsOnPage
592 * Return Type: 	void
593 * Description: 	render all HTML FORM elements residing on the current page.
594 * In:
595 *	dpy:		current Postscript output area
596 * Returns:
597 *	nothing.
598 * Note:
599 *	Called at end of a page to draw the form widgets.
600 *
601 *	Koen,
602 *		for this to work correctly, each widget would have had to have
603 *	been mapped at one time to get its formatted position. Then in here
604 *	we would have to map those that aren't, wait on the expose, then
605 *	grab the image and unmap it.
606 *
607 *	Kind of academic until we can ensure all form widgets have been mapped.
608 *
609 *	Scott,
610 *		Since we can't rely on each widget being mapped (it's very well
611 *		possible the XmHTMLWidget will get mapped at all: think about a
612 *		HTML to Postscript converter), we just draw a grayed rectangle.
613 *		Later on we could decide to do it another way, say map the
614 *		widget to an offscreen position, take a snapshot of that and render
615 *		the resulting image. Come to think of it, that would be the way
616 *		to do it.
617 *****/
618 static void
PSwidgetsOnPage(PSDisplay * dpy)619 PSwidgetsOnPage(PSDisplay *dpy)
620 {
621 	XmHTMLWidget html = dpy->html;
622 #if 0
623 	ToolkitAbstraction *tka = HTML_ATTR(tka);
624 #endif
625 	XmHTMLFormData *form;
626 	XmHTMLForm *entry;
627 	int xs, ys;
628 
629 	/* XmHTMLExtObj *u; ??Should we print these also?? Not implemented yet */
630 
631 	if((form = HTML_ATTR(form_data)) == NULL)
632 		return;
633 
634 	/* walk all defined forms */
635 	for(; form != NULL; form = form->next)
636 	{
637 		/* check all form components */
638 		for(entry = form->components; entry != NULL; entry = entry->next)
639 		{
640 			/* only render visible form members */
641 			if(entry->w)
642 			{
643 				/*****
644 				* get widget position on current page
645 				* data->x and data->y contain the computed widget's position
646 				* relative to the start of the document, so to compute
647 				* the widget position relative to the current page we must
648 				* substract the region that's already behind us.
649 				*****/
650 				xs = entry->data->x - HTML_ATTR(scroll_x);
651 				ys = entry->data->y - HTML_ATTR(scroll_y);
652 
653 				/* check if this widget is in the viewable area */
654 				if(xs + entry->width > 0 && xs < HTML_ATTR(work_width) &&
655 					ys + entry->height > 0 && ys < HTML_ATTR(work_height))
656 				{
657 					PSprintf(dpy, "%% PSwidgetsOnPage %s (%dx%d+%d+%d)\n",
658 						TkaWidgetName(entry->w), entry->data->width,
659 						entry->data->height, xs, ys);
660 
661 					/* render a grayed rectangle */
662 #if 1
663 					/* Positioning might be wrong... */
664 
665 					PSprintf(dpy, "%d %d translate", xs,
666 						-(ys - dpy->start_y) - entry->data->height);
667 					PSprintf(dpy, "gsave currentpoint %d sub translate ",
668 						entry->data->height);
669 					PSprintf(dpy, "%d %d scale\n", entry->data->width,
670 						entry->data->height);
671 					PSprintf(dpy, "SQ 0.9 setgray fill\ngrestore\n");
672 #else
673 					if(tka->IsRealized(entry->w) && entry->mapped)
674 						ImageToPs(dpy, XtDisplay((Widget)html),
675 							XtWindow(c->w), 0, 0, c->width, c->height,
676 							xs, -(ys - dpy->start_y), 1);
677 #endif
678 				}
679 			}
680 		}
681 	}
682 }
683 
684 /*****
685 * Name:			PSshowpage
686 * Return Type: 	end of page function. Show current page and restore
687 *				any changes to the printer state.
688 * Description:
689 * In:
690 *	dpy:		current postscript output area.
691 * Returns:
692 *	nothing.
693 *****/
694 static void
PSshowpage(PSDisplay * dpy)695 PSshowpage(PSDisplay *dpy)
696 {
697 	PSwidgetsOnPage(dpy);
698 
699 	if(dpy->curr_page > 0)
700 		PSfootnotes(dpy);
701 
702 	dpy->Pixels_This_Page = dpy->Pixels_Page;
703 
704 	PSprintf(dpy, "showpage restore\n");
705 }
706 
707 /*****
708 * Name: 		PSnewpage
709 * Return Type: 	begin a fresh page. Increments page count and handles
710 *				structured comment conventions.
711 * Description:
712 * In:
713 *	dpy:		current postscript output area.
714 * Returns:
715 *	nothing.
716 *****/
717 static void
PSnewpage(PSDisplay * dpy)718 PSnewpage(PSDisplay *dpy)
719 {
720 	dpy->curr_page++;
721 
722 	/*****
723 	* The PostScript reference Manual states that the Page: Tag
724 	* should have a label and a ordinal; otherwise programs like
725 	* psutils fail -- gustaf
726 	*****/
727 	PSprintf(dpy, "%%%%Page: %d %d\n", dpy->curr_page, dpy->curr_page);
728 	PSprintf(dpy, "save\nNP\n");
729 	PSfont(dpy, (Font)0, True); /* force re-flush of last font used */
730 
731 	/* reserve footnote space (if requested) */
732 	if(dpy->options & XmHTMLTEXT_ADDFOOTER)
733 		dpy->Pixels_This_Page -= FOOTNOTE_ROW_HEIGHT;
734 }
735 
736 /*****
737 * Name:			PSinit_latin1
738 * Return Type: 	void
739 * Description: 	handle ISO Latin1 encoding.
740 * In:
741 *	dpy:		current postscript output area
742 * Returns:
743 *	nothing.
744 * Note:
745 *	This table contains the names of all characters in the ISO Latin1 font
746 *	encoding (191 defined characters in total). The first valid character
747 *	is space.
748 *	This table comes from the Idraw program (from Stanford's InterViews
749 *	package), courtesy of Steinar Kjaernsrd, steinar@ifi.uio.no
750 *****/
751 static void
PSinit_latin1(PSDisplay * dpy)752 PSinit_latin1(PSDisplay *dpy)
753 {
754 	static char *txt[] = {
755 		"/reencodeISO {",
756 		"dup dup findfont dup length dict begin",
757 		"{ 1 index /FID ne { def }{ pop pop } ifelse } forall",
758 		"/Encoding ISOLatin1Encoding D",
759 		"currentdict end definefont",
760 		"} D",
761 		"/ISOLatin1Encoding [",
762 		"/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef",
763 		"/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef",
764 		"/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef",
765 		"/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef",
766 		"/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright",
767 		"/parenleft/parenright/asterisk/plus/comma/minus/period/slash",
768 		"/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon",
769 		"/less/equal/greater/question/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N",
770 		"/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash/bracketright",
771 		"/asciicircum/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m",
772 		"/n/o/p/q/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/asciitilde",
773 		"/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef",
774 		"/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef",
775 		"/.notdef/dotlessi/grave/acute/circumflex/tilde/macron/breve",
776 		"/dotaccent/dieresis/.notdef/ring/cedilla/.notdef/hungarumlaut",
777 		"/ogonek/caron/space/exclamdown/cent/sterling/currency/yen/brokenbar",
778 		"/section/dieresis/copyright/ordfeminine/guillemotleft/logicalnot",
779 		"/hyphen/registered/macron/degree/plusminus/twosuperior/threesuperior",
780 		"/acute/mu/paragraph/periodcentered/cedilla/onesuperior/ordmasculine",
781 		"/guillemotright/onequarter/onehalf/threequarters/questiondown",
782 		"/Agrave/Aacute/Acircumflex/Atilde/Adieresis/Aring/AE/Ccedilla",
783 		"/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute/Icircumflex",
784 		"/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis",
785 		"/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute",
786 		"/Thorn/germandbls/agrave/aacute/acircumflex/atilde/adieresis",
787 		"/aring/ae/ccedilla/egrave/eacute/ecircumflex/edieresis/igrave",
788 		"/iacute/icircumflex/idieresis/eth/ntilde/ograve/oacute/ocircumflex",
789 		"/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex/udieresis",
790 		"/yacute/thorn/ydieresis",
791 		"] D",
792 		"[RF BF IF FR FB FI] {reencodeISO D} forall"
793 	};
794 
795 	PSconst_out(dpy, txt);
796 }
797 
798 /*****
799 * Name:			PSinit
800 * Return Type: 	void
801 * Description: 	initialize postscript output per HTML document.
802 * In:
803 *	dpy:		current postscript output area
804 * Returns:
805 *	nothing.
806 *****/
807 static void
PSinit(PSDisplay * dpy)808 PSinit(PSDisplay *dpy)
809 {
810 	dpy->offset = dpy->hexi = dpy->page_offset = 0;
811 	dpy->start_y = 0;
812 
813 	/* Initialize output buffer */
814 	dpy->size = 1024;
815 	dpy->string = (char*)calloc(1, sizeof(char)*dpy->size);
816 	dpy->len = 0;
817 
818 	dpy->oldfs = 0;
819 	dpy->oldfn = RF;
820 	dpy->curr_page = 0 ;
821 }
822 
823 /*****
824 * Name: 		PSheader
825 * Return Type: 	void
826 * Description: 	initialize postscript output and prints the postscript prolog.
827 * In:
828 *	dpy:		current postscript output area
829 *	title:		document title (if any)
830 *	font:		document basefont.
831 * Returns:
832 *	nothing.
833 *****/
834 static void
PSheader(PSDisplay * dpy,char * title,int font)835 PSheader(PSDisplay *dpy, char *title, int font)
836 {
837 	time_t currtime = time(NULL);
838 
839 	static char *fontname[] = {
840 		/* in order: regular, bold, italic */
841 		"Times-Roman", "Times-Bold", "Times-Italic",
842 		"Helvetica", "Helvetica-Bold", "Helvetica-Oblique",
843 		"NewCenturySchlbk-Roman", "NewCenturySchlbk-Bold",
844 				"NewCenturySchlbk-Italic",
845 		/* this is a nasty trick, I have put Times in place of
846 		 * Lucida, because most printers don't have Lucida font
847 		 */
848 		"Times-Roman", "Times-Bold", "Times-Italic"
849 		/* "Lucida", "Lucida-Bold", "Lucida-Italic" */
850 	};
851 
852 	static char *txt[] = {
853 		"%%EndComments",
854 		"save",
855 		"/D {def} def /E {exch} D",
856 		"/M {moveto} D",
857 		"/S {show} D",
858 		"/R {rmoveto} D",
859 		"/L {lineto} D",
860 		"/RL {rlineto} D",
861 		"/SQ {newpath 0 0 M 0 1 L 1 1 L 1 0 L closepath} D",
862 		"/U {gsave currentpoint currentfont /FontInfo get /UnderlinePosition get",
863 		" 0 E currentfont /FontMatrix get dtransform E pop add newpath moveto",
864 		" dup stringwidth rlineto stroke grestore S } D",
865 		"/B {/r E D gsave -13 0  R currentpoint ",
866 		"  newpath r 0 360 arc closepath fill grestore } D",
867 		"/OB {/r E D gsave -13 0  R currentpoint ",
868 		"  newpath r 0 360 arc closepath stroke grestore } D",
869 		"/NP {xmargin topmargin translate scalfac dup scale newpath } D",
870 		"/HR {/l E D gsave l 0 RL  stroke grestore } D",
871 		"/SF {E findfont E scalefont setfont } D",
872 		"/FR {/Courier } D",
873 		"/FB {/Courier-Bold } D",
874 		"/FI {/Courier-Oblique } D"
875 	};
876 
877 	/* Postscript level */
878 	PSprintf(dpy, "%%!PS-Adobe-1.0\n");
879 	PSprintf(dpy,"%%%%Creator: %s\n", XmHTMLVERSION_STRING);
880 
881 	/* set document title (if any) */
882 	if(title != NULL && *title != '\0')
883 	{
884 		char *tmp;
885 		for(tmp = title; *tmp; tmp++)
886 		{
887 			/* convert newlines to spaces */
888 			if(*tmp == CR || *tmp == LF)
889 				*tmp = ' ';
890 		}
891 		PSprintf(dpy, "%%%%Title: %s\n", title);
892 	}
893 	else
894 		PSprintf(dpy, "%%%%Title: (no title)\n");
895 
896 	/* print remainder of header */
897 
898 	/* creation data */
899 	PSprintf(dpy, "%%%%CreationData: %s", ctime(&currtime));
900 
901 	/* tell no of pages is at the end of the document */
902 	PSprintf(dpy, "%%%%Pages: (atend)\n");
903 	PSprintf(dpy, "%%%%PageOrder: Ascend\n");
904 
905 	/* set document fonts */
906 	PSprintf(dpy, "%%%%DocumentFonts: %s %s %s Courier Courier-Bold "
907 		"Courier-Oblique\n", fontname[font*3], fontname[font*3+1],
908 		fontname[font*3+2]);
909 
910 	/* print remainder of prolog */
911 	PSconst_out(dpy, txt);
912 
913 	PSprintf(dpy, "/RF {/%s} D\n", fontname[font*3]);
914 	PSprintf(dpy, "/BF {/%s} D\n", fontname[font*3+1]);
915 	PSprintf(dpy, "/IF {/%s} D\n", fontname[font*3+2]);
916 
917 	PSinit_latin1(dpy);
918 
919 	PSprintf(dpy, "/xmargin %d D\n", (int)LEFT_MARGIN);
920 	PSprintf(dpy, "/topmargin %d D\n", (int)TOP_MARGIN);
921 	PSprintf(dpy, "/scalfac %.5f D\n", dpy->Points_Pixel);
922 	PSprintf(dpy, "%%%%EndProlog\n");
923 }
924 
925 /*****
926 * Name:			PStrailer
927 * Return Type: 	void
928 * Description: 	write document trailer.
929 * In:
930 *	dpy:		current postscript output area
931 * Returns:
932 *	nothing.
933 *****/
934 static void
PStrailer(PSDisplay * dpy)935 PStrailer(PSDisplay *dpy)
936 {
937 	PSprintf(dpy, "%%%%Trailer\n");
938 	PSprintf(dpy, "restore\n");
939 	PSprintf(dpy, "%%%%Pages: %d\n", dpy->curr_page);
940 }
941 
942 /*****
943 * Name: 		PStext
944 * Return Type: 	void
945 * Description: 	output text, renders the given text, protects special
946 *				characters and underlines words if required.
947 * In:
948 *	dpy:		current postscript output area
949 *	t:			text to be drawn
950 *	underline:	if non-zero, text is drawn underlined.
951 * Returns:
952 *	nothing.
953 *****/
954 static void
PStext(PSDisplay * dpy,String t,Boolean underline)955 PStext(PSDisplay *dpy, String t, Boolean underline)
956 {
957 	String chPtr, t2;					/* temporary buffers */
958 	int nspecial = 0, nisochar = 0;		/* special character counters */
959 
960 	chPtr = t;
961 
962 	/* Count number of special characters in the given text */
963 	while(*chPtr != '\0')
964 	{
965 		if(*chPtr == L_PAREN || *chPtr == R_PAREN || *chPtr == B_SLASH)
966 			nspecial++;
967 		else if(*(Byte *)chPtr > (Byte )MAX_ASCII)
968 			nisochar++;
969 		chPtr++;
970 	}
971 
972 	if(nspecial == 0 && nisochar == 0)
973 	{
974 		/* no special characters found, display as is */
975 		PSprintf(dpy, "(%s)%c\n", t, (underline) ? 'U' : 'S');
976 		return;
977 	}
978 
979 	/*****
980 	* Special characters found. Create a new output buffer that will
981 	* contain the original text and any special characters that need to
982 	* be protected.
983 	*
984 	* We don't need to check the return value: all memory allocation
985 	* routines are actually defined as macros to functions that perform
986 	* return value checking.
987 	*****/
988 
989 	t2 = (String)malloc((chPtr - t) + nspecial + 3*nisochar + 1);
990 
991 	/*****
992 	* For each character in the provided text, check if it is a special char.
993 	* If we have a special, escape it by inserting a backslash into t2, then
994 	* insert the actual char. Ordinary characters are copied as is.
995 	*****/
996 	chPtr = t2;
997 	while(*t != '\0')
998 	{
999 		if(*t == L_PAREN || *t == R_PAREN || *t == B_SLASH)
1000 		{
1001 			/* parenthesis and backslash must be escaped */
1002 			*(chPtr++) = B_SLASH;
1003 			*(chPtr++) = *t;
1004 		}
1005 		else
1006 		{
1007 			/* non-ascii character, escape and convert to octal */
1008 			if(*(Byte*)t > (Byte)MAX_ASCII)
1009 			{
1010 				/*  convert to octal */
1011 				*(chPtr++) = B_SLASH;
1012 				*(chPtr++) = ((int)(*(Byte*)t)>>6 & 007) + '0';
1013 				*(chPtr++) = ((int)(*(Byte*)t)>>3 & 007) + '0';
1014 				*(chPtr++) = ((int)(*(Byte*)t) & 007) + '0';
1015 			}
1016 			else
1017 				*(chPtr++) = *t;
1018 		}
1019 
1020 		t++;
1021 	}
1022 	/* terminate */
1023 	*(chPtr) = '\0';
1024 
1025 	/* output */
1026 	PSprintf(dpy, "(%s)%c\n", t2, (underline) ? 'U' : 'S');
1027 
1028 	/* all done */
1029 	free(t2);
1030 }
1031 
1032 /*****
1033 * Name:			PScheckPage
1034 * Return Type: 	void
1035 * Description: 	check if a new new page should be started.
1036 * In:
1037 *	dpy:		current postscript output area
1038 *	x:			current horizontal pixel position
1039 *	y:			current vertical pixel position
1040 * Returns:
1041 *	nothing
1042 *****/
1043 static void
PScheckPage(PSDisplay * dpy,int x,int y)1044 PScheckPage(PSDisplay *dpy, int x, int y)
1045 {
1046 	if(y > dpy->start_y + dpy->Pixels_This_Page)
1047 	{
1048 		PSshowpage(dpy);
1049 		dpy->start_y = y;
1050 		PSnewpage(dpy);
1051 	}
1052 	dpy->offset = 0;
1053 }
1054 
1055 /*****
1056 * Name: 		PSmoveto
1057 * Return Type: 	void
1058 * Description: 	move to output vector to a new x,y location. If the given
1059 *				y value does not fit within the current page, start a new
1060 *				page.
1061 * In:
1062 *	dpy:		current postscript output area
1063 *	x:			current horizontal pixel position
1064 *	y:			current vertical pixel position
1065 * Returns:
1066 *	nothing.
1067 *****/
1068 static void
PSmoveto(PSDisplay * dpy,int x,int y)1069 PSmoveto(PSDisplay *dpy, int x, int y)
1070 {
1071 	PScheckPage(dpy, x, y);
1072 	dpy->offset = 0;
1073 	PSprintf(dpy, "%d %d M\n", x, -(y - dpy->start_y));
1074 }
1075 
1076 /*****
1077 * Name: 		PSmove_offset
1078 * Return Type: 	void
1079 * Description: 	set y-offset. Performs a relative vertical move whenever
1080 *				the offset changes.
1081 * In:
1082 *	dpy:		current postscript output area
1083 *	offset:		vertical (pixel) offset.
1084 * Returns:
1085 *	nothing.
1086 *****/
1087 static void
PSmove_offset(PSDisplay * dpy,int offset)1088 PSmove_offset(PSDisplay *dpy, int offset)
1089 {
1090 	if(offset != dpy->offset)
1091 	{
1092 		PSprintf(dpy, "0 %d R\n", dpy->offset - offset );
1093 		dpy->offset = offset;
1094 	}
1095 }
1096 
1097 /*****************************************************************************
1098 ****************** Image & Color conversion Routines *************************
1099 *****************************************************************************/
1100 
1101 /*****
1102 * Name:			PScolormap
1103 * Return Type: 	void
1104 * Description: 	writes a colormap. Produces code for the colormap of
1105 *				any images following. If this is a grayscale image, a
1106 *				grayscale colormap is produced.
1107 * In:
1108 *	dpy:		current postscript output area
1109 *	color:		indicates color or grayscale.
1110 *	nc:			number of colors
1111 *	rmap:		red color components;
1112 *	gmap:		green color components;
1113 *	bmap:		blue color components;
1114 * Returns:
1115 *	nothing.
1116 *****/
1117 static void
PScolormap(PSDisplay * dpy,Boolean color,int nc,Dimension * rmap,Dimension * gmap,Dimension * bmap)1118 PScolormap(PSDisplay *dpy, Boolean color, int nc,
1119 	Dimension *rmap, Dimension *gmap, Dimension *bmap)
1120 {
1121 	int i;
1122 
1123 	/* define the colormap */
1124 	PSprintf(dpy, "/cmap %d string def\n\n\n", nc * ((color) ? 3 : 1));
1125 
1126 	/* load it */
1127 	PSprintf(dpy, "currentfile cmap readhexstring\n");
1128 
1129 	/* write out each color */
1130 	for(i = 0; i < nc; i++)
1131 	{
1132 		if(color)	/* downscale to 0-255 */
1133 			PSprintf(dpy, "%02x%02x%02x ", rmap[i]>>8, gmap[i]>>8, bmap[i]>>8);
1134 		else
1135 			PSprintf(dpy, "%02x ", MONO(rmap[i], gmap[i], bmap[i]));
1136 
1137 		/* make it look nice */
1138 		if((i % 10) == 9)
1139 			PSprintf(dpy, "\n");
1140 	}
1141 	PSprintf(dpy, "\n");
1142 	PSprintf(dpy, "pop pop\n"); /* lose return values from readhexstring */
1143 }
1144 
1145 /*****
1146 * Name:			PSencode
1147 * Return Type: 	int
1148 * Description: 	perform run length encoding.
1149 *				RLE is used to reduce file size (and thus reduce the time
1150 *				required to send it to the printer). The disadvantage is
1151 *				increased processing time.
1152 * In:
1153 *	data:		data to be rle'd
1154 *	rle:		return buffer.
1155 *	size:		size of data.
1156 * Returns:
1157 *	size of return buffer.
1158 * Note:
1159 *	rle is encoded as such:
1160 *	<count> <value>			# 'run' of count+1 equal pixels
1161 *	<count | 0x80> <count+1 data bytes>	# count+1 non-equal pixels
1162 *	count can range between 0 and 127
1163 *****/
1164 static int
PSencode(unsigned char * data,unsigned char * rle,int size)1165 PSencode(unsigned char *data, unsigned char *rle, int size)
1166 {
1167     int i, j, blocklen, isrun, rlen;
1168     unsigned char block[256], pix;
1169 
1170     blocklen = isrun = rlen = 0;
1171 
1172     for(i = 0; i < size; i++)
1173     {
1174 		/*****
1175 		* 5 possible states:
1176 		*   0: block empty.
1177 		*   1: block is a run, current pix == previous pix
1178 		*   2: block is a run, current pix != previous pix
1179 		*   3: block not a run, current pix == previous pix
1180 		*   4: block not a run, current pix != previous pix
1181 		*****/
1182 
1183 		pix = data[i];
1184 
1185 		if(!blocklen)
1186 		{
1187 		    /* case 0:  empty */
1188 		    block[blocklen++] = pix;
1189 		    isrun = 1;
1190 		}
1191 		else if(isrun)
1192 		{
1193 		    if(pix == block[blocklen-1])
1194 		    {
1195 				/*  case 1:  isrun, prev==cur */
1196 				block[blocklen++] = pix;
1197 		    }
1198 		    else
1199 		    {
1200 				/*  case 2:  isrun, prev!=cur */
1201 				if(blocklen>1)
1202 				{
1203 				    /*  we have a run block to flush */
1204 					rle[rlen++] = blocklen-1;
1205 					rle[rlen++] = block[0];
1206 					/*  start new run block with pix */
1207 					block[0] = pix;
1208 					blocklen = 1;
1209 				}
1210 				else
1211 				{
1212 					/*  blocklen<=1, turn into non-run */
1213 					isrun = 0;
1214 					block[blocklen++] = pix;
1215 				}
1216 			}
1217 		}
1218 		else
1219 		{
1220 			/* not a run */
1221 			if(pix == block[blocklen-1])
1222 			{
1223 				/* case 3: non-run, prev==cur */
1224 				if(blocklen>1)
1225 				{
1226 					/*  have a non-run block to flush */
1227 					rle[rlen++] = (blocklen-1) | 0x80;
1228 					for(j = 0; j < blocklen; j++)
1229 						rle[rlen++] = block[j];
1230 					/*  start new run block with pix */
1231 					block[0] = pix;
1232 					blocklen = isrun = 1;
1233 				}
1234 				else
1235 				{
1236 					/*  blocklen<=1 turn into a run */
1237 					isrun = 1;
1238 					block[blocklen++] = pix;
1239 				}
1240 			}
1241 			else
1242 			{
1243 				/* case 4:  non-run, prev!=cur */
1244 				block[blocklen++] = pix;
1245 			}
1246 		}
1247 
1248 		/* max block length.  flush */
1249 		if(blocklen == 128)
1250 		{
1251 			if(isrun)
1252 			{
1253 				rle[rlen++] = blocklen-1;
1254 				rle[rlen++] = block[0];
1255 			}
1256 			else
1257 			{
1258 				rle[rlen++] = (blocklen-1) | 0x80;
1259 				for(j = 0; j < blocklen; j++)
1260 					rle[rlen++] = block[j];
1261 			}
1262 			blocklen = 0;
1263 		}
1264 	}
1265 
1266 	/* flush last block */
1267 	if(blocklen)
1268 	{
1269 		if(isrun)
1270 		{
1271 			rle[rlen++] = blocklen-1;
1272 			rle[rlen++] = block[0];
1273 		}
1274 		else
1275 		{
1276 			rle[rlen++] = (blocklen-1) | 0x80;
1277 			for(j = 0; j < blocklen; j++)
1278 				rle[rlen++] = block[j];
1279 		}
1280 	}
1281 	return(rlen);
1282 }
1283 
1284 /*****
1285 * Name: 		PShex
1286 * Return Type: 	output a hexadecimal value.
1287 * Description:
1288 * In:
1289 *	dpy:		current postscript output area
1290 *	val:		value to be converted
1291 *	flush:		if True, flush out the current buffer.
1292 * Returns:
1293 *	EOF on failure, something else otherwise.
1294 *****/
1295 static int
PShex(PSDisplay * dpy,Byte val,int flush)1296 PShex(PSDisplay *dpy, Byte val, int flush)
1297 {
1298 	static char digit[] = "0123456789abcdef";
1299 
1300 	/* convert to hexadecimal and collect */
1301 	if(!flush)
1302 	{
1303 		dpy->hexline[dpy->hexi++] = (char) digit[((unsigned) val >>
1304 			(unsigned) 4) & (unsigned) 0x0f];
1305 		dpy->hexline[dpy->hexi++] = (char) digit[(unsigned) val &
1306 			(unsigned) 0x0f];
1307 	}
1308 
1309 	/* flush requested or buffer full */
1310 	if((flush && dpy->hexi) || (dpy->hexi > 77))
1311 	{
1312 		dpy->hexline[dpy->hexi] = '\0';
1313 		dpy->hexi = 0;
1314 		return(PSprintf(dpy, "%s\n", dpy->hexline));
1315 	}
1316 	return(0);
1317 }
1318 
1319 /*****
1320 * Name: 		PSColorImage
1321 * Return Type:	void
1322 * Description:	created postscript colorimage operator
1323 *				Adds code that checks if the PostScript device in question
1324 *				knows about the 'colorimage' operator.  If it doesn't, it
1325 *				defines 'colorimage' in terms of image (ie, generates a
1326 *				greyscale image from RGB data)
1327 * In:
1328 *	dpy:		current postscript output area
1329 * Returns:
1330 *	nothing.
1331 *****/
1332 static void
PSColorImage(PSDisplay * dpy)1333 PSColorImage(PSDisplay *dpy)
1334 {
1335     static char *txt[] = {
1336 	"% define 'colorimage' if it isn't defined",
1337 	"%   ('colortogray' and 'mergeprocs' come from xwd2ps",
1338 	"%	 via xgrab)",
1339 	"/colorimage where   % do we know about 'colorimage'?",
1340 	"  { pop }		   % yes: pop off the 'dict' returned",
1341 	"  {				 % no:  define one",
1342 	"	/colortogray {  % define an RGB->I function",
1343 	"	  /rgbdata exch store	% call input 'rgbdata'",
1344 	"	  rgbdata length 3 idiv",
1345 	"	  /npixls exch store",
1346 	"	  /rgbindx 0 store",
1347 	"	  /grays npixls string store  % str to hold the result",
1348 	"	  0 1 npixls 1 sub {",
1349 	"		grays exch",
1350 	"		rgbdata rgbindx	   get 20 mul	% Red",
1351 	"		rgbdata rgbindx 1 add get 32 mul	% Green",
1352 	"		rgbdata rgbindx 2 add get 12 mul	% Blue",
1353 	"		add add 64 idiv	  % I = .5G + .31R + .18B",
1354 	"		put",
1355 	"		/rgbindx rgbindx 3 add store",
1356 	"	  } for",
1357 	"	  grays",
1358 	"	} bind def\n",
1359 	/* Utility procedure for colorimage operator.
1360 	 * This procedure takes two procedures off the
1361 	 * stack and merges them into a single procedure
1362 	 */
1363 	"	/mergeprocs { % def",
1364 	"	  dup length",
1365 	"	  3 -1 roll dup length dup 5 1 roll",
1366 	"	  3 -1 roll add array cvx dup",
1367 	"	  3 -1 roll 0 exch putinterval",
1368 	"	  dup 4 2 roll putinterval",
1369 	"	} bind def\n",
1370 	"	/colorimage { % def",
1371 	/* remove 'false 3' operands */
1372 	"	  pop pop",
1373 	"	  {colortogray} mergeprocs",
1374 	"	  image",
1375 	"	} bind def",
1376 	/* end of 'false' case */
1377 	"  } ifelse"
1378     };
1379 
1380     PSconst_out(dpy, txt);
1381 }
1382 
1383 /*****
1384 * Name:			PSrle_cmapimage
1385 * Return Type: 	void
1386 * Description: 	define rlecmapimage operator
1387 * In:
1388 *	dpy:		current postscript output area
1389 *	color:		indicates color or mono.
1390 * Returns:
1391 *	nothing.
1392 *****/
1393 static void
PSrle_cmapimage(PSDisplay * dpy,int color)1394 PSrle_cmapimage(PSDisplay *dpy, int color)
1395 {
1396 	/* prolog */
1397 	static char *txt[] = {
1398 		/* rlecmapimage expects to have 'w h bits matrix' on stack */
1399 		"/rlecmapimage {",
1400 		"  /buffer 1 string def",
1401 		"  /rgbval 3 string def",
1402 		"  /block  384 string def",
1403 		"  { currentfile buffer readhexstring pop",
1404 		"	/bcount exch 0 get store",
1405 		"	bcount 128 ge",
1406 		"	{ ",
1407 		"	  0 1 bcount 128 sub",
1408 		"	{ currentfile buffer readhexstring pop pop"
1409 	};
1410 
1411 	/* color operator */
1412 	static char *txt_color[] = {
1413 		"		/rgbval cmap buffer 0 get 3 mul 3 getinterval store",
1414 		"		block exch 3 mul rgbval putinterval",
1415 		"	  } for",
1416 		"	  block  0  bcount 127 sub 3 mul  getinterval",
1417 		"	}",
1418 		"	{ ",
1419 		"	  currentfile buffer readhexstring pop pop",
1420 		"	  /rgbval cmap buffer 0 get 3 mul 3 getinterval store",
1421 		"	  0 1 bcount { block exch 3 mul rgbval putinterval } for",
1422 		"	  block 0 bcount 1 add 3 mul getinterval",
1423 		"	} ifelse",
1424 		"  }",
1425 		"  false 3 colorimage",
1426 		"} bind def"
1427 	};
1428 
1429 	/* grayscale operator */
1430 	static char *txt_gray[] = {
1431 		"		/rgbval cmap buffer 0 get 1 getinterval store",
1432 		"		block exch rgbval putinterval",
1433 		"	  } for",
1434 		"	  block  0  bcount 127 sub  getinterval",
1435 		"	}",
1436 		"	{ ",
1437 		"	  currentfile buffer readhexstring pop pop",
1438 		"	  /rgbval cmap buffer 0 get 1 getinterval store",
1439 		"	  0 1 bcount { block exch rgbval putinterval } for",
1440 		"	  block 0 bcount 1 add getinterval",
1441 		"	} ifelse",
1442 		"  }",
1443 		"  image",
1444 		"} bind def"
1445 	};
1446 
1447 	/* put prolog */
1448 	PSconst_out(dpy, txt);
1449 
1450 	if(color)
1451 		PSconst_out(dpy, txt_color);
1452 	else
1453 		PSconst_out(dpy, txt_gray);
1454 }
1455 
1456 /*****
1457 * Name: 		PSImageBW
1458 * Return Type: 	int
1459 * Description: 	writes out a Black & White image
1460 * In:
1461 *	dpy:		current postscript output area
1462 *	data:		image data
1463 *	w:			scanline width
1464 *	h:			no of scanlines
1465 *	inverse:	indicates color reversal
1466 * Returns:
1467 *	0 on success, EOF if write failed.
1468 * Note
1469 *	Write the given image array 'pic' (B/W stippled, 1 byte per pixel,
1470 *	0=blk,1=wht) out as hexadecimal, max of 72 hex chars per line.  If
1471 *	inverse is True, then white = 0 and black = 1.
1472 *****/
1473 static int
PSImageBW(PSDisplay * dpy,Byte * data,int w,int h,Boolean inverse)1474 PSImageBW(PSDisplay *dpy, Byte *data, int w, int h, Boolean inverse)
1475 {
1476 	int	i, j;
1477 	int	err = 0;
1478 	Byte outbyte, bitnum, bit;
1479 
1480 	outbyte = bitnum = 0;
1481 
1482 	/* from left to right, top to bottom */
1483 	for(i = 0; i < h && err != EOF; i++)
1484 	{
1485 		for(j = 0; j < w && err != EOF; j++)
1486 		{
1487 			bit = *(data++);
1488 			outbyte = (outbyte<<1) | ((bit)&0x01);
1489 			bitnum++;
1490 
1491 			if(bitnum == 8)
1492 			{
1493 				if(inverse)
1494 					outbyte = ~outbyte & 0xff;
1495 				err = PShex(dpy, outbyte, False);
1496 				outbyte = bitnum = 0;
1497 			}
1498 		}
1499 		if(bitnum)
1500 		{	/*  few bits left over in this row */
1501 			outbyte <<= 8-bitnum;
1502 			if(inverse)
1503 				outbyte = ~outbyte & 0xff;
1504 			err = PShex(dpy, outbyte, False);
1505 			outbyte = bitnum = 0;
1506 		}
1507 	}
1508 	err = PShex(dpy, '\0', True);	/*  Flush the hex buffer if needed */
1509 
1510 	return(err);
1511 }
1512 
1513 /*****
1514 * Name:			PSImage
1515 * Return Type: 	void
1516 * Description: 	convert image to postscript.
1517 * In:
1518 *	dpy:		current postscript output area
1519 *	image:		image data
1520 * Returns:
1521 *
1522 *****/
1523 static void
PSImage(PSDisplay * dpy,XmHTMLImage * image,int x,int y)1524 PSImage(PSDisplay *dpy, XmHTMLImage *image, int x, int y)
1525 {
1526 	XmImageInfo *info = image->html_image;
1527 	Byte *data = info->data;
1528 	int nc = info->ncolors;
1529 	int i, j;
1530 	int w = info->width;
1531 	int h = info->height;
1532 	int slen, colortype, bits;
1533 	int err=0;
1534 	int extra = 0;
1535 	int dest_x = x, dest_y = 0;
1536 	Boolean isanchor = False;
1537 	Boolean colorps = False;
1538 
1539 	/* set image name as a comment */
1540 	PSprintf(dpy, "%% PSImage, URL=%s, width = %i, height = %i\n",
1541 		image->url ? image->url : "(unknown)", image->width, image->height);
1542 
1543     /* Isgray returns true if the nth color index is a gray value */
1544 #define Isgray(i,n) (i->reds[n] == i->greens[n] && i->reds[n] == i->blues[n])
1545 
1546     /* Is_bg returns true if the nth color index is the screen background */
1547 #define Is_bg(i,n)  (i->reds[n] == dpy->bg[0] && \
1548 		i->greens[n] == dpy->bg[1] && i->blues[n]== dpy->bg[2])
1549 
1550     /* Is_fg returns true if the nth color index is the screen foreground */
1551 #define Is_fg(i,n)  (i->reds[n] == dpy->fg[0] && \
1552 		i->greens[n] == dpy->fg[1] && i->blues[n] == dpy->fg[2])
1553 
1554 	dest_y = -(y - dpy->start_y) - image->height;
1555 
1556 	if(data == NULL)
1557 	{
1558 		/*****
1559 		*  image was not available, draw an empty square instead
1560 		*****/
1561 		PSprintf(dpy, "gsave\n%i %i translate\n%d %d scale\n",
1562 			dest_x, dest_y, w, h);
1563 		PSprintf(dpy, "0.9 setgray SQ fill\n");
1564 		PSprintf(dpy, "grestore\n\n");
1565 		return;
1566 	}
1567 
1568 	if(image->owner && image->owner->anchor != NULL &&
1569 		image->owner->anchor->url_type != ANCHOR_NAMED)
1570 		isanchor = True;
1571 
1572 	/* draw outline if this is an anchored image */
1573 	if(isanchor)
1574 	{
1575 		PSprintf(dpy, "gsave\n%i %i translate\n%d %d scale\n",
1576 			dest_x-2, dest_y-2, w+4, h+4);
1577 		PSprintf(dpy, "SQ fill\n");
1578 		PSprintf(dpy, "grestore\n");
1579 		extra = 4;
1580 	}
1581 
1582 	/*****
1583 	* This is a hack to see if the image is Black & White,
1584 	* Greyscale or 8 bit color
1585 	* assume it's bw if it has only one or two colors, both some grey's
1586 	* assume it's greyscale if all the colors (>2) are grey
1587 	* Images with only one color do occur too.
1588 	*****/
1589 
1590 	if(((nc == 2)
1591 			&& ((Isgray(info,0) && Isgray(info,1))
1592 			|| (Is_bg(info,0) && Is_fg(info,1))
1593 			|| (Is_fg(info,0) && Is_bg(info,1)) ))
1594 		|| ((nc == 1)
1595 			&& (Isgray(info,0) || Is_bg(info,0) || Is_fg(info,0))))
1596 	{
1597 		/* this is a black & white image */
1598 		colortype = F_BWDITHER;
1599 		slen = (w+7)/8;
1600 		bits = 1;
1601 		colorps = False;
1602 	}
1603 	else
1604 	{
1605 		/* assume grayscale unless proven otherwise */
1606 		colortype = F_GREYSCALE;
1607 		slen = w;
1608 		bits = 8;
1609 		colorps = False;
1610 		for(i = 0; i < nc; i++)
1611 		{
1612 			if(!Isgray(info,i))
1613 			{
1614 				/* it's color */
1615 				colortype = F_REDUCED;
1616 				slen = w*3;
1617 				bits = 8;
1618 				colorps = True;
1619 				break;
1620 			}
1621 		}
1622 	}
1623 
1624 	/*  build a temporary dictionary */
1625 	PSprintf(dpy, "20 dict begin\n\n");
1626 
1627 	/*  define string to hold a scanline's worth of data */
1628 	PSprintf(dpy, "/pix %d string def\n\n", slen);
1629 
1630 	/*  position and scaling */
1631 	PSprintf(dpy, "gsave\n");
1632 
1633 	if(colortype == F_BWDITHER)
1634 	{
1635 		/*  1-bit dither code uses 'image' */
1636 		Boolean inverse = False;
1637 
1638 		/*  set if color#0 is 'white' */
1639 		if((nc == 2 &&
1640 			MONO(info->reds[0], info->greens[0], info->blues[0]) >
1641 				MONO(info->reds[1], info->greens[1], info->blues[1])) ||
1642 			(nc == 1 &&
1643 				MONO(info->reds[0], info->greens[0],info->blues[0]) >
1644 				MONO(127, 127, 127)))
1645 		{
1646 			inverse = True;
1647 		}
1648 
1649 		/*  dimensions of data */
1650 		PSprintf(dpy, "%d %d %d\n", w, h, bits);
1651 
1652 		/*  mapping matrix */
1653 		PSprintf(dpy, "[%d 0 0 %d 0 %d]\n\n", w, -h, h);
1654 
1655 		/* Position and scaling */
1656 		PSprintf(dpy, "%i %i translate\n%d %d scale\n", dest_x, dest_y, w, h);
1657 
1658 		PSprintf(dpy, "{currentfile pix readhexstring pop}\n");
1659 		PSprintf(dpy, "image\n");
1660 
1661 		/*  write the actual image data */
1662 		err = PSImageBW(dpy, data, w, h, inverse);
1663 	}
1664 	else
1665 	{
1666 		/*  all other formats */
1667 		unsigned char *rleline = (unsigned char *) NULL;
1668 		int rlen;
1669 
1670 		/*  if we're using color, make sure 'colorimage' is defined */
1671 		if(colorps)
1672 			PSColorImage(dpy);
1673 
1674 		PScolormap(dpy, colorps, nc, info->reds, info->greens, info->blues);
1675 		PSrle_cmapimage(dpy, colorps);
1676 
1677 		/*  dimensions of data */
1678 		PSprintf(dpy, "%d %d %d\n", w, h, bits);
1679 
1680 		/*  mapping matrix */
1681 		PSprintf(dpy, "[%d 0 0 %d 0 %d]\n", w, -h, h);
1682 
1683 		/* Position and scaling */
1684 		PSprintf(dpy, "%i %i translate\n%d %d scale\n", dest_x, dest_y, w, h);
1685 
1686 		PSprintf(dpy, "rlecmapimage\n");
1687 
1688 		rleline = (unsigned char *) malloc(w * 2);
1689 		if(!rleline)
1690 			return;
1691 
1692 		for(i = 0; i < h && err != EOF; i++)
1693 		{
1694 		    rlen = PSencode(data, rleline, w);
1695 		    data += w;
1696 		    for(j = 0; j < rlen && err != EOF; j++)
1697 				err=PShex(dpy, rleline[j], False);
1698 		    err=PShex(dpy, '\0', True);	/*  Flush the hex buffer */
1699 		}
1700 		free(rleline);
1701 	}
1702 
1703 	/*  stop using temporary dictionary */
1704 	PSprintf(dpy, "end\n");
1705 	PSprintf(dpy, "grestore\n\n");
1706 
1707 #undef Isgray
1708 #undef Is_fg
1709 #undef Is_bg
1710 }
1711 
1712 
1713 /************************************************************************/
1714 /***********************								 ****************/
1715 /*********************** XmHTML ToolkitAbstrion Routines ****************/
1716 /***********************								 ****************/
1717 /************************************************************************/
1718 
1719 /*****
1720 * Name:			pstkSetForeground
1721 * Return Type: 	int
1722 * Description: 	sets foreground color
1723 * In:
1724 *	dpy:		current postscript output area
1725 *	gc:			graphics context, unused
1726 *	foreground:	foreground color to set
1727 * Returns:
1728 *	PSprintf return value (ignored by caller)
1729 *****/
1730 static int
pstkSetForeground(Display * disp,XGC gc,unsigned long foreground)1731 pstkSetForeground(Display *disp, XGC gc, unsigned long foreground)
1732 {
1733 	PSDisplay *dpy = (PSDisplay*)disp;
1734 	XmHTMLWidget html = dpy->html;
1735 	unsigned short red, green, blue;
1736 
1737 	XCCGetColor(HTML_ATTR(xcc), foreground, &red, &green, &blue);
1738 
1739 	return(PSprintf(dpy, "%u %u %u setrgbcolor\n", red, green, blue));
1740 }
1741 
1742 static GC
pstkCreateGC(Display * disp,DRAWABLE win,unsigned long valuemask,XGCValues * values)1743 pstkCreateGC(Display *disp, DRAWABLE win, unsigned long valuemask,
1744 	XGCValues *values)
1745 {
1746 	return(NULL);
1747 }
1748 
1749 static int
pstkFreeGC(Display * disp,XGC gc)1750 pstkFreeGC(Display *disp, XGC gc)
1751 {
1752 	return(1);
1753 }
1754 
1755 static int
pstkCopyGC(Display * disp,XGC src,unsigned long valuemask,XGC dest)1756 pstkCopyGC(Display *disp, XGC src, unsigned long valuemask, XGC dest)
1757 {
1758 	return(1);
1759 }
1760 
1761 static int
pstkSetFunction(Display * disp,XGC gc,int function)1762 pstkSetFunction(Display *disp, XGC gc, int function)
1763 {
1764 	return(1);
1765 }
1766 
1767 static int
pstkSetClipMask(Display * disp,XGC gc,PIXMAP pixmap)1768 pstkSetClipMask(Display *disp, XGC gc, PIXMAP pixmap)
1769 {
1770 	return(1);
1771 }
1772 
1773 static int
pstkSetClipOrigin(Display * disp,XGC gc,int clip_x_origin,int clip_y_origin)1774 pstkSetClipOrigin(Display *disp, XGC gc, int clip_x_origin,
1775 	int clip_y_origin)
1776 {
1777 	return(1);
1778 }
1779 
1780 static int
pstkSetTile(Display * disp,XGC gc,PIXMAP tile)1781 pstkSetTile(Display *disp, XGC gc, PIXMAP tile)
1782 {
1783 	return(1);
1784 }
1785 
1786 static int
pstkSetTSOrigin(Display * disp,XGC gc,int ts_x_origin,int ts_y_origin)1787 pstkSetTSOrigin(Display *disp, XGC gc, int ts_x_origin, int ts_y_origin)
1788 {
1789 	return(1);
1790 }
1791 
1792 static int
pstkSetFillStyle(Display * disp,XGC gc,int fill_style)1793 pstkSetFillStyle(Display *disp, XGC gc, int fill_style)
1794 {
1795 	return(1);
1796 }
1797 
1798 static int
pstkSetLineAttributes(Display * disp,XGC gc,unsigned int line_width,int line_style,int cap_style,int join_style)1799 pstkSetLineAttributes(Display *disp, XGC gc, unsigned int line_width,
1800 	int line_style, int cap_style, int join_style)
1801 {
1802 	return(1);
1803 }
1804 
1805 static int
pstkSetBackground(Display * disp,XGC gc,unsigned long background)1806 pstkSetBackground(Display *disp, XGC gc, unsigned long background)
1807 {
1808 	return(0);
1809 }
1810 
1811 /*****
1812 * Name:			pstkSetFont
1813 * Return Type: 	int
1814 * Description: 	sets the requested font
1815 * In:
1816 *	dpy:		current postscript output area
1817 *	gc:			graphics context, unused
1818 *	font:		font to set
1819 * Returns:
1820 *	always 1 (ignored by caller)
1821 *****/
1822 static int
pstkSetFont(Display * disp,XGC gc,XmHTMLfont * font)1823 pstkSetFont(Display *disp, XGC gc, XmHTMLfont *font)
1824 {
1825 	PSDisplay *dpy = (PSDisplay*)disp;
1826 	PSfont(dpy, font, False);
1827 	return(1);
1828 }
1829 
1830 /*****
1831 * Name:			pstkTextWidth
1832 * Return Type: 	int
1833 * Description: 	Postscript textwidth function.
1834 * In:
1835 *	font:		font to be used when computing pixel width;
1836 *	string:		string for which to compute pixel width;
1837 *	count:		no of characters in string.
1838 * Returns:
1839 *	pixel width of given string.
1840 *****/
1841 static int
pstkTextWidth(XmHTMLfont * font,const char * string,int count)1842 pstkTextWidth(XmHTMLfont *font, const char* string, int count)
1843 {
1844 	char *chPtr;
1845 	int width = 0;
1846 	int i = 0;
1847    fprintf(stderr,"Warning: This function does nothing, returning 0 width.\n");
1848 	for(chPtr = (char*)string; i < count; chPtr++, i++)
1849 	{
1850 	}
1851    return(0); /* Looks like an incomplete function */
1852 }
1853 
1854 #if 0
1855 /*****
1856 * Name:			pstkCopyArea
1857 * Return Type: 	int
1858 * Description: 	postscript XCopyArea.
1859 * In:
1860 *	too many
1861 * Returns:
1862 *	always 1 (ignored by caller)
1863 *****/
1864 static int
1865 pstkCopyArea(Display *disp, DRAWABLE src, DRAWABLE dest, XGC gc, int src_x,
1866 	int src_y, unsigned int width, unsigned int height, int dest_x, int dest_y)
1867 {
1868 	PSDisplay *dpy = (PSDisplay*)disp;
1869 
1870 	PSprintf(dpy, "%% pstkCopyArea\n");
1871 
1872 	PScheckPage(dpy, dest_x, dest_y);
1873 	ImageToPs(XtDisplay(dpy->html), src, src_x, src_y,
1874 		width, height, dest_x, -(dest_y - dpy->start_y), 1);
1875 
1876 	return(1);
1877 }
1878 #endif
1879 
1880 /*****
1881 * Name:			pstkDrawString
1882 * Return Type: 	int
1883 * Description: 	renders text
1884 * In:
1885 *	too many
1886 * Returns:
1887 *	always 1 (ignored by caller)
1888 *****/
1889 static int
pstkDrawString(Display * disp,DRAWABLE win,struct _XmHTMLFont * font,XGC gc,int x,int y,char * string,int length)1890 pstkDrawString(Display *disp, DRAWABLE win, struct _XmHTMLFont *font,
1891 	XGC gc, int x, int y, char *string, int length)
1892 {
1893 	PSDisplay *dpy = (PSDisplay*)disp;
1894 	static char *last_ep=NULL;
1895 	char *ep = string+strlen(string);
1896 
1897 	/*****
1898 	* XmHTML sends preformatted(<PRE>) text as multiple words within 'str'.
1899 	* Then makes successive calls to this routine adjusting the pointer to
1900 	* the start of the next word in the same string. The problem is, what
1901 	* XmHTML (actually the X server's adobe fonts) thinks  is the width of a
1902 	* space is not what my postscript printer thinks it is. So, until XmHTML
1903 	* changes how it draws <PRE> text, or until I can figure a better way,
1904 	* we'll just ignore successive calls on the rest of the string.
1905 	* -- scott
1906 	*
1907 	* The only correct way would be to add full postscript font handling:
1908 	* one of the properties contained in the XmHTMLfont structure is the
1909 	* width of a single space. I guess we need a real postscript wizard
1910 	* for this as I haven't got *any* clue on how postscript fonts are defined
1911 	* let alone what properties it contains. -- kdh
1912 	*****/
1913 	if(last_ep && last_ep==ep)
1914 		return(1);
1915 	last_ep = ep;
1916 	PSmoveto(dpy, x, y);
1917 	PSfont(dpy, font, False);
1918 	PStext(dpy, string, False);
1919 
1920 	return(1);
1921 }
1922 
1923 /*****
1924 * Name:			pstkDrawAnchorData
1925 * Return Type: 	void
1926 * Description:
1927 * In:
1928 *
1929 * Returns:
1930 *	nothing
1931 *****/
1932 static void
pstkDrawAnchorData(Display * disp,WINDOW win,XGC gc,int x,int y,XmHTMLObjectTableElement data)1933 pstkDrawAnchorData(Display *disp, WINDOW win, XGC gc, int x, int y,
1934 	XmHTMLObjectTableElement data)
1935 {
1936 	PSDisplay *dpy = (PSDisplay*)disp;
1937 	int fnum, offset;
1938 	static char *last_href;
1939 
1940 	/*****
1941 	* Check if this is a real anchor
1942 	* We don't footnote internal anchors. seems sane to me.
1943 	* might want to exclude other types as well. -- scott
1944 	*****/
1945 	if(!data->anchor || !data->anchor->href || data->anchor->href[0]=='\0' ||
1946 		data->anchor->href[0] == '#' || !dpy->font)
1947 		return;
1948 
1949 	if(last_href == data->anchor->href)
1950 		return;
1951 	last_href = data->anchor->href;
1952 
1953 	if(y > dpy->start_y + dpy->Pixels_This_Page)
1954 		return;	/* won't fit on page! */
1955 
1956 	/* add a footnote marker */
1957 	PSprintf(dpy, "%d %d M\n", x, -(y - dpy->start_y));
1958 	offset = dpy->font_size - 6; /* 6 point font for footnote number */
1959 	fnum = fnAdd(dpy->footnotes, data->anchor->href);
1960 	PSprintf(dpy, "/helvetica 6 SF\n");
1961 	PSprintf(dpy, "2 %d R\n(%d)S\n", offset, fnum+1);
1962 	PSprintf(dpy, "%s %d SF\n", dpy->font_style, dpy->font_size);
1963 
1964 	/* reserve room for this footnote */
1965 	dpy->Pixels_This_Page -= FOOTNOTE_ROW_HEIGHT+2;
1966 }
1967 
1968 /*****
1969 * Name:			pstkDrawLine
1970 * Return Type: 	int
1971 * Description: 	renders a single line
1972 * In:
1973 *	too many
1974 * Returns:
1975 *	always 1 (ignored by caller)
1976 *****/
1977 static int
pstkDrawLine(Display * disp,WINDOW win,XGC gc,int x1,int y1,int x2,int y2)1978 pstkDrawLine(Display *disp, WINDOW win, XGC gc, int x1, int y1, int x2, int y2)
1979 {
1980 	PSDisplay *dpy = (PSDisplay*)disp;
1981 	int yd = y2 - y1;
1982 	int xd = x2 - x1;
1983 
1984 	PSprintf(dpy, "%% pstkDrawLine (%d, %d) (%d, %d)\n", x1, y1, x2, y2);
1985 	PSmoveto(dpy, x1, y1);
1986 	PSprintf(dpy, "%d %d RL stroke\n", xd, yd);
1987 
1988 	return(1);
1989 }
1990 
1991 /*****
1992 * Name:			pstkDrawLines
1993 * Return Type: 	void
1994 * Description: 	draws a collection of lines
1995 * In:
1996 *	too many
1997 * Returns:
1998 *	always 1 (ignored by caller)
1999 *****/
2000 static int
pstkDrawLines(Display * disp,DRAWABLE win,XGC gc,XPoint * points,int num_points,int mode)2001 pstkDrawLines(Display *disp, DRAWABLE win, XGC gc, XPoint *points,
2002 	int num_points, int mode)
2003 {
2004 	PSDisplay *dpy = (PSDisplay*)disp;
2005 	int i;
2006 
2007 	PSprintf(dpy, "%% pstkDrawLines\n");
2008 	for(i = 0; i < num_points - 1; ++i)
2009 	{
2010 		pstkDrawLine(disp, win, gc,
2011 			points[i].x, points[i].y, points[i+1].x, points[i+1].y);
2012 	}
2013 	return(1);
2014 }
2015 
2016 /*****
2017 * Name:			pstkDrawRectangle
2018 * Return Type: 	int
2019 * Description: 	renders a plain rectangle
2020 * In:
2021 *	too many
2022 * Returns:
2023 *	always 1 (ignored by caller)
2024 *****/
2025 static int
pstkDrawRectangle(Display * disp,WINDOW win,XGC gc,int x,int y,unsigned int width,unsigned int height)2026 pstkDrawRectangle(Display *disp, WINDOW win, XGC gc, int x, int y,
2027 	unsigned int width, unsigned int height)
2028 {
2029 	PSDisplay *dpy = (PSDisplay*)disp;
2030 	int ny = y + height;
2031 
2032 	PSprintf(dpy, "%% pstkDrawRectangle\n");
2033 	PScheckPage(dpy, x, ny);
2034 	PSprintf(dpy, "newpath %d %d M %u 0 RL 0 -%u RL -%u 0 RL 0 %u RL "
2035 		"closepath stroke\n", x, -(y - dpy->start_y),
2036 		width, height, width, height);
2037 
2038 	return(1);
2039 }
2040 
2041 /*****
2042 * Name:			pstkFillRectangle
2043 * Return Type: 	int
2044 * Description: 	renders a filled rectangle.
2045 * In:
2046 *	too many
2047 * Returns:
2048 *	always 1 (ignored by caller)
2049 *****/
2050 static int
pstkFillRectangle(Display * disp,WINDOW win,XGC gc,int x,int y,unsigned int width,unsigned int height)2051 pstkFillRectangle(Display *disp, WINDOW win, XGC gc, int x, int y,
2052 	unsigned int width, unsigned int height)
2053 {
2054 	PSDisplay *dpy = (PSDisplay*)disp;
2055 	int ny = y + height;
2056 
2057 	PSprintf(dpy, "%% pstkFillRectangle\n");
2058 	PScheckPage(dpy, x, ny);
2059 	PSprintf(dpy, "newpath %d %d M %d 0 RL 0 -%d RL -%d 0 RL 0 %d RL "
2060 		"closepath fill stroke\n", x, -(y - dpy->start_y),
2061 		width, height, width, height);
2062 
2063 	return(1);
2064 }
2065 
2066 /*****
2067 * Name:			pstkDrawShadows
2068 * Return Type: 	void
2069 * Description: 	draws a shadow rectangle
2070 * In:
2071 *	too many
2072 * Returns:
2073 *	nothing
2074 * Note:
2075 *	Uses setgray instead of setrgbcolor for the shadow colors. This ensures
2076 *	that shadows will always be drawn.
2077 *****/
2078 static void
pstkDrawShadows(Display * disp,DRAWABLE drawable,XGC top_shadow_GC,XGC bottom_shadow_GC,int x,int y,int width,int height,int shadow_thickness,unsigned int shadow_type)2079 pstkDrawShadows(Display *disp, DRAWABLE drawable,
2080 	XGC top_shadow_GC, XGC bottom_shadow_GC,
2081 #if NeedWidePrototypes
2082 	int x, int y, int width, int height, int shadow_thickness,
2083 #else
2084 	Position x, Position y, Dimension width, Dimension height,
2085 	Dimension shadow_thickness,
2086 #endif
2087 	unsigned int shadow_type)
2088 {
2089 	PSDisplay *dpy = (PSDisplay*)disp;
2090 	int ts=8, bs=4;
2091 
2092 	switch(shadow_type)
2093 	{
2094 		case XmSHADOW_IN:
2095 			/* top & left border */
2096 			PSprintf(dpy, ".%d setgray\n", bs);
2097 			pstkFillRectangle(disp, drawable, bottom_shadow_GC, x, y,
2098 				width, 1);
2099 			pstkFillRectangle(disp, drawable, bottom_shadow_GC, x, y,
2100 				1, height-1);
2101 
2102 			/* bottom & right border */
2103 			PSprintf(dpy, ".%d setgray\n", ts);
2104 			pstkFillRectangle(disp, drawable, top_shadow_GC, x + 1,
2105 				y + height - 1, width - 1, 1);
2106 			pstkFillRectangle(disp, drawable, top_shadow_GC, x - 1, y + 1, 1,
2107 				height - 2);
2108 			break;
2109 		case XmSHADOW_OUT:
2110 			/* top & left border */
2111 			PSprintf(dpy, ".%d setgray\n", ts);
2112 			pstkFillRectangle(disp, drawable, top_shadow_GC, x, y, width, 1);
2113 			pstkFillRectangle(disp, drawable, top_shadow_GC, x, y, 1, height-1);
2114 
2115 			/* bottom & right border */
2116 			PSprintf(dpy, ".%d setgray\n", bs);
2117 			pstkFillRectangle(disp, drawable, bottom_shadow_GC, x + 1,
2118 				y + height - 1, width - 1, 1);
2119 			pstkFillRectangle(disp, drawable, bottom_shadow_GC, x - 1,
2120 				y + 1, 1, height - 2);
2121 			break;
2122 		default:
2123 			break;
2124 	}
2125 	PSprintf(dpy, "1 setgray\n");
2126 }
2127 
2128 /*****
2129 * Name:			pstkDrawArc
2130 * Return Type: 	int
2131 * Description: 	renders an (unfilled) arc.
2132 * In:
2133 *	too many
2134 * Returns:
2135 *	always 1 (ignored by caller)
2136 *****/
2137 static int
pstkDrawArc(Display * disp,WINDOW win,XGC gc,int x,int y,unsigned int width,unsigned int height,int angle1,int angle2)2138 pstkDrawArc(Display *disp, WINDOW win, XGC gc, int x, int y,
2139 	unsigned int width, unsigned int height, int angle1, int angle2)
2140 {
2141 	int ny = y+height;
2142 	int x0, y0;		/* center */
2143 	int r;			/* radius */
2144 	PSDisplay *dpy = (PSDisplay*)disp;
2145 
2146 	PSprintf(dpy, "%% pstkDrawArc (%ux%u+%d+%d)\n", width, height, x, y);
2147 	PScheckPage(dpy, x, ny);
2148 
2149 	r = height/2;
2150 	x0 = x + r;
2151 	y0 = -(y - dpy->start_y + r);
2152 
2153 	PSprintf(dpy, "newpath %d %d M %d %d %d %d %d arc closepath\n",
2154 		x0, y0, x, y, r, angle1, angle2);
2155 
2156 	return(1);
2157 }
2158 
2159 /*****
2160 * Name:			pstkFillArc
2161 * Return Type: 	int
2162 * Description: 	renders a filled arc.
2163 * In:
2164 *	too many args
2165 * Returns:
2166 *	always 1 (ignored by caller)
2167 *****/
2168 static int
pstkFillArc(Display * disp,WINDOW win,XGC gc,int x,int y,unsigned int width,unsigned int height,int angle1,int angle2)2169 pstkFillArc(Display *disp, WINDOW win, XGC gc, int x, int y,
2170 	unsigned int width, unsigned int height, int angle1, int angle2)
2171 {
2172 	int ny = y+height;
2173 	int x0, y0;		/* center */
2174 	int r;			/* radius */
2175 	PSDisplay *dpy = (PSDisplay*)disp;
2176 
2177 	PSprintf(dpy, "%% pstkFillArc (%ux%u+%d+%d) %d\n", width, height, x, y,
2178 		dpy->start_y);
2179 
2180 	PScheckPage(dpy, x, ny);
2181 
2182 	r = height/2;
2183 	x0 = x + r;
2184 	y0 = -(y - dpy->start_y + r);
2185 
2186 	PSprintf(dpy, "newpath %d %d M %d %d %d %d %d arc fill closepath\n",
2187 		x0, y0, x0, y0, r, angle1, angle2);
2188 
2189 	return(1);
2190 }
2191 
2192 static void
pstkDrawImage(XmHTMLWidget html,XmHTMLImage * image,XGC gc,int src_x,int src_y,unsigned int width,unsigned int height,int dest_x,int dest_y)2193 pstkDrawImage(XmHTMLWidget html, XmHTMLImage *image, XGC gc,
2194 	int src_x, int src_y, unsigned int width, unsigned int height,
2195 	int dest_x, int dest_y)
2196 {
2197 	ToolkitAbstraction *tka = HTML_ATTR(tka);
2198 	PSDisplay *dpy = (PSDisplay*)tka->dpy;
2199 
2200 	PSImage(dpy, image, dest_x, dest_y);
2201 }
2202 
2203 /*****
2204 * Name:			_CreatePostscriptTka
2205 * Return Type: 	ToolkitAbstraction
2206 * Description: 	Creates the tka required for Postscript output
2207 * In:
2208 *	html:		current XmHTMLWidget id
2209 * Returns:
2210 *	A new tka, based upon the current tka as found in the widget
2211 *****/
2212 static ToolkitAbstraction*
_CreatePostscriptTka(XmHTMLWidget html)2213 _CreatePostscriptTka(XmHTMLWidget html)
2214 {
2215 	static ToolkitAbstraction *tka;
2216 
2217 	/* copy current tka and override the necessary functions */
2218 	tka = XmHTMLTkaCopy(HTML_ATTR(tka));
2219 
2220 	/* GC functions */
2221 	tka->CreateGC      = pstkCreateGC;
2222 	tka->FreeGC        = pstkFreeGC;
2223 	tka->CopyGC        = pstkCopyGC;
2224 	tka->SetFunction   = pstkSetFunction;
2225 	tka->SetClipMask   = pstkSetClipMask;
2226 	tka->SetClipOrigin = pstkSetClipOrigin;
2227 	tka->SetTile       = pstkSetTile;
2228 	tka->SetTSOrigin   = pstkSetTSOrigin;
2229 	tka->SetFillStyle  = pstkSetFillStyle;
2230 	tka->SetFont       = pstkSetFont;
2231 	tka->SetForeground = pstkSetForeground;
2232 	tka->SetBackground = pstkSetBackground;
2233 	tka->SetLineAttributes = pstkSetLineAttributes;
2234 
2235 	/* Font Allocation functions are not used by postscript output */
2236 
2237 	/* Cursor & pointer functions are not used by postscript output */
2238 
2239 	/* Color functions are not used by postscript output */
2240 
2241 	/* Pixmap functions are not used by postscript output */
2242 
2243 	/* XImage functions are not used by postscript output */
2244 
2245 	/* misc. render functions */
2246 	tka->DrawImage     = pstkDrawImage;
2247 	tka->DrawAnchorData= pstkDrawAnchorData;
2248 
2249 	/* string/text functions are not used by postscript output */
2250 
2251 	/* Render functions */
2252 	tka->DrawString     = pstkDrawString;
2253 	tka->DrawLine       = pstkDrawLine;
2254 	tka->DrawLines      = pstkDrawLines;
2255 	tka->DrawRectangle  = pstkDrawRectangle;
2256 	tka->FillRectangle  = pstkFillRectangle;
2257 	tka->DrawArc        = pstkDrawArc;
2258 	tka->FillArc        = pstkFillArc;
2259 
2260 	/* misc. functions are not used by postscript output */
2261 
2262 	/* X Intrinsic wrappers are not used by postscript output */
2263 
2264 	/* Motif Wrappers */
2265 	tka->DrawShadows    = pstkDrawShadows;
2266 
2267 	return(tka);
2268 }
2269 
2270 /*****
2271 * Name:			_XmHTMLTextGetPS
2272 * Return Type:	String
2273 * Description:	Formats the current HTML into postscript output.
2274 * In:
2275 *	html:		XmHTML Widget id;
2276 *	pdef:		defines the paper size;
2277 *	start/end:	ignored until XmHTML can do selections;
2278 *   options:	An OR of:
2279 *				  XmHTMLTEXT_ADDFOOTER - prints the page number.
2280 *				  XmHTMLTEXT_ANCHORFOOTNOTES - footnotes each anchor on the
2281 *					 the page and provides their HREF's at the bottom.
2282 * Returns:
2283 *	A malloc'd buffer containing postscript output. Calling routine must
2284 *   free the returned buffer.
2285 *****/
2286 String
_XmHTMLTextGetPS(XmHTMLWidget html,XmHTMLPaperSize * pdef,XmHTMLObjectTableElement start,XmHTMLObjectTableElement end,Byte options)2287 _XmHTMLTextGetPS(XmHTMLWidget html, XmHTMLPaperSize *pdef,
2288 	XmHTMLObjectTableElement start, XmHTMLObjectTableElement end,
2289 	Byte options)
2290 {
2291 	ToolkitAbstraction *tka_orig, *tka;
2292 	XmHTMLObjectTableElement pstart, pend;
2293 	int pagewidth;
2294 	Dimension paint_w, paint_h, margin_w, margin_h, work_w;
2295 	Position paint_x, paint_y, scroll_x, scroll_y;
2296 	Boolean anchorb;
2297 	String title;
2298 	PSDisplay *dpy;
2299 	unsigned short red, green, blue;
2300 	String psbuf;
2301 
2302 	/*****
2303 	* Sanity check, Postscript output requires a papersize definition
2304 	* in points.
2305 	*****/
2306 	if(pdef->unit_type != XmHTML_POINT)
2307 	{
2308 		_XmHTMLWarning(__WFUNC__(html, "_XmHTMLTextGetPS"),
2309 			XMHTML_MSG_88, "POINT");
2310 		return(NULL);
2311 	}
2312 
2313 	/* Create a Postscript output area */
2314 	dpy = (PSDisplay *)calloc(1, sizeof(PSDisplay));
2315 
2316 	/* initialize the output area */
2317 	dpy->html = html;
2318 	dpy->options = options;
2319 
2320 	/* copy paper definition into output screen */
2321 	memcpy((void*)&dpy->screen, pdef, sizeof(XmHTMLPaperSize));
2322 
2323 	/*****
2324 	* Postscript top margin seems to behave somewhat differently...
2325 	*
2326 	* Scott, I replaced 11*72 (which I assume is the height of Letter in
2327 	* inches) by the height of the provided papersize (which is already
2328 	* in points). -- kdh.
2329 	*****/
2330 	dpy->screen.top_margin = (pdef->height - pdef->top_margin);
2331 	dpy->screen.height = (dpy->screen.top_margin - dpy->screen.bottom_margin);
2332 
2333 	/*****
2334 	* Calculate the number of Postscript points per pixel of current screen,
2335 	* and the height of the page in pixels (used in figuring when we've hit
2336 	* the bottom of the page) and for computing correct text widths.
2337 	*****/
2338 	dpy->Points_Pixel = 72.0 / GetDpi(html);
2339 
2340 	pagewidth = pdef->width;
2341 
2342 	/*****
2343 	* Reduce the scaling if the width used for formatting is greater than
2344 	* 8 * 72 pixels (8 inch). In theory, this is not what you want for A4
2345 	* paper (only 8.27 inch wide), but I guess that the hw->html.doc_width
2346 	* includes some left and right margins, so it seems to work in practice.
2347 	*
2348 	* Scott, does this test work? Above you set pagewidth to pdef->width
2349 	* and upon entry of this routine, screen.width was set to pdef->width?
2350 	*****/
2351 	if(pagewidth > PAGE_WIDTH)
2352 		dpy->Points_Pixel = dpy->Points_Pixel * (float)PAGE_WIDTH/pagewidth;
2353 
2354 	dpy->Pixels_This_Page = (int)(PAGE_HEIGHT/dpy->Points_Pixel);
2355 	dpy->Pixels_Page = dpy->Pixels_This_Page;
2356 
2357 	/*****
2358 	* Get the foreground and background colors so we can check later
2359 	* for black&white documents
2360 	*****/
2361 	XCCGetColor(HTML_ATTR(xcc), HTML_ATTR(body_fg), &red, &green, &blue);
2362 	dpy->fg[0] = red;
2363 	dpy->fg[1] = green;
2364 	dpy->fg[2] = blue;
2365 	XCCGetColor(HTML_ATTR(xcc), HTML_ATTR(body_bg), &red, &green, &blue);
2366 	dpy->bg[0] = red;
2367 	dpy->bg[1] = green;
2368 	dpy->bg[2] = blue;
2369 
2370 	fnDestroy(dpy->footnotes); /* clear footnotes */
2371 
2372 	PSinit(dpy);
2373 
2374 	title = XmHTMLGetTitle((Widget)html);
2375 
2376 	PSheader(dpy, title ? title : "", 0);
2377 
2378 	PSnewpage(dpy);
2379 
2380 	/* save all settings that will get altered */
2381 	scroll_x = HTML_ATTR(scroll_x);
2382 	scroll_y = HTML_ATTR(scroll_y);
2383 	paint_y  = HTML_ATTR(paint_y);
2384 	paint_h  = HTML_ATTR(paint_height);
2385 	paint_x  = HTML_ATTR(paint_x);
2386 	paint_w  = HTML_ATTR(paint_width);
2387 	pstart   = HTML_ATTR(paint_start);
2388 	pend	 = HTML_ATTR(paint_end);
2389 	margin_w = HTML_ATTR(margin_width);
2390 	margin_h = HTML_ATTR(margin_height);
2391 	work_w   = HTML_ATTR(work_width);
2392 	anchorb  = HTML_ATTR(anchor_buttons);
2393 
2394 	/* reset and set paper properties */
2395 	HTML_ATTR(scroll_x) = 0;
2396 	HTML_ATTR(scroll_y) = 0;
2397 	HTML_ATTR(paint_y) = 0;
2398 	HTML_ATTR(paint_x) = 0;
2399 	HTML_ATTR(paint_width) = pdef->width;
2400 	HTML_ATTR(paint_height) = pdef->height - pdef->bottom_margin;
2401 	HTML_ATTR(paint_start)  = NULL;
2402 	HTML_ATTR(paint_end)  = NULL;
2403 	HTML_ATTR(margin_width) = pdef->left_margin;
2404 	HTML_ATTR(margin_height) = pdef->top_margin;
2405 	HTML_ATTR(work_width) = pdef->width - pdef->left_margin;
2406 	HTML_ATTR(anchor_buttons) = False;		/* no button anchors, looks ugly */
2407 
2408 	dpy->start_y = 0;
2409 
2410 	/* Recompute layout for new paper definition before we set tka */
2411 	_XmHTMLComputeLayout(html);
2412 
2413 	HTML_ATTR(paint_height) = max(HTML_ATTR(paint_height),
2414 		HTML_ATTR(formatted_height));
2415 
2416 	/* temporarily set the ToolkitAbstractions for postscript */
2417 
2418 	/* save current tka */
2419 	tka_orig = HTML_ATTR(tka);
2420 	tka = _CreatePostscriptTka(html);
2421 	HTML_ATTR(tka) = tka;
2422 
2423 	/* save original display pointer */
2424 	dpy->dpy = tka->dpy;
2425 
2426 	/* store new one */
2427 	tka->dpy = (Display*)dpy;
2428 	tka->win = tka_orig->win;
2429 
2430 	/* render as postscript */
2431 	_XmHTMLPaint(html, HTML_ATTR(formatted), NULL);
2432 
2433 	PSshowpage(dpy);
2434 	PStrailer(dpy);
2435 
2436 	/* All done. Get return buffer */
2437 	psbuf = dpy->string;
2438 
2439 	/* all done, release dpy */
2440 	fnDestroy(dpy->footnotes);
2441 	free(dpy);
2442 	XmHTMLTkaDestroy(tka);
2443 
2444 	HTML_ATTR(tka) = tka_orig;
2445 
2446 	/* reset everything */
2447 	HTML_ATTR(scroll_x) = scroll_x;
2448 	HTML_ATTR(scroll_y) = scroll_y;
2449 	HTML_ATTR(paint_y) = paint_y;
2450 	HTML_ATTR(paint_x) = paint_x;
2451 	HTML_ATTR(paint_width) = paint_w;
2452 	HTML_ATTR(paint_height) = paint_h;
2453 	HTML_ATTR(paint_start)  = pstart;
2454 	HTML_ATTR(paint_end)  = pend;
2455 	HTML_ATTR(margin_width) = margin_w;
2456 	HTML_ATTR(margin_height) = margin_h;
2457 	HTML_ATTR(work_width) = work_w;
2458 	HTML_ATTR(anchor_buttons) = anchorb;
2459 
2460 	/* Redisplay to restore everything correctly */
2461 	XmHTMLRedisplay((Widget)html);
2462 
2463 	return(psbuf);
2464 }
2465