1 /*
2 * emspecial.c
3 * This routine handles the emTeX special commands.
4 */
5 #include "dvips.h" /* The copyright notice in that file is included too!*/
6
7 #include <ctype.h>
8 #include <stdlib.h>
9 /*
10 * The external declarations:
11 */
12 #include "protos.h"
13
14 #ifdef EMTEX
15 /* emtex specials, added by rjl */
16
17 #ifndef TRUE
18 #define TRUE 1
19 #define FALSE 0
20 #endif
21
22 static long emmax = 161;
23
24 /*
25 * We define these seek constants if they don't have their
26 * values already defined.
27 */
28 #ifndef SEEK_SET
29 #define SEEK_SET (0)
30 #endif
31 #ifndef SEEK_END
32 #define SEEK_END (2)
33 #endif
34
35 struct empt {
36 struct empt *next;
37 shalfword point;
38 integer x, y;
39 };
40
41 static struct empt **empoints = NULL;
42 boolean emused = FALSE; /* true if em points used on this page */
43 integer emx, emy;
44
45 struct emunit {
46 const char *unit;
47 float factor;
48 };
49 struct emunit emtable[] = {
50 {"pt",72.27},
51 {"pc",72.27/12},
52 {"in",1.0},
53 {"bp",72.0},
54 {"cm",2.54},
55 {"mm",25.4},
56 {"dd",72.27/(1238.0/1157)},
57 {"cc",72.27/12/(1238.0/1157)},
58 {"sp",72.27*65536},
59 {0,0.0}
60 };
61
62 static void emgraph(char *filename, float emwidth, float emheight);
63
64 /* clear the empoints array if necessary */
65 void
emclear(void)66 emclear(void)
67 {
68 int i;
69 if (emused && empoints)
70 for (i=0; i<emmax; i++)
71 empoints[i] = 0;
72 emused = FALSE;
73 }
74
75 /* put an empoint into the empoints array */
76 static struct empt *
emptput(shalfword point,integer x,integer y)77 emptput(shalfword point, integer x, integer y)
78 {
79 struct empt *p;
80 int start;
81
82 emused = TRUE;
83 start = point % emmax;
84 p = empoints[start];
85 while ( p ) {
86 if ( p->point == point )
87 break;
88 p = p->next;
89 }
90 if (p == 0) {
91 p = (struct empt *)mymalloc(sizeof(struct empt));
92 p->next = empoints[start];
93 empoints[start] = p;
94 }
95 p->point = point;
96 p->x = x;
97 p->y = y;
98 return(p);
99 }
100
101 /* get an empoint from the empoints array */
102 static struct empt *
emptget(shalfword point)103 emptget(shalfword point)
104 {
105 struct empt *p;
106 int start;
107
108 start = point % emmax;
109 if (emused == TRUE) {
110 p = empoints[start];
111 while (p) {
112 if (p->point == point)
113 return p;
114 p = p->next;
115 }
116 }
117 sprintf(errbuf,"!em: point %d not defined",point);
118 specerror(errbuf);
119 return(NULL); /* never returns due to error */
120 }
121
122
123 /* convert width into dpi units */
124 static float
emunits(float width,char * unit)125 emunits(float width, char *unit)
126 {
127 struct emunit *p;
128 for (p=emtable; p->unit; p++) {
129 if (strcmp(p->unit,unit)==0)
130 return( width * actualdpi / p->factor );
131 }
132 return (-1.0); /* invalid unit */
133 }
134
135 /* The main routine for \special{em:graph ...} called from dospecial.c */
136 /* the line cut parameter is not supported (and is ignored) */
137
138 void
emspecial(char * p)139 emspecial(char *p)
140 {
141 float emwidth, emheight;
142 shalfword empoint1, empoint2;
143 struct empt *empoint;
144 char emunit[30];
145 char emstr[500];
146 char *emp;
147
148 hvpos();
149 for (emp = p+3; *emp && isspace((unsigned char)*emp); emp++); /* skip blanks */
150 if (strncmp(emp, "linewidth", 9) == 0) {
151 /* code for linewidth */
152 for (emp = emp+9; *emp && isspace((unsigned char)*emp); emp++); /* skip blanks */
153 sscanf(emp, "%f%2s", &emwidth, emunit);
154 emwidth = emunits(emwidth,emunit);
155 if (emwidth!=-1.0) {
156 snprintf(emstr, sizeof(emstr), "%.1f setlinewidth", emwidth);
157 cmdout(emstr);
158 #ifdef DEBUG
159 if (dd(D_SPECIAL))
160 fprintf(stderr, "em special: Linewidth set to %.1f dots\n",
161 emwidth);
162 #endif
163 } else {
164 sprintf(errbuf,"Unknown em: special width");
165 specerror(errbuf);
166 }
167 }
168 else if (strncmp(emp, "moveto", 6) == 0) {
169 #ifdef DEBUG
170 if (dd(D_SPECIAL))
171 #ifdef SHORTINT
172 fprintf(stderr, "em special: moveto %ld,%ld\n", hh, vv);
173 #else
174 fprintf(stderr, "em special: moveto %d,%d\n", hh, vv);
175 #endif
176 #endif
177 emx = hh;
178 emy = vv;
179 }
180 else if (strncmp(emp, "lineto", 6) == 0) {
181 #ifdef DEBUG
182 if (dd(D_SPECIAL))
183 #ifdef SHORTINT
184 fprintf(stderr, "em special: lineto %ld,%ld\n", hh, vv);
185 #else
186 fprintf(stderr, "em special: lineto %d,%d\n", hh, vv);
187 #endif
188 #endif
189 cmdout("np");
190 numout(emx);
191 numout(emy);
192 cmdout("a");
193 numout(hh);
194 numout(vv);
195 cmdout("li");
196 cmdout("st");
197 emx = hh;
198 emy = vv;
199 }
200 else if (strncmp(emp, "point", 5) == 0) {
201 if (empoints == NULL) {
202 empoints =
203 (struct empt **)mymalloc((integer)emmax * sizeof(struct empt *));
204 emused = TRUE;
205 emclear();
206 }
207 for (emp = emp+5; *emp && isspace((unsigned char)*emp); emp++); /* skip blanks */
208 empoint1 = (shalfword)atoi(emp);
209 empoint = emptput(empoint1,hh,vv);
210 #ifdef DEBUG
211 if (dd(D_SPECIAL))
212 #ifdef SHORTINT
213 fprintf(stderr, "em special: Point %d is %ld,%ld\n",
214 #else
215 fprintf(stderr, "em special: Point %d is %d,%d\n",
216 #endif
217 empoint->point, empoint->x, empoint->y);
218 #endif
219 }
220 else if (strncmp(emp, "line", 4) == 0) {
221 for (emp = emp+4; *emp && isspace((unsigned char)*emp); emp++); /* skip blanks */
222 empoint1 = (shalfword)atoi(emp);
223 for (; *emp && isdigit((unsigned char)*emp); emp++); /* skip point 1 */
224 if ( *emp && strchr("hvp",*emp)!=0 )
225 emp++; /* skip line cut */
226 for (; *emp && isspace((unsigned char)*emp); emp++); /* skip blanks */
227 if ( *emp && (*emp==',') )
228 emp++; /* skip comma separator */
229 for (; *emp && isspace((unsigned char)*emp); emp++); /* skip blanks */
230 empoint2 = (shalfword)atoi(emp);
231 for (; *emp && isdigit((unsigned char)*emp); emp++); /* skip point 2 */
232 if ( *emp && strchr("hvp",*emp)!=0 )
233 emp++; /* skip line cut */
234 for (; *emp && isspace((unsigned char)*emp); emp++); /* skip blanks */
235 if ( *emp && (*emp==',') )
236 emp++; /* skip comma separator */
237 emwidth = -1.0;
238 emunit[0]='\0';
239 sscanf(emp, "%f%2s", &emwidth, emunit);
240 emwidth = emunits(emwidth,emunit);
241 #ifdef DEBUG
242 if (dd(D_SPECIAL))
243 fprintf(stderr, "em special: Line from point %d to point %d\n",
244 empoint1, empoint2);
245 #endif
246 cmdout("np");
247 if (emwidth!=-1.0) {
248 #ifdef DEBUG
249 if (dd(D_SPECIAL))
250 fprintf(stderr,"em special: Linewidth temporarily set to %.1f dots\n",
251 emwidth);
252 #endif
253 strcpy(emstr,"currentlinewidth");
254 cmdout(emstr);
255 snprintf(emstr, sizeof(emstr), "%.1f setlinewidth", emwidth);
256 cmdout(emstr);
257 }
258 empoint = emptget(empoint1);
259 numout(empoint->x);
260 numout(empoint->y);
261 cmdout("a");
262 empoint = emptget(empoint2);
263 numout(empoint->x);
264 numout(empoint->y);
265 cmdout("li");
266 cmdout("st");
267 if (emwidth!=-1.0) {
268 strcpy(emstr,"setlinewidth");
269 cmdout(emstr);
270 }
271 }
272 else if (strncmp(emp, "message", 7) == 0) {
273 fprintf(stderr, "em message: %s\n", emp+7);
274 }
275 else if (strncmp(emp, "graph", 5) == 0) {
276 int i;
277 for (emp = emp+5; *emp && isspace((unsigned char)*emp); emp++); /* skip blanks */
278 for (i=0; *emp && !isspace((unsigned char)*emp) && !(*emp==','); emp++) {
279 if (strlen(emstr) - 2 >= sizeof(emstr)) {
280 fprintf(stderr, "em:graph: special too long, truncating\n");
281 break;
282 }
283 emstr[i++] = *emp; /* copy filename */
284 }
285 emstr[i] = '\0';
286 /* now get optional width and height */
287 emwidth = emheight = -1.0; /* no dimension is <= 0 */
288 for (; *emp && ( isspace((unsigned char)*emp) || (*emp==',') ); emp++)
289 ; /* skip blanks and comma */
290 if (*emp) {
291 sscanf(emp, "%f%2s", &emwidth, emunit); /* read width */
292 emwidth = emunits(emwidth,emunit); /* convert to pixels */
293 for (; *emp && (*emp=='.'||isdigit((unsigned char)*emp)||isalpha((unsigned char)*emp)); emp++)
294 ; /* skip width dimension */
295 for (; *emp && ( isspace((unsigned char)*emp) || (*emp==',') ); emp++)
296 ; /* skip blanks and comma */
297 if (*emp) {
298 sscanf(emp, "%f%2s", &emheight, emunit); /* read height */
299 emheight = emunits(emheight,emunit)*vactualdpi/actualdpi;
300 }
301 }
302 if (emstr[0]) {
303 emgraph(emstr,emwidth,emheight);
304 }
305 else {
306 fprintf(stderr, "em:graph: no file given\n");
307 }
308 }
309 else {
310 sprintf(errbuf,
311 "Unknown em: command (%s) in \\special will be ignored", p);
312 specerror(errbuf);
313 }
314 return;
315 }
316
317
318 /* em:graph routines */
319
320 /* The graphics routines currently decode 3 types of IBM-PC based graphics */
321 /* files: .pcx, .bmp, .msp. The information on the formats has occasionally */
322 /* been sketchy, and subject to interpretation. I have attempted to implement*/
323 /* these routines to correctly decode the graphics file types mentioned. */
324 /* The compressed .bmp file type has not been tested fully due to a lack of */
325 /* test files. */
326 /* */
327 /* The method of reading in the headers of the binary files is ungainly, but */
328 /* portable. Failure to use a byte by byte read and convert method will */
329 /* result in portability problems. Specifically there is no requirement that */
330 /* elements of a C structure be contiguous, only that they have an ascending */
331 /* order of addresses. */
332 /* */
333 /* The current implementations of the graphics format to postscript */
334 /* conversion are not the most efficient possible. Specifically: color */
335 /* formats are converted to RGB ratios prior to using a simple thresholding */
336 /* method to determine if the postscript bit is to be set. The thresholding */
337 /* method is compabible with that used in emtex's drivers. */
338 /* */
339 /* Please send bug reports relating to the em:graph routines to: */
340 /* maurice@bruce.cs.monash.edu.au */
341 /* */
342 /* My thanks to Russell Lang for his assistance with the implementation of */
343 /* these routines in a manner compatible with dvips and for the optimization */
344 /* of some routines. */
345 /* */
346 /* Maurice Castro */
347 /* 8 Oct 92 */
348
349 /* Routines to read binary numbers from IBM PC file types */
350 static integer
readinteger(FILE * f)351 readinteger(FILE *f)
352 {
353 integer i;
354 int r;
355
356 i = 0;
357 for (r = 0; r < 4; r++)
358 {
359 i = i | ( ((integer) fgetc(f)) << (8*r) );
360 }
361 return(i);
362 }
363
364 static halfword
readhalfword(FILE * f)365 readhalfword(FILE *f)
366 {
367 halfword i;
368 int r;
369
370 i = 0;
371 for (r = 0; r < 2; r++)
372 {
373 i = i | ( ((halfword) fgetc(f)) << (8*r) );
374 }
375 return(i);
376 }
377
378 #define readquarterword(f) ((unsigned char)fgetc(f))
379 #define tobyte(x) ((x/8) + (x%8 ? 1 : 0))
380
381 /* These routines will decode PCX files produced by Microsoft
382 * Windows Paint. Other programs may not produce files which
383 * will be successfully decoded, most notably version 1.xx of
384 * PC Paint. */
385
386 /*
387 * Type declarations. integer must be a 32-bit signed; shalfword must
388 * be a sixteen-bit signed; halfword must be a sixteen-bit unsigned;
389 * quarterword must be an eight-bit unsigned.
390 */
391 typedef struct
392 {
393 quarterword man;
394 quarterword ver;
395 quarterword enc;
396 quarterword bitperpix;
397 halfword xmin;
398 halfword ymin;
399 halfword xmax;
400 halfword ymax;
401 halfword hres;
402 halfword vres;
403 quarterword pal[48];
404 quarterword reserved;
405 quarterword colorplanes;
406 halfword byteperline;
407 halfword paltype;
408 quarterword fill[58];
409 } PCXHEAD;
410
411 static int
PCXreadhead(FILE * pcxf,PCXHEAD * pcxh)412 PCXreadhead(FILE *pcxf, PCXHEAD *pcxh)
413 {
414 pcxh->man = readquarterword(pcxf);
415 pcxh->ver = readquarterword(pcxf);
416 pcxh->enc = readquarterword(pcxf);
417 pcxh->bitperpix = readquarterword(pcxf);
418 pcxh->xmin = readhalfword(pcxf);
419 pcxh->ymin = readhalfword(pcxf);
420 pcxh->xmax = readhalfword(pcxf);
421 pcxh->ymax = readhalfword(pcxf);
422 pcxh->hres = readhalfword(pcxf);
423 pcxh->vres = readhalfword(pcxf);
424 fread(pcxh->pal, 1, 48, pcxf);
425 pcxh->reserved = readquarterword(pcxf);
426 pcxh->colorplanes = readquarterword(pcxf);
427 pcxh->byteperline = readhalfword(pcxf);
428 pcxh->paltype = readhalfword(pcxf);
429 fread(pcxh->fill, 1, 58, pcxf);
430
431 if (feof(pcxf))
432 return(0); /* fail */
433 if (pcxh->man != 0x0a)
434 return(0); /* fail */
435 if (pcxh->enc != 0x1)
436 return(0); /* fail */
437 return(1); /* success */
438 }
439
440 static int
PCXreadline(FILE * pcxf,unsigned char * pcxbuf,unsigned int byteperline)441 PCXreadline(FILE *pcxf,
442 unsigned char *pcxbuf,
443 unsigned int byteperline)
444 {
445 int n;
446 int c;
447 int i;
448
449 n = 0;
450 memset(pcxbuf,0,byteperline);
451 do {
452 c = fgetc(pcxf);
453 if ((c & 0xc0) == 0xc0) {
454 i = c & 0x3f;
455 c = fgetc(pcxf) & 0xff;
456 while ((i--) && (n < byteperline)) pcxbuf[n++] = c;
457 }
458 else {
459 pcxbuf[n++] = c & 0xff;
460 }
461 } while (n < byteperline);
462 if (c==EOF) n=0;
463 return(n);
464 }
465
466 static void
PCXgetpalette(FILE * pcxf,PCXHEAD * pcxh,unsigned char * r,unsigned char * g,unsigned char * b)467 PCXgetpalette(FILE *pcxf, PCXHEAD *pcxh,
468 unsigned char *r,
469 unsigned char *g,
470 unsigned char *b)
471 {
472 int i;
473
474 /* clear palette */
475 for (i=0; i < 256; i++) {
476 r[i] = 0;
477 g[i] = 0;
478 b[i] = 0;
479 }
480
481 switch (pcxh->ver) {
482 case 0:
483 /* version 2.5 of PC Paint Brush */
484 for (i=0; i < 16; i++) {
485 r[i] = pcxh->pal[i*3];
486 g[i] = pcxh->pal[i*3+1];
487 b[i] = pcxh->pal[i*3+2];
488 }
489 break;
490 case 2:
491 if (pcxh->colorplanes != 1) {
492 /* version 2.8 of PC Paint Brush with valid Palette */
493 for (i=0; i < 16; i++) {
494 r[i] = pcxh->pal[i*3];
495 g[i] = pcxh->pal[i*3+1];
496 b[i] = pcxh->pal[i*3+2];
497 }
498 }
499 else { /* not sure if this is correct - rjl */
500 /* mono palette */
501 r[0] = 0x00; g[0] = 0x00; b[0] = 0x00;
502 r[1] = 0xff; g[1] = 0xff; b[1] = 0xff;
503 }
504 break;
505 case 3:
506 /* version 2.8 of PC Paint Brush with no valid Palette */
507 /* either mono or default */
508
509 if (pcxh->colorplanes != 1) {
510 /* Color default palette - should be checked */
511 r[0] = 0x00; g[0] = 0x00; b[0] = 0x00;
512 r[1] = 0x80; g[1] = 0x00; b[1] = 0x00;
513 r[2] = 0x00; g[2] = 0x80; b[2] = 0x00;
514 r[3] = 0x80; g[3] = 0x80; b[3] = 0x00;
515 r[4] = 0x00; g[4] = 0x00; b[4] = 0x80;
516 r[5] = 0x80; g[5] = 0x00; b[5] = 0x80;
517 r[6] = 0x00; g[6] = 0x80; b[6] = 0x80;
518 r[7] = 0x80; g[7] = 0x80; b[7] = 0x80;
519 r[8] = 0xc0; g[8] = 0xc0; b[8] = 0xc0;
520 r[9] = 0xff; g[9] = 0x00; b[9] = 0x00;
521 r[10] = 0x00; g[10] = 0xff; b[10] = 0x00;
522 r[11] = 0xff; g[11] = 0xff; b[11] = 0x00;
523 r[12] = 0x00; g[12] = 0x00; b[12] = 0xff;
524 r[13] = 0xff; g[13] = 0x00; b[13] = 0xff;
525 r[14] = 0x00; g[14] = 0xff; b[14] = 0xff;
526 r[15] = 0xff; g[15] = 0xff; b[15] = 0xff;
527 }
528 else {
529 /* mono palette */
530 r[0] = 0x00; g[0] = 0x00; b[0] = 0x00;
531 r[1] = 0xff; g[1] = 0xff; b[1] = 0xff;
532 }
533 break;
534 case 5:
535 default:
536 /* version 3.0 of PC Paint Brush or Better */
537 fseek(pcxf, -769L, SEEK_END);
538 /* if signature byte is correct then read the palette */
539 /* otherwise copy the existing palette */
540 if (fgetc(pcxf) == 12) {
541 for (i=0; i < 256; i++) {
542 r[i] = fgetc(pcxf);
543 g[i] = fgetc(pcxf);
544 b[i] = fgetc(pcxf);
545 }
546 }
547 else {
548 for (i=0; i < 16; i++) {
549 r[i] = pcxh->pal[i*3];
550 g[i] = pcxh->pal[i*3+1];
551 b[i] = pcxh->pal[i*3+2];
552 }
553 }
554 break;
555 }
556 }
557
558 static void
PCXshowpicture(FILE * pcxf,int wide,int high,int bytes,int cp,int bp,unsigned char * r,unsigned char * g,unsigned char * b)559 PCXshowpicture(FILE *pcxf, int wide, int high, int bytes,
560 int cp, int bp, unsigned char *r,
561 unsigned char *g, unsigned char *b)
562 {
563 int x;
564 int y;
565 int c;
566 unsigned char *rowa[4]; /* row array */
567 unsigned char *row;
568 int p;
569 unsigned char *pshexa;
570 int xdiv, xmod, xbit;
571 int width;
572 int bytewidth;
573
574 bytewidth = tobyte(wide);
575 width = bytewidth * 8;
576
577 /* output the postscript image size preamble */
578 cmdout("/picstr ");
579 numout((integer)tobyte(wide));
580 cmdout("string def");
581
582 numout((integer)width);
583 numout((integer)high);
584 numout((integer)1);
585 newline();
586
587 cmdout("[");
588 numout((integer)width);
589 numout((integer)0);
590 numout((integer)0);
591 numout((integer)-high);
592 numout((integer)0);
593 numout((integer)0);
594 cmdout("]");
595
596 nlcmdout("{currentfile picstr readhexstring pop} image");
597
598 /* allocate the postscript hex output array */
599 pshexa = (unsigned char *) mymalloc((integer)bytewidth);
600
601 /* make sure that you start at the right point in the file */
602 fseek(pcxf, (long) sizeof(PCXHEAD), SEEK_SET);
603
604 /* malloc the lines */
605 row = (unsigned char *) mymalloc((integer)bytes * cp);
606 for (c = 0; c < cp; c++)
607 rowa[c] = row + bytes*c;
608
609 for (y = 0; y < high; y++) {
610 /* clear the postscript hex array */
611 memset(pshexa,0xff,bytewidth);
612
613 /* read in all the color planes for a row of pixels */
614 PCXreadline(pcxf, rowa[0], bytes*cp);
615
616 /* process each pixel */
617 for (x = 0; x < wide; x++) {
618 /* build up the pixel value from color plane entries */
619 p = 0;
620 xdiv = x>>3;
621 xmod = 7 - (x&7);
622 xbit = 1 << xmod;
623 switch(bp) {
624 case 1:
625 for (c = 0; c < cp; c++) {
626 row = rowa[c];
627 p |= ( (unsigned)(row[xdiv] & xbit) >> xmod ) << c;
628 }
629 break;
630 case 4: /* untested - most programs use 1 bit/pixel, 4 planes */
631 row = rowa[0]; /* assume single plane */
632 p = ( x & 1 ? row[xdiv] : row[xdiv]>>4 ) & 0x0f;
633 break;
634 case 8:
635 row = rowa[0]; /* assume single plane */
636 p = (unsigned) (row[x]);
637 break;
638 default:
639 fprintf(stderr, "em:graph: Unable to Decode this PCX file\n");
640 return;
641 }
642 if ((r[p] < 0xff) || (g[p] < 0xff) || (b[p] < 0xff))
643 pshexa[xdiv] &= (~xbit);
644 else
645 pshexa[xdiv] |= xbit;
646 }
647 newline();
648 mhexout(pshexa,(long)bytewidth);
649 }
650 free(pshexa);
651 free(rowa[0]);
652 }
653
654 static void
imagehead(char * filename,int wide,int high,float emwidth,float emheight)655 imagehead(char *filename, int wide, int high,
656 float emwidth, float emheight)
657 {
658 char *fullname = NULL, *name;
659 if (!quiet) {
660 #ifdef KPATHSEA
661 fullname = (char *)kpse_find_file (filename, pictpath, 0);
662 #endif
663 if (!fullname)
664 name = filename;
665 else
666 name = fullname;
667 if (strlen(name) + prettycolumn > STDOUTSIZE) {
668 fprintf(stderr,"\n");
669 prettycolumn = 0;
670 }
671 fprintf(stderr,"<%s",name);
672 fflush(stderr);
673 prettycolumn += 2+strlen(name);
674 if (fullname) free (fullname);
675 }
676 hvpos();
677 nlcmdout("@beginspecial @setspecial");
678 if (!disablecomments) {
679 cmdout("%%BeginDocument: em:graph");
680 cmdout(filename);
681 newline();
682 }
683 /* set the image size */
684 if (emwidth <= 0.0) emwidth = (float)wide;
685 if (emheight <= 0.0) emheight = (float)high;
686 floatout(emwidth*72/actualdpi);
687 floatout(emheight*72/vactualdpi);
688 newline();
689 cmdout("scale");
690 #ifdef DEBUG
691 if (dd(D_SPECIAL)) {
692 fprintf(stderr,
693 "\nem:graph: %s width %d pixels scaled to %.1f pixels\n",
694 filename, wide, emwidth);
695 fprintf(stderr,
696 "em:graph: %s height %d pixels scaled to %.1f pixels\n",
697 filename, high, emheight);
698 }
699 #endif
700 }
701
702 static void
imagetail(void)703 imagetail(void)
704 {
705 if (!disablecomments) {
706 fprintf(bitfile, "\n%%%%EndDocument\n");
707 linepos = 0;
708 }
709 nlcmdout("@endspecial");
710 if (!quiet) {
711 fprintf(stderr,">");
712 fflush(stderr);
713 }
714 }
715
716 static void
pcxgraph(FILE * pcxf,char * filename,float emwidth,float emheight)717 pcxgraph(FILE *pcxf, char *filename,
718 float emwidth, float emheight /* dimension in pixels */ )
719 {
720 PCXHEAD pcxh;
721 unsigned char red[256];
722 unsigned char green[256];
723 unsigned char blue[256];
724 int wide;
725 int high;
726 int bytes;
727 int cp;
728 int bpp;
729
730 if (!PCXreadhead(pcxf, &pcxh)) {
731 sprintf(errbuf,"em:graph: Unable to Read Valid PCX Header");
732 specerror(errbuf);
733 }
734 PCXgetpalette(pcxf, &pcxh, red, green, blue);
735
736 /* picture size calculation */
737 wide = (pcxh.xmax - pcxh.xmin) + 1;
738 high = (pcxh.ymax - pcxh.ymin) + 1;
739 bytes = pcxh.byteperline;
740 cp = pcxh.colorplanes;
741 bpp = pcxh.bitperpix;
742
743 imagehead(filename,wide,high,emwidth,emheight);
744 PCXshowpicture(pcxf, wide, high, bytes, cp, bpp, red, green, blue);
745 imagetail();
746 }
747
748 /* Microsoft Paint routines */
749 struct wpnt_1 {
750 quarterword id[4];
751 halfword width;
752 halfword high;
753 halfword x_asp;
754 halfword y_asp;
755 halfword x_asp_prn;
756 halfword y_asp_prn;
757 halfword width_prn;
758 halfword high_prn;
759 integer chk_sum;
760 halfword chk_head;
761 };
762
763 #define WPAINT_1 1
764 #define WPAINT_2 2
765
766 static void
MSP_2_ps(FILE * f,int wide,int high)767 MSP_2_ps(FILE *f, int wide, int high)
768 {
769 char *line;
770 char *l;
771 int i;
772 int j;
773 unsigned char a, b, c;
774 int d;
775 int width;
776 halfword *linelen;
777
778 /* an undocumented format - based on a type of run length encoding */
779 /* the format is made up of a list of line lengths, followed by a set */
780 /* of lines. Each line is made up of 2 types of entries: */
781 /* 1) A 3 term entry (0 a b): the byte b is repeated a times */
782 /* 2) A variable length entry (a xxxx....xxxx): a bytes are read */
783 /* from the file. */
784 /* These entries are combined to build up a line */
785
786 width = tobyte(wide)*8;
787
788 /* output the postscript image size preamble */
789 cmdout("/picstr");
790 numout((integer)tobyte(wide));
791 cmdout("string def");
792
793 numout((integer)width);
794 numout((integer)high);
795 numout((integer)1);
796
797 cmdout("[");
798 numout((integer)width);
799 numout((integer)0);
800 numout((integer)0);
801 numout((integer)-high);
802 numout((integer)0);
803 numout((integer)0);
804 cmdout("]");
805
806 nlcmdout("{currentfile picstr readhexstring pop} image");
807
808 fseek(f, 32, SEEK_SET);
809
810 /* read in the table of line lengths */
811 linelen = (halfword *) mymalloc((integer)sizeof(halfword) * high);
812 for (i = 0; i < high; i++) {
813 linelen[i] = readhalfword(f);
814 if (feof(f))
815 return;
816 }
817
818 line = (char *) mymalloc((integer)tobyte(wide));
819 for (i = 0; i < high; i++) {
820 memset(line, 0xff, tobyte(wide));
821 l = line;
822 if (linelen[i] != 0) {
823 d = linelen[i];
824 while (d) {
825 a = fgetc(f);
826 d--;
827 if (a == 0) {
828 b = fgetc(f);
829 c = fgetc(f);
830 d -= 2;
831 for (j = 0; j < b; j++)
832 *l++ = c;
833 }
834 else {
835 for (j = 0; j < a; j++)
836 *l++ = fgetc(f);
837 d -= j;
838 }
839 }
840 }
841 newline();
842 mhexout((unsigned char *) line,(long)tobyte(wide));
843 }
844 free(linelen);
845 free(line);
846 }
847
848 static void
MSP_1_ps(FILE * f,int wide,int high)849 MSP_1_ps(FILE *f, int wide, int high)
850 {
851 char *line;
852 int i;
853 int width;
854
855 width = tobyte(wide)*8;
856 /* an partly documented format - see The PC Sourcebook */
857 /* the format is made up of a simple bitmap. */
858
859 /* output the postscript image size preamble */
860 cmdout("/picstr");
861 numout((integer)tobyte(wide));
862 cmdout("string def");
863
864 numout((integer)width);
865 numout((integer)high);
866 numout((integer)1);
867
868 cmdout("[");
869 numout((integer)width);
870 numout((integer)0);
871 numout((integer)0);
872 numout((integer)-high);
873 numout((integer)0);
874 numout((integer)0);
875 cmdout("]");
876
877 nlcmdout("{currentfile picstr readhexstring pop} image");
878
879 fseek(f, 32, SEEK_SET);
880
881 line = (char *) mymalloc((integer)tobyte(wide));
882 for (i = 0; i < high; i++) {
883 fread(line, 1, tobyte(wide), f);
884 newline();
885 mhexout((unsigned char *) line,(long)tobyte(wide));
886 }
887 free(line);
888 }
889
890
891 static void
mspgraph(FILE * f,char * filename,float emwidth,float emheight)892 mspgraph(FILE *f, char *filename, float emwidth, float emheight)
893 {
894 struct wpnt_1 head;
895 int paint_type = 0;
896
897 /* read the header of the file and figure out what it is */
898 fread(head.id, 1, 4, f);
899 head.width = readhalfword(f);
900 head.high = readhalfword(f);
901 head.x_asp = readhalfword(f);
902 head.y_asp = readhalfword(f);
903 head.x_asp_prn = readhalfword(f);
904 head.y_asp_prn = readhalfword(f);
905 head.width_prn = readhalfword(f);
906 head.high_prn = readhalfword(f);
907 head.chk_sum = readinteger(f);
908 head.chk_head = readhalfword(f);
909
910 if (feof(f)) {
911 fprintf(stderr, "em:graph: Unable to Read Valid MSP Header\n");
912 return;
913 }
914
915 /* check the id bytes */
916 if (!memcmp(head.id, "DanM", 4))
917 paint_type = WPAINT_1;
918 if (!memcmp(head.id, "LinS", 4))
919 paint_type = WPAINT_2;
920
921
922 imagehead(filename,head.width,head.high,emwidth,emheight);
923 switch (paint_type) {
924 case WPAINT_1:
925 MSP_1_ps(f, head.width, head.high);
926 break;
927 case WPAINT_2:
928 MSP_2_ps(f, head.width, head.high);
929 break;
930 default:
931 sprintf(errbuf, "em:graph: Unknown MSP File Type");
932 specerror(errbuf);
933 }
934 imagetail();
935 }
936
937 /* ------------------------------------------------------------------------ */
938 /* .BMP file structures */
939 struct rgbquad {
940 char blue;
941 char green;
942 char red;
943 char res;
944 };
945
946 struct bitmapinfoheader {
947 integer size;
948 integer width;
949 integer height;
950 halfword planes; /* must be set to 1 */
951 halfword bitcount; /* 1, 4, 8 or 24 */
952 integer compression;
953 integer sizeimage;
954 integer xpelspermeter;
955 integer ypelspermeter;
956 integer clrused;
957 integer clrimportant;
958 };
959
960 #ifdef WIN32
961 #undef RGB
962 #endif
963
964 /* constants for the compression field */
965 #define RGB 0L
966 #define RLE8 1L
967 #define RLE4 2L
968
969 struct bitmapfileheader {
970 char type[2];
971 integer size;
972 halfword reserved1;
973 halfword reserved2;
974 integer offbits;
975 };
976
977 static void
rgbread(FILE * f,int w,int b,char * s)978 rgbread(FILE *f, int w, int b, char *s)
979 {
980 int i;
981
982 /* set the line to white */
983 memset(s, 0xff, ((w*b)/8)+1);
984
985 /* read in all the full bytes */
986 for (i = 0; i < (w * b) / 8; i++)
987 *s++ = fgetc(f);
988
989 /* read in a partly filled byte */
990 if ((w * b) % 8) {
991 i++;
992 *s++ = fgetc(f);
993 }
994
995 /* check that we are on a 32 bit boundary; otherwise align */
996 while (i % 4 != 0) {
997 fgetc(f);
998 i++;
999 }
1000 }
1001
1002 unsigned rle_dx = 0; /* delta command horizontal offset */
1003 unsigned rle_dy = 0; /* delta command vertical offset */
1004
1005 /* checked against output from Borland Resource Workshop */
1006 static void
rle4read(FILE * f,int w,int b,char * s)1007 rle4read(FILE *f, int w, int b, char *s)
1008 {
1009 int i;
1010 int limit;
1011 int ch;
1012 unsigned cnt;
1013 int hi;
1014
1015 limit = (w*b)/8;
1016 i = 0;
1017 hi = TRUE;
1018 /* set the line to white */
1019 memset(s, 0xff, limit+1);
1020
1021 if (rle_dy) {
1022 rle_dy--;
1023 return;
1024 }
1025
1026 if (rle_dx) {
1027 for (; rle_dx>1; rle_dx-=2) {
1028 s++;
1029 i++;
1030 }
1031 if (rle_dx)
1032 hi = FALSE;
1033 }
1034
1035 while (i<=limit) {
1036 cnt = fgetc(f);
1037 ch = fgetc(f);
1038 if (cnt == 0) { /* special operation */
1039 switch(ch) {
1040 case 0: /* EOL */
1041 return; /* this is our way out */
1042 case 1: /* End of Bitmap */
1043 return;
1044 case 2: /* Delta */ /* untested */
1045 rle_dx = fgetc(f) + i*2 + (hi ? 1 : 0);
1046 rle_dy = fgetc(f);
1047 return;
1048 default: /* next cnt bytes are absolute */
1049 /* must be aligned on word boundary */
1050 if (!hi)
1051 fprintf(stderr,"em:graph: RLE4 absolute is not byte aligned\n");
1052 for (cnt = ch; cnt>0 && i<=limit; cnt-=2) {
1053 i++;
1054 *s++ = fgetc(f);
1055 }
1056 if (ch % 4) /* word align file */
1057 fgetc(f);
1058 }
1059 }
1060 else { /* cnt is repeat count */
1061 if (!hi) { /* we are about to place the low 4 bits */
1062 ch = ((ch>>4)&0x0f) | ((ch<<4)&0xf0); /* swap order */
1063 i++;
1064 *s = (*s & 0xf0) | (ch & 0x0f);
1065 s++;
1066 hi = TRUE;
1067 cnt--;
1068 }
1069 /* we are about to place the high 4 bits */
1070 for (; cnt>1 && i<=limit; cnt-=2) { /* place the whole bytes */
1071 i++;
1072 *s++ = ch;
1073 }
1074 if (cnt) { /* place the partial byte */
1075 *s = (*s & 0x0f) | (ch & 0xf0);
1076 hi = FALSE;
1077 }
1078 }
1079 }
1080 }
1081
1082 /* untested */
1083 static void
rle8read(FILE * f,int w,int b,char * s)1084 rle8read(FILE *f, int w, int b, char *s)
1085 {
1086 int i;
1087 int limit;
1088 int ch;
1089 unsigned cnt;
1090
1091 limit = (w*b)/8;
1092 i = 0;
1093 /* set the line to white */
1094 memset(s, 0xff, limit+1);
1095
1096 if (rle_dy) {
1097 rle_dy--;
1098 return;
1099 }
1100
1101 if (rle_dx) {
1102 for (; rle_dx > 0; rle_dx--) {
1103 s++;
1104 i++;
1105 }
1106 }
1107
1108 while (i<=limit) {
1109 cnt = fgetc(f);
1110 ch = fgetc(f);
1111 if (cnt == 0) { /* special operation */
1112 switch(ch) {
1113 case 0: /* EOL */
1114 return; /* this is our way out */
1115 case 1: /* End of Bitmap */
1116 return;
1117 case 2: /* Delta */ /* untested */
1118 rle_dx = fgetc(f) + i;
1119 rle_dy = fgetc(f);
1120 return;
1121 default: /* next cnt bytes are absolute */
1122 for (cnt = ch; cnt>0 && i<=limit; cnt--) {
1123 i++;
1124 *s++ = fgetc(f);
1125 }
1126 if (ch % 2) /* word align file */
1127 fgetc(f);
1128 }
1129 }
1130 else { /* cnt is repeat count */
1131 for (; cnt>0 && i<=limit; cnt--) {
1132 i++;
1133 *s++ = ch;
1134 }
1135 }
1136 }
1137 }
1138
1139 static void
bmpgraph(FILE * f,char * filename,float emwidth,float emheight)1140 bmpgraph(FILE *f, char *filename, float emwidth, float emheight)
1141 {
1142 struct bitmapfileheader bmfh;
1143 struct bitmapinfoheader bmih;
1144
1145 unsigned char isblack[256];
1146 unsigned char rr;
1147 unsigned char gg;
1148 unsigned char bb;
1149 unsigned char c = 0;
1150
1151 char *line;
1152 char *pshexa;
1153
1154 int clrtablesize;
1155 int i;
1156 int j;
1157
1158 unsigned char omask;
1159 int oroll;
1160
1161 unsigned char emask = 0;
1162 integer ewidth = 0;
1163 int isOS2;
1164
1165 /* read the header of the file */
1166 fread(bmfh.type, 1, 2, f);
1167 bmfh.size = readinteger(f);
1168 bmfh.reserved1 = readhalfword(f);
1169 bmfh.reserved2 = readhalfword(f);
1170 bmfh.offbits = readinteger(f);
1171 if (feof(f)) {
1172 sprintf(errbuf, "em:graph: Unable to Read Valid BMP Header\n");
1173 specerror(errbuf);
1174 return;
1175 }
1176
1177 bmih.size = readinteger(f);
1178 if (bmih.size == 12) { /* OS2 bitmap */
1179 isOS2 = TRUE;
1180 bmih.width = readhalfword(f);
1181 bmih.height = readhalfword(f);
1182 bmih.planes = readhalfword(f);
1183 bmih.bitcount = readhalfword(f);
1184 /* the following don't exist in OS2 format so fill with 0's */
1185 bmih.compression = RGB;
1186 bmih.sizeimage = 0;
1187 bmih.xpelspermeter = 0;
1188 bmih.ypelspermeter = 0;
1189 bmih.clrused = 0;
1190 bmih.clrimportant = 0;
1191 }
1192 else { /* Windows bitmap */
1193 isOS2 = FALSE;
1194 bmih.width = readinteger(f);
1195 bmih.height = readinteger(f);
1196 bmih.planes = readhalfword(f);
1197 bmih.bitcount = readhalfword(f);
1198 bmih.compression = readinteger(f);
1199 bmih.sizeimage = readinteger(f);
1200 bmih.xpelspermeter = readinteger(f);
1201 bmih.ypelspermeter = readinteger(f);
1202 bmih.clrused = readinteger(f);
1203 bmih.clrimportant = readinteger(f);
1204 }
1205
1206 if (feof(f)) {
1207 sprintf(errbuf, "em:graph: Unable to Read Valid BMP Info");
1208 specerror(errbuf);
1209 return;
1210 }
1211
1212 if (memcmp(bmfh.type, "BM", 2)) {
1213 sprintf(errbuf, "em:graph: Unknown BMP File Type");
1214 specerror(errbuf);
1215 return;
1216 }
1217
1218 if ((bmih.compression == RLE4) && (bmih.bitcount != 4)) {
1219 sprintf(errbuf, "em:graph: Can't do BMP RLE4 with %d bits per pixel",
1220 bmih.bitcount);
1221 specerror(errbuf);
1222 return;
1223 }
1224
1225 if ((bmih.compression == RLE8) && (bmih.bitcount != 8)) {
1226 sprintf(errbuf, "em:graph: Can't do BMP RLE8 with %d bits per pixel\n",
1227 bmih.bitcount);
1228 specerror(errbuf);
1229 return;
1230 }
1231
1232 imagehead(filename,(int)bmih.width,(int)bmih.height,emwidth,emheight);
1233
1234 /* determine the size of the color table to read */
1235 clrtablesize = 0;
1236 if (bmih.clrused == 0) {
1237 switch (bmih.bitcount) {
1238 case 1:
1239 clrtablesize = 2;
1240 break;
1241 case 4:
1242 clrtablesize = 16;
1243 break;
1244 case 8:
1245 clrtablesize = 256;
1246 break;
1247 case 24:
1248 break;
1249 }
1250 }
1251 else
1252 clrtablesize = bmih.clrused;
1253
1254 /* read in the color table */
1255 for (i = 0; i < clrtablesize; i++) {
1256 bb = fgetc(f);
1257 gg = fgetc(f);
1258 rr = fgetc(f);
1259 isblack[i] = (rr < 0xff) || (gg < 0xff) || (bb < 0xff);
1260 if (!isOS2)
1261 (void) fgetc(f);
1262 }
1263
1264 line = (char *) mymalloc((integer)((bmih.width * bmih.bitcount) / 8) + 1);
1265 pshexa = (char *) mymalloc((integer)tobyte(bmih.width));
1266
1267 /* output the postscript image size preamble */
1268 cmdout("/picstr");
1269 numout((integer)tobyte(bmih.width));
1270 cmdout("string def");
1271
1272 numout((integer)bmih.width);
1273 numout((integer)bmih.height);
1274 numout((integer)1);
1275
1276 cmdout("[");
1277 numout((integer)bmih.width);
1278 numout((integer)0);
1279 numout((integer)0);
1280 numout((integer)bmih.height);
1281 numout((integer)0);
1282 numout((integer)bmih.height);
1283 cmdout("]");
1284
1285 nlcmdout("{currentfile picstr readhexstring pop} image");
1286
1287 if (bmih.bitcount == 1) {
1288 if (bmih.width%8)
1289 emask = (1<<(8-(bmih.width%8)))-1; /* mask for edge of bitmap */
1290 else
1291 emask = 0;
1292 ewidth = tobyte(bmih.width);
1293 }
1294
1295 /* read in all the lines of the file */
1296 for (i = 0; i < bmih.height; i++) {
1297 memset(pshexa,0xff,tobyte(bmih.width));
1298 switch (bmih.compression) {
1299 case RGB:
1300 rgbread(f, (int) bmih.width, (int) bmih.bitcount, line);
1301 break;
1302 case RLE4:
1303 rle4read(f, (int) bmih.width, (int) bmih.bitcount, line);
1304 break;
1305 case RLE8:
1306 rle8read(f, (int) bmih.width, (int) bmih.bitcount, line);
1307 break;
1308 default:
1309 sprintf(errbuf,"em:graph: Unknown BMP compression\n");
1310 specerror(errbuf);
1311 return;
1312 }
1313
1314 omask = 0x80;
1315 oroll = 7;
1316
1317 if (bmih.bitcount == 1) {
1318 if (isblack[0])
1319 for (j = 0; j < ewidth; j++)
1320 pshexa[j] = line[j];
1321 else
1322 for (j = 0; j < ewidth; j++)
1323 pshexa[j] = ~line[j];
1324 pshexa[ewidth-1] |= emask;
1325 }
1326 else {
1327 for (j = 0; j < bmih.width; j++) {
1328 switch (bmih.bitcount) {
1329 case 4:
1330 c = line[j>>1];
1331 if (!(j&1))
1332 c >>= 4;
1333 c = isblack[ c & 0x0f ];
1334 break;
1335 case 8:
1336 c = isblack[ (int)(line[j]) ];
1337 break;
1338 case 24:
1339 rr = line[j*3];
1340 gg = line[j*3+1];
1341 bb = line[j*3+2];
1342 c = (rr < 0xff) || (gg < 0xff) || (bb < 0xff);
1343 break;
1344 }
1345 if (c)
1346 pshexa[j/8] &= ~omask;
1347 else
1348 pshexa[j/8] |= omask;
1349 oroll--;
1350 omask >>= 1;
1351 if (oroll < 0) {
1352 omask = 0x80;
1353 oroll = 7;
1354 }
1355 }
1356 }
1357 newline();
1358 mhexout((unsigned char *) pshexa,(long)tobyte(bmih.width));
1359 }
1360 imagetail();
1361 free(pshexa);
1362 free(line);
1363 }
1364 /* ------------------------------------------------------------------------ */
1365
1366 #define PCX 0
1367 #define MSP 1
1368 #define BMP 2
1369 const char *extarr[]=
1370 { ".pcx", ".msp", ".bmp", NULL };
1371
1372 static void
emgraph(char * filename,float emwidth,float emheight)1373 emgraph(char *filename, float emwidth, float emheight)
1374 {
1375 char fname[500];
1376 int filetype;
1377 FILE *f;
1378 char *env;
1379 char id[4];
1380 int i;
1381
1382 strcpy(fname, filename);
1383
1384 /* find the file */
1385 f = search(figpath, fname, READBIN);
1386 if (f == (FILE *)NULL) {
1387 if ( (env = getenv("DVIDRVGRAPH")) != NULL )
1388 #ifdef KPATHSEA
1389 f = search((kpse_file_format_type)env,filename,READBIN);
1390 #else
1391 f = search(env,filename,READBIN);
1392 #endif
1393 }
1394 /* if still haven't found it try adding extensions */
1395 if (f == (FILE *)NULL) {
1396 i = 0;
1397 while (extarr[i] != NULL) {
1398 strcpy(fname, filename);
1399 strcat(fname, extarr[i]);
1400 f = search(figpath, fname, READBIN);
1401 if (f == (FILE *)NULL) {
1402 if ( (env = getenv("DVIDRVGRAPH")) != NULL )
1403 #ifdef KPATHSEA
1404 f = search((kpse_file_format_type)env,filename,READBIN);
1405 #else
1406 f = search(env,filename,READBIN);
1407 #endif
1408 }
1409 if (f != (FILE *)NULL)
1410 break;
1411 i++;
1412 }
1413 }
1414
1415 filetype = -1;
1416 if (f != (FILE *)NULL) {
1417 for (i=0; i<4; i++) {
1418 id[i] = readquarterword(f);
1419 }
1420 if ( (id[0] == 0x0a) && (id[2] == 0x01) )
1421 filetype = PCX;
1422 if (!memcmp(id, "DanM", 4))
1423 filetype = MSP;
1424 if (!memcmp(id, "LinS", 4))
1425 filetype = MSP;
1426 if (!memcmp(id, "BM", 2))
1427 filetype = BMP;
1428 fseek(f, 0L, SEEK_SET);
1429 }
1430
1431 switch (filetype) {
1432 case PCX:
1433 pcxgraph(f, fname, emwidth, emheight);
1434 break;
1435 case MSP:
1436 mspgraph(f, fname, emwidth, emheight);
1437 break;
1438 case BMP:
1439 bmpgraph(f, fname, emwidth, emheight);
1440 break;
1441 default:
1442 snprintf(fname, sizeof(fname),
1443 "em:graph: %s: File not found", filename);
1444 error(fname);
1445 }
1446 if (f != (FILE *)NULL)
1447 close_file(f);
1448 }
1449
1450 #else
1451 void
emspecial(char * p)1452 emspecial(char *p)
1453 {
1454 sprintf(errbuf,"emTeX specials not compiled in this version");
1455 specerror(errbuf);
1456 }
1457 #endif /* EMTEX */
1458