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