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