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