1 /*
2  * Raster3D V3.0
3  * local.c
4  *
5  * Output from render.f is performed by calls to routine LOCAL.
6  *
7  * This version of local.c supports 6 output modes, 5 of which
8  * are controlled by conditional compilation directives.
9  *
10  *	mode 0	AVS image file sent to stdout
11  *		(2 integer header followed by AlphaRGB bytes)
12  *		Alpha byte is set to 255 if no explicit alpha
13  *		values are passed from the caller
14  *
15  *	mode 1	original, long-obsolete, private format
16  *
17  *	mode 2	#ifdef LIBIMAGE_SUPPORT
18  *		calls to the libimage library if LIBIMAGE_SUPPORT is defined
19  *		(probably only makes sense on an SGI machine)
20  *
21  *	mode 3	#ifdef TIFF_SUPPORT
22  *		calls to the TIFF library if TIFF_SUPPORT is defined
23  *
24  *	mode 4	#ifdef GD_SUPPORT
25  *		JPEG image output to file (defaults to stdout)
26  *
27  *	mode 5	#ifdef GD_SUPPORT
28  *		libgd PNG image output to file (defaults to stdout)
29  *
30  * Command line switches other than output file format handled elsewhere
31  * here we recognize
32  *		-invert	         invert y coordinate axis
33  *		-jpeg [filename] for jpeg output
34  *		-png  [filename] for png output
35  *		-avs  [filename] for AVS output
36  *		-sgi  [filename] SGI libimage format output
37  *		-tiff [filename] TIFF output format
38  */
39 
40 #include	<stdio.h>
41 #include	<fcntl.h>
42 #include	<string.h>
43 #include	<ctype.h>
44 #include	<time.h>
45 #include	<stdlib.h>
46 #ifdef NETWORKBYTEORDER
47 #include	<netinet/in.h>
48 #endif
49 
50 #ifdef LIBIMAGE_SUPPORT
51 #include	<gl/image.h>
52 #endif
53 
54 #ifdef TIFF_SUPPORT
55 #include        <tiff.h>
56 #include        <tiffio.h>
57 #if defined(LINUX) || defined(OSX)
58 #include	<signal.h>
59 #endif
60 #endif /* TIFF_SUPPORT */
61 
62 #if defined GD_SUPPORT
63 #include "gd.h"
64 #endif
65 
66 /* Pick up version number from same include file used by Makefile */
67 char *
68 #include "VERSION"
69 ;
70 
71 /* Define bits in returned status */
72 #define		ANTIALIAS	007
73 #define		INVERT		010
74 #define		DEBUGGING	020
75 
76 /* Define bits passed in 3rd parameter of mode 1 */
77 #define		ALPHACHANNEL	040
78 
79 int		alpha_channel = 0;
80 
81 /* Loaded by mode 5; used to initialize image file by mode 1 */
82 static int bkg_r = 0, bkg_g = 0, bkg_b = 0;
83 
84 /* Define bits to be pass to both the local_() and addlabel_() functions */
85 static int xsize = 0, ysize = 0;
86 static int mode = -1; /* this is needed by goth local_() and addlabel_() */
87 #if defined GD_SUPPORT
88   /* For -png OR -jpeg (using libgd) output option only */
89   static gdImagePtr molecule_img = NULL;
90   static gdImagePtr label_img = NULL;
91 #endif
92 
93 /* HPUX lacks Fortran intrinsic functions AND and OR for some reason, */
94 /* so I put a copy here. On the other hand HPUX has an unusually sane */
95 /* calling convention for Fortran subroutine names.                   */
96 #if defined(__hpux)
97 #define local_ local
and(i,j)98 int and(i,j) int *i,*j; {return (*i & *j);}
or(i,j)99 int or(i,j)  int *i,*j; {return (*i | *j);}
100 #endif
101 
102 #if defined(gfortran)
and_(i,j)103 int and_(i,j) int *i,*j; {return (*i & *j);}
or_(i,j)104 int or_(i,j)  int *i,*j; {return (*i | *j);}
105 #endif
106 
107 size_t trimwhitespace(char *out, size_t len, const char *str);
108 
local_(option,buffer1,buffer2,buffer3,buffer4)109 int local_(option,buffer1,buffer2,buffer3,buffer4)
110      int	*option;
111      short	*buffer1, *buffer2, *buffer3, *buffer4;
112 {
113 
114   /* Everyone needs these */
115   int	        i;
116   static char	*ofile;
117   int		status = 0;
118   int		invert = 0;
119   int		bits;
120   static int	quality = 90;
121 
122   static time_t	start_time, end_time;
123   static char program_name[20] = "Raster3D         G";
124 
125   /* For -original output mode only */
126   static int header[8] = { 3, 1, 1, 0, 0, 0, 0, 0 };
127   char  *c = (void *)header;
128 
129 #ifdef LIBIMAGE_SUPPORT
130   /* For -sgi output mode only */
131   static IMAGE	*image;
132   static int	row=0;
133 #endif
134 
135 #ifdef TIFF_SUPPORT
136   /* For -tiff output option only */
137   static TIFF   *tfile;
138   static unsigned char *scanline;
139   unsigned short  rows_per_strip;
140   void my_write_tiff();
141 #endif
142 
143 
144 /****************************************************************/
145 /* The action taken by this subroutine is determined by the	*/
146 /* option parameter. The first call (option=0) determines the 	*/
147 /* output mode.							*/
148 /* As of V2.2.1 multiple bits may be set in the value returned.	*/
149 /****************************************************************/
150 if (*option == 0)
151     {
152     strncpy( &program_name[9], VERSION, strlen(VERSION)+1 );
153 
154     if (strncmp( (char *)buffer1, "-invert", 7) ==0)
155       {
156       invert = !invert;
157       buffer1 = buffer2;   buffer2 = buffer3;   buffer3 = buffer4;
158       }
159 
160     if (strncmp( (char *)buffer1, "-tiff", 5) == 0)
161       {
162 #ifdef TIFF_SUPPORT
163 	mode  = 3;
164 	ofile = (char *)buffer2;
165 #else
166 	fprintf(stderr,
167 		"\n This copy of render was not built with tiff support\n");
168 	exit(-1);
169 #endif
170 #ifndef TIFF_INVERT
171 	invert = !invert;
172 #endif
173       }
174 
175     /* Version 2.7a - AVS used to be the default, but no longer */
176     else if (strncmp( (char *)buffer1, "-avs" , 4) == 0) {
177 	mode = 0;
178     }
179 
180     else if (strncmp( (char *)buffer1, "-sgi" , 4) == 0)
181       {
182 #ifdef LIBIMAGE_SUPPORT
183 	mode  = 2;
184 	ofile = (char *)buffer2;
185 	invert = !invert;
186 #else
187 	fprintf(stderr,
188 		"\n This copy of render was not built with sgi libimage support\n");
189 	exit(-1);
190 #endif
191       }
192 
193     else if (strncmp( (char *)buffer1, "-jpeg" , 5) == 0)
194       {
195 #ifdef GD_SUPPORT /* JPEG */
196 	mode  = 4;
197 	ofile = (char *)buffer2;
198 #else
199 	fprintf(stderr,
200 		"\n This copy of render was not built with libgd jpeg support\n");
201 	exit(-1);
202 #endif
203       }
204 
205     else if (strncmp( (char *)buffer1, "-png" , 4) == 0)
206       {
207 #ifdef GD_SUPPORT /* PNG */
208 	mode  = 5;
209 	ofile = (char *)buffer2;
210 #else
211 	fprintf(stderr,
212 		"\n This copy of render was not built with libgd png support\n");
213 	exit(-1);
214 #endif
215       }
216 
217     /* This option is long since deprecated */
218     else if (strncmp( (char *)buffer1, "-orig", 5) == 0)
219       {
220       	mode = 1;
221 	invert = !invert;
222       }
223     else if (strncmp( (char *)buffer1, "  ", 2) != 0)
224       {
225 	fprintf(stderr, "\n%s",program_name);
226       	if (strncmp( (char *)buffer1, "-help", 5) != 0)
227 	    fprintf(stderr, "\n Unfamiliar switch: %12.12s", (char *)buffer1);
228 	fprintf(stderr, "\n\n Usage:");
229 	fprintf(stderr, "\n   input from stdin; output mode controlled from command line \n");
230 	fprintf(stderr,
231 		"\n     render [-png [outfile]]       PNG image to stdout (default) or file");
232 	fprintf(stderr,
233 		"\n     render -jpeg [outfile]        JPEG image to stdout (default) or file");
234 	fprintf(stderr,
235 		"\n     render -avs                   AVS image to stdout");
236 #ifdef LIBIMAGE_SUPPORT
237 	fprintf(stderr,
238 		"\n     render -sgi  [outfile]        output to SGI libimage file (defaults to render.rgb)");
239 #endif
240 #ifdef TIFF_SUPPORT
241 	fprintf(stderr,
242 		"\n     render -tiff [outfile]        output to TIFF file (defaults to render.tif)");
243 #endif
244 	fprintf(stderr,"\n");
245 	fprintf(stderr,"\n Options:");
246 	fprintf(stderr,"\n   these over-ride contents of input stream header records \n");
247 	fprintf(stderr,"\n    -aa                   anti-aliasing (SCHEME 4)");
248 	fprintf(stderr,"\n    -alpha                alpha channel in output image (SCHEME 0)");
249 	fprintf(stderr,"\n    -bg white|black|<col> set background color (<col> is hex #RRGGBB)");
250 	fprintf(stderr,"\n    -debug                verbose output while running");
251 	fprintf(stderr,"\n    -draft                no anti-aliasing (SCHEME 1)");
252 	fprintf(stderr,"\n    -fontscale FF         multiplier for libgd font size [default 1.0]");
253 	fprintf(stderr,"\n    -gamma GG             gamma correction applied to output image");
254 	fprintf(stderr,"\n    -invert               invert y axis");
255 	fprintf(stderr,"\n    -labels               pass labels to libgd to composite with molecular image");
256 	fprintf(stderr,"\n    -quality QQ           0 < QQ < 95  jpeg compression [default 90]");
257 	fprintf(stderr,"\n    -[no]shadow           enable or disable shadowing");
258 	fprintf(stderr,"\n    -size HHHxVVV         specify size of output image in pixels");
259 	fprintf(stderr,"\n    -transparent          same as -alpha (SCHEME 0)");
260 	fprintf(stderr,"\n    -zoom ZZ[%%]           rescale image by ZZ      ");
261 	fprintf(stderr,"\n");
262 	exit(-1);
263       }
264 
265     else /* default to png */
266       {
267 #ifdef GD_SUPPORT /* PNG */
268 	mode  = 5;
269 	ofile = (char *)buffer2;
270 #else
271 	fprintf(stderr,
272 		"\n This copy of render was not built with libgd png support\n");
273 	fprintf(stderr,
274 		"Defaulting to AVS instead\n");
275 	mode  = 0;
276 #endif
277       }
278 
279 
280     if (invert) status |= INVERT;
281 
282     return( status );
283   }
284 
285 /****************************************************************/
286 /* Subsequent calls are treated differently depending on mode	*/
287 /****************************************************************/
288 if (mode < 0)
289     {
290     fprintf(stderr,"\n Output mode not set before output request\n");
291     exit(-1);
292     }
293 
294 /****************************************************************/
295 /* Open output file and initialize image descriptor information	*/
296 /****************************************************************/
297 else if (*option == 1)
298     {
299     xsize   = *(int *)buffer1;
300     ysize   = *(int *)buffer2;
301     bits    = *(int *)buffer3;
302     quality = *(int *)buffer4;
303 
304     if (bits & ALPHACHANNEL) alpha_channel = 1;
305 
306     if (mode == 0)	/* avs */
307 	{
308 #ifdef NETWORKBYTEORDER
309 	putw( htonl(xsize), stdout );
310 	putw( htonl(ysize), stdout );
311 #else
312 	putw( xsize, stdout );
313 	putw( ysize, stdout );
314 #endif
315 	}
316 
317     if (mode == 1)   /* original */
318 	{
319 	header[3] = xsize;
320 	header[4] = ysize;
321 	for (i=0; i<sizeof(header); i++)
322 	    putchar(*c++);
323 	}
324     else
325 
326 #ifdef LIBIMAGE_SUPPORT
327     if (mode == 2)   /* sgi rgb mode */
328 	{
329 	if (*ofile != ' ')
330 	    ofile = strtok( ofile, " " );
331 	else
332 	    ofile = "render.rgb";
333 	    image = iopen(ofile,"w",RLE(1),3,xsize,ysize,alpha_channel?4:3);
334 	}
335     else
336 #endif
337 
338 #ifdef TIFF_SUPPORT
339 #define TIFFSET(A,B,C) if (!(TIFFSetField(A,B,C))) fprintf(stderr,"TIFF library error\n");
340     if (mode == 3)   /* tiff */
341 	{
342 	if (*ofile != ' ')
343 	    ofile = strtok( ofile, " " );
344 	else
345 	    ofile = "render.tiff";
346 	tfile=TIFFOpen(ofile,"w");
347 	if (!tfile) exit(-1);
348 	TIFFSET(tfile,TIFFTAG_DOCUMENTNAME,ofile);
349 	TIFFSET(tfile,TIFFTAG_SOFTWARE,program_name);
350 	TIFFSET(tfile,TIFFTAG_BITSPERSAMPLE,8);
351 	TIFFSET(tfile,TIFFTAG_SAMPLESPERPIXEL,(alpha_channel ? 4 : 3));
352 	TIFFSET(tfile,TIFFTAG_PHOTOMETRIC,PHOTOMETRIC_RGB);
353 	TIFFSET(tfile,TIFFTAG_IMAGEWIDTH,xsize);
354 	TIFFSET(tfile,TIFFTAG_IMAGELENGTH,ysize);
355 	TIFFSET(tfile,TIFFTAG_RESOLUTIONUNIT,2);
356 	TIFFSET(tfile,TIFFTAG_XRESOLUTION,300.);
357 	TIFFSET(tfile,TIFFTAG_YRESOLUTION,300.);
358 #ifdef __alpha
359 	TIFFSET(tfile,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB);
360 #endif
361 #ifdef	TIFF_INVERT
362 	TIFFSET(tfile,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT);
363 #else
364 	TIFFSET(tfile,TIFFTAG_ORIENTATION,ORIENTATION_BOTLEFT);
365 #endif
366 	TIFFSET(tfile,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
367 	TIFFSET(tfile,TIFFTAG_COMPRESSION,COMPRESSION_LZW);
368 	rows_per_strip = ysize;
369 	TIFFSET(tfile,TIFFTAG_ROWSPERSTRIP,rows_per_strip);
370 	if (alpha_channel)
371 	    {
372 	    uint16 extra_samples, sample_info[1];
373 	    extra_samples=1;
374 	    sample_info[0]=EXTRASAMPLE_ASSOCALPHA;
375 	    TIFFSetField(tfile,TIFFTAG_EXTRASAMPLES,extra_samples,&sample_info[0]);
376 	    }
377 	scanline=(unsigned char *) malloc(TIFFScanlineSize(tfile));
378 	if (scanline == (unsigned char *) NULL)
379 	    {
380 	    fprintf(stderr,"\nMemory allocation error\n");
381 	    return(-1);
382 	    }
383   	}
384     else
385 #endif
386 
387 #ifdef GD_SUPPORT /* JPEG + PNG */
388     if (mode == 4 || mode == 5) {
389 	int transparent;
390 	molecule_img = gdImageCreateTrueColor(xsize, ysize);
391 	label_img = gdImageCreateTrueColor(xsize, ysize);
392 	gdImageAlphaBlending(label_img, 0);
393 /*
394 	if (!molecule_img)
395 	{
396 	    fprintf(stderr,"PNG initialization failed - die\n");
397 	    exit(-1);
398 	}
399 */
400 
401         /* Signal that all freetype font calls in this program will receive
402          * fontconfig patterns rather than filenames of font files */
403         gdFTUseFontConfig(1);
404 
405 	transparent = gdImageColorResolve(label_img, bkg_r, bkg_g, bkg_b);
406         gdImageColorTransparent(label_img, transparent);
407 	gdImageFilledRectangle(label_img,0,0,xsize,ysize,transparent);
408 
409 	ofile = strtok( ofile, " " );
410     }
411     else
412 #endif
413 
414     /* NOP */ ;
415     start_time = time(NULL);
416     return(1);
417     }
418 
419 /****************************************************************/
420 /* Write out a single row of output pixels			*/
421 /****************************************************************/
422 else if (*option == 2)
423     {
424 
425     if (mode == 0) /* AVS image file (AlphaRGB) bytes */
426 	{
427 	if (alpha_channel)
428 	    for (i=0; i<xsize; i++)
429 		{
430 		putchar( buffer4[i] );
431 		putchar( buffer1[i] );
432 		putchar( buffer2[i] );
433 		putchar( buffer3[i] );
434 		}
435 	else
436 	    for (i=0; i<xsize; i++)
437 		{
438 		putchar( 255 );
439 		putchar( buffer1[i] );
440 		putchar( buffer2[i] );
441 		putchar( buffer3[i] );
442 		}
443 	}
444     else
445 
446     if (mode == 1)	/* original RGB bytes */
447 	{
448 	for (i=0; i<xsize; i++)
449 	    {
450 	    putchar( buffer1[i] );
451 	    putchar( buffer2[i] );
452 	    putchar( buffer3[i] );
453 	    }
454 	}
455     else
456 
457 #ifdef LIBIMAGE_SUPPORT
458     if (mode == 2)      /* -sgi option (libimage format) */
459 	{
460 	putrow(image,buffer1,row,0);
461 	putrow(image,buffer2,row,1);
462 	putrow(image,buffer3,row,2);
463 	if (alpha_channel)
464 	    putrow(image,buffer4,row,3);
465 	row++;
466 	}
467     else
468 #endif
469 
470 #ifdef TIFF_SUPPORT
471     if (mode == 3)
472 	{
473 	my_write_tiff(tfile,buffer1,buffer2,buffer3,buffer4,xsize,scanline);
474 	}
475     else
476 #endif
477 
478 #ifdef GD_SUPPORT /* JPEG + PNG */
479     if (mode == 4 || mode == 5)
480 	{
481 /* TODO: Speed up using full-line-at-a-time copy instead of pixel-by-pixel */
482 	int pixel_color;
483 	static int j = 0;
484 	int transparent;
485 
486 	if (alpha_channel)
487 #if 1
488 	    for (i=0; i<xsize; i++) {
489 		pixel_color = gdImageColorResolveAlpha(molecule_img,
490 			buffer1[i], buffer2[i], buffer3[i],
491 			(255-buffer4[i])>>1); /* inverse bitwise shift */
492 		gdImageSetPixel(molecule_img, i, j, pixel_color);
493 	    }
494 
495 /* TODO: Check the following to see if it is faster than the above. */
496 #else
497 	    for (i=0; i<xsize; i++) {
498 		molecule_img->tpixels[j][i] = gdTrueColorAlpha(
499 			buffer1[i], buffer2[i], buffer3[i],
500 			(255-buffer4[i])>>1);
501 	    }
502 #endif
503 	else
504 	    for (i=0; i<xsize; i++) {
505 		pixel_color = gdImageColorResolve(molecule_img,
506 			buffer1[i], buffer2[i], buffer3[i]);
507 		gdImageSetPixel(molecule_img, i, j, pixel_color);
508 	    }
509 
510 	if (alpha_channel) {
511 	    transparent = gdImageColorResolveAlpha(molecule_img, 0, 0, 0, 127);
512 	    gdImageColorTransparent(molecule_img, transparent);
513 	}
514 
515 	j++;
516 	}
517     else
518 #endif
519 
520     if (mode >= 7)
521     	{
522     	fprintf(stderr,"\n local.c: illegal output mode\n");
523 	exit(-1);
524 	}
525 
526     return(1);
527     }
528 
529 /****************************************************************/
530 /* Close output file if necessary				*/
531 /****************************************************************/
532 else if (*option == 3)
533     {
534 
535 #ifdef LIBIMAGE_SUPPORT
536     if (mode == 2)
537 	iclose(image);
538     else
539 #endif
540 
541 #ifdef TIFF_SUPPORT
542     if (mode == 3)
543 	{
544 	(void) TIFFFlushData(tfile);
545 #if defined(LINUX) || defined(OSX)
546 	signal( SIGSEGV, SIG_IGN );
547 #endif /* LINUX */
548 	(void) TIFFClose(tfile);
549 	}
550     else
551 #endif
552 
553 #ifdef GD_SUPPORT /* JPEG + PNG */
554     if (mode == 4 || mode == 5) {
555 
556 	if (label_img) {
557 	    /*
558 	    gdImageAlphaBlending(molecule_img, 0);
559 	    gdImageSaveAlpha(molecule_img, 1);
560 	    */
561 	    gdImageSetTile(molecule_img, label_img);
562 	    gdImageFilledRectangle(molecule_img, 0, 0, xsize, ysize, gdTiled);
563 	}
564 
565 	/*if (*ofile != ' ' && *ofile != '-')*/
566 	if (ofile) {
567 	    FILE *fd = fopen(ofile,"wb");
568             if (!fd) {
569                 fprintf(stderr,"Could not open output file %s\n",ofile);
570                 exit(-1);
571             }
572 	    if (mode == 4)
573 		gdImageJpeg(molecule_img, fd, quality);
574 	    else
575 		gdImagePng(molecule_img, fd);
576 	    fclose(fd);
577 
578 	    if (label_img) {
579 		gdImageDestroy(label_img);
580 	    }
581 	}
582 	else
583 	    if (mode == 4)
584 		gdImageJpeg(molecule_img, stdout, quality);
585 	    else
586 		gdImagePng(molecule_img, stdout);
587 	gdImageDestroy(molecule_img);
588     }
589     else
590 #endif
591 
592     /* NOP */ ;
593     end_time = time(NULL);
594     fprintf(stderr,"rendering time - %5d sec\n",(int)(end_time-start_time));
595 
596     return(1);
597     }
598 
599 /****************************************************************/
600 /* Add title to image file					*/
601 /****************************************************************/
602 else if (*option == 4)
603     {
604 
605 #ifdef LIBIMAGE_SUPPORT
606     if (mode == 2)
607 	isetname(image,buffer1);
608     else
609 #endif
610 
611 #ifdef TIFF_SUPPORT
612     if (mode == 3)
613 	TIFFSetField(tfile,TIFFTAG_IMAGEDESCRIPTION,buffer1);
614     else
615 #endif
616 
617     /* NOP */ ;
618     return(1);
619     }
620 
621 /****************************************************************/
622 /* Load background color to local storage			*/
623 /****************************************************************/
624 else if (*option == 5)
625     {
626 
627     bkg_r  = *buffer1;
628     bkg_g  = *buffer2;
629     bkg_b  = *buffer3;
630 /*
631     fprintf(stderr,"Loading background color %d %d %d\n", bkg_r, bkg_g, bkg_b);
632  */
633     return(1);
634     }
635 
636 return 0;
637 }
638 
639 #ifdef TIFF_SUPPORT
my_write_tiff(fp,buf1,buf2,buf3,buf4,size,scanline)640 void my_write_tiff(fp, buf1, buf2, buf3, buf4, size, scanline)
641 TIFF		*fp;
642 short		buf1[], buf2[], buf3[], buf4[];
643 int  		size;
644 unsigned char 	scanline[];
645 {
646 static int row=0;
647 int i;
648 int j = 0;
649 
650   if (alpha_channel)
651     for (i=0; i<size; i++) {
652 	scanline[j++] = buf1[i];
653 	scanline[j++] = buf2[i];
654 	scanline[j++] = buf3[i];
655 	scanline[j++] = buf4[i];
656     }
657   else
658     for (i=0; i<size; i++) {
659 	scanline[j++] = buf1[i];
660 	scanline[j++] = buf2[i];
661 	scanline[j++] = buf3[i];
662     }
663 
664   if (TIFFWriteScanline(fp,scanline,row,0) < 0)
665     fprintf (stderr, "\nBad return code from TIFF write\n");
666 
667 row++;
668 }
669 #endif
670 
671 /*===========================================================================*/
672 /*
673  * _addlabel()
674  */
addlabel_(fontname,fontsize,fontscale,fontalign,xp,yp,zp,red,grn,blu,labelstring,font_len,label_len)675 int addlabel_(fontname, fontsize, fontscale, fontalign, xp, yp, zp, red, grn, blu,
676 	labelstring, font_len, label_len)
677 	int *fontalign;
678 	float *fontsize, *fontscale, *xp, *yp, *zp, *red, *grn, *blu;
679 	char *fontname, *labelstring;
680 	long int font_len, label_len;
681 {
682 #ifdef GD_SUPPORT
683 	static double last_x = 0.0, last_y = 0.0, last_z = 0.0;
684 
685 	double x = *xp, y = *yp, z = *zp;
686         float angle = 0.0;
687 	int brect[8], font_color;
688 	char *err, font[128], instring[128], *justify;
689 	size_t out_size; /* out_size is just a dummy variable */
690 	char *string;
691 
692 	if (mode != 4 && mode != 5)
693 	    return 0;
694 
695 	/* TODO: If FONTNAME does not exist in the GDFONTPATH, fail gracefully */
696 	out_size = trimwhitespace(font, 128, fontname);
697 	out_size = trimwhitespace(instring, 128, labelstring);
698 	string = instring;
699 
700 	(void)out_size;		/* prevents compiler complaints */
701 	(void)last_z;		/* about unused variables */
702 	(void)justify;
703 
704 	/* Allocate colours */
705 	/* FIXME: how to pass non-zero alpha value? */
706 	font_color = gdImageColorAllocateAlpha(label_img,
707 	    (double)*red*255.0, (double)*grn*255.0, (double)*blu*255.0, 0);
708 
709         /* Draw once with a NULL image to get the bounding rectangle */
710         err = gdImageStringFT(NULL, &brect[0], font_color,
711 		font, (double)*fontsize*(double)*fontscale, angle, 0, 0, string);
712 	if (err)
713 		fprintf(stderr,"gdImageStringFT: %s while printing string %s with font %s\n",
714 			err, string, font);
715 
716 	/* Align the string */
717 	if ((int)*fontalign == 1) {
718 		/* Justify string center */
719 		justify = "Center";
720 		x -= (brect[2]-brect[0]) / 2.;
721 		y += (brect[3]-brect[1]) / 2.;
722 	}
723 	else if ((int)*fontalign == 2) {
724 		/* Justify string right */
725 		justify = "Right";
726 		x -= (brect[2]-brect[0]);
727 		y += (brect[3]-brect[1]);
728 	}
729 	else if ((int)*fontalign == 3) {
730 		justify = "Offset";
731 		x += last_x;
732 		y += last_y;
733 	}
734 	else {
735 		/* Do nothing (default) */
736 		justify = "Left";
737 	}
738 
739 	/* TODO: Process super/subscripts */
740 	while (strpbrk(string, "{}^_")) {
741 	    char *mark = strpbrk(string, "{}^_");
742 	    char save_mark = *mark;
743 
744 	    /* dump up to the first special character */
745 	    *mark = '\0';
746 	    err = gdImageStringFT(label_img, &brect[0], font_color,
747 		font, (double)*fontsize*(double)*fontscale, angle, x, y, string);
748 	    x += (brect[2] - brect[0]);
749 	    string = mark+1;
750 
751 	    /* start sub/superscript placement */
752 	    last_y = y;
753 	    if (save_mark == '^')
754 		y -= 0.5 * (*fontsize) * (*fontscale);
755 	    else if (save_mark == '_')
756 		y += 0.5 * (*fontsize) * (*fontscale);
757 	    /* dump either a single character (careful: UTF-8!) */
758 	    /* or the string enclosed by {} */
759 	    if (*string == '{') {
760 		string++;
761 		if ((mark = strchr(string,'}')) != NULL) {
762 		    *mark = '\0';
763 		    err = gdImageStringFT(label_img, &brect[0], font_color,
764 			font, (double)*fontsize*(double)*fontscale, angle, x, y, string);
765 		    x += (brect[2] - brect[0]);
766 		    y = last_y;
767 		    string = mark+1;
768 		} else {
769 		    /* No closing '}', just print the rest of the string */
770 		}
771 
772 	    } else {
773 	    	/* FIXME: check if it's not really UTF-8 */
774 		char utf8[6] = {0,0,0,0,0,0};
775 		char *u = utf8;
776 		*u = *string++;
777 		if ((*u & 0xC0) == 0xC0) { /* Start of a UTF-8 sequence */
778 		    *++u = *string++;
779 		    if ((*string & 0xc0) == 0x80)
780 			*++u = *string++;
781 		    if ((*string & 0xc0) == 0x80)
782 			*++u = *string++;
783 		    if ((*string & 0xc0) == 0x80)
784 			*++u = *string++;
785 		}
786 		err = gdImageStringFT(label_img, &brect[0], font_color,
787 			font, (double)*fontsize*(double)*fontscale, angle, x, y, utf8);
788 		x += (brect[2] - brect[0]);
789 		y = last_y;
790 	    }
791 
792 	}
793 
794 	/* Now render the remaining string, if any */
795 	err = gdImageStringFT(label_img, &brect[0], font_color,
796 		font, (double)*fontsize*(double)*fontscale, angle, x, y, string);
797 	if (err)
798 		fprintf(stderr,"gdImageStringFT: %s while printing string %s with font %s\n",
799 			err, string, font);
800 
801 	last_x = x + (brect[2] - brect[0]);
802 	last_y = y;
803 	last_z = z;
804 
805 #endif
806 	return 0;
807 }
808 
trimwhitespace(char * out,size_t len,const char * str)809 size_t trimwhitespace(char *out, size_t len, const char *str)
810 {
811 	/* A simple function to trim the whitespace from the right-side of
812 	 * a string.
813 	 */
814 
815 	const char *end;
816 	size_t out_size;
817 
818 	/* Trim leading space */
819 	/*while(isspace(*str)) str++;*/
820 
821 	/* Trim trailing space */
822 	end = str + strlen(str) - 1;
823 	while(end > str && isspace(*end)) end--;
824 	end++;
825 
826 	/* Set output size to minimum of trimmed string length and buffer size
827 	 * minus 1 */
828 	out_size = (end - str) < len-1 ? (end - str) : len-1;
829 
830 	/* Copy trimmed string and add null terminator */
831 	memcpy(out, str, out_size);
832 	out[out_size] = 0;
833 
834 	return out_size;
835 }
836