/*--------------------------------------------------------------------
 *	$Id: pstext.c,v 1.2.4.3 2002/01/17 17:42:51 pwessel Exp $
 *
 *	Copyright (c) 1991-2002 by P. Wessel and W. H. F. Smith
 *	See COPYING file for copying and redistribution conditions.
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; version 2 of the License.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	Contact info: gmt.soest.hawaii.edu
 *--------------------------------------------------------------------*/
/*
 * pstext will read (x, y, angle, size, fontno, justify, text) from GMT_stdin
 * or file and plot the textstrings at (x,y) on a map using the size, font,
 * and justification selected by the user.  Alternatively (with -H), read
 * one or more text paragraphs to be typeset.
 *
 * Author:	Paul Wessel
 * Date:	21-JAN-1991-2000
 * Version:	2.0 based on old v1.x
 * Version:	3.1 based on old 3.0
 * Version:	3.3: PW: Introducing paragraph mode -M
 * Version:	3.3.4: PW: Input x,y can be in dd:mm[:ss] format
 * Version:	3.4.1
 *
 */

#include "gmt.h"

struct GMT_TEXT {
	double x_offset, y_offset;	/* Offset from reference point */
	double line_spacing;
	double paragraph_width;
	int text_justify;
	int paragraph_font;
	int font_size;
	double paragraph_angle;
	int block_justify;
	int boxflag;
	double x_space, y_space;	/* Extra spacing between box and text */
	struct GMT_PEN boxpen;
	struct GMT_PEN vecpen;
	struct GMT_PEN txtpen;
	struct GMT_FILL txtfill;
	struct GMT_FILL boxfill;
};

int GMT_just_decode(char *key);
void GMT_putwords (double x, double y, char **text, int n_words, struct GMT_TEXT *info);

main(int argc, char **argv)
{
	int i, j, k, last_font, fno, n_files = 0, n_args, n_words = 0, nscan;
	int ix, iy, use_font[N_FONTS], n_processed = 0, n_read = 0, n_paragraphs = 0;
	size_t n_alloc;
	
	double xy[2], west = 0., east = 0.0, south = 0.0, north = 0.0, plot_x, plot_y;
	double new_z_level = 0.0, z_level = 0.0;
	
	BOOLEAN error = FALSE, nofile = TRUE, outline = FALSE, set_z = FALSE, master_record = FALSE, offset_outward = FALSE;
	BOOLEAN solid = TRUE, do_paragraphs = FALSE, list = FALSE, done = FALSE, clip = TRUE, old_GMT_world_map, add;
	BOOLEAN skip_text_records = FALSE;
	
	char text[BUFSIZ], line[BUFSIZ], this_font[80], just_key[5], txt_a[32], txt_b[32], txt_x[32], txt_y[32];
	char *p, **word, pjust;
	
	FILE *fp = NULL;
	
	struct GMT_TEXT T;

	memset ((void *)&T, 0, sizeof (struct GMT_TEXT));

	GMT_init_pen (&T.txtpen, GMT_PENWIDTH);
	GMT_init_pen (&T.boxpen, GMT_PENWIDTH);
	GMT_init_pen (&T.vecpen, GMT_PENWIDTH);
	GMT_init_fill (&T.boxfill, -1, -1, -1);	/* No fill */
	GMT_init_fill (&T.txtfill, 0, 0, 0);

	argc = GMT_begin (argc, argv);

	T.x_space = T.y_space = 0.05;
	if (gmtdefs.measure_unit == GMT_CM) T.x_space = T.y_space = 0.15 / 2.54;

	/* Check and interpret the command line arguments */
	
	for (i = 1; i < argc; i++) {
		if (argv[i][0] == '-') {
			switch(argv[i][1]) {
		
				/* Common parameters */
			
				case 'B':
				case 'H':
				case 'J':
				case 'K':
				case 'O':
				case 'P':
				case 'R':
				case 'U':
				case 'V':
				case 'X':
				case 'x':
				case 'Y':
				case 'y':
				case 'c':
				case ':':
				case '\0':
					error += GMT_get_common_args (argv[i], &west, &east, &south, &north);
					break;
				
				/* Supplemental parameters */
			
				case 'C':
					sscanf (&argv[i][2], "%[^/]/%s", txt_a, txt_b);
					T.x_space = GMT_convert_units (txt_a, GMT_INCH);
					T.y_space = GMT_convert_units (txt_b, GMT_INCH);
					break;
				case 'D':
					k = 2;
					if (argv[i][k] == 'j') offset_outward = TRUE, k++;
					for (j = k; argv[i][j] && argv[i][j] != 'v'; j++);
					if (argv[i][j] == 'v') {
						T.boxflag |= 32;
						if (argv[i][j+1] && GMT_getpen (&argv[i][j+1], &T.vecpen)) {
							fprintf (stderr, "%s: GMT SYNTAX ERROR -D option: Give pen after c\n", GMT_program);
							error++;
						}
						argv[i][j] = 0;
					}
					sscanf (&argv[i][k], "%[^/]/%s", txt_a, txt_b);
					T.x_offset = GMT_convert_units (txt_a, GMT_INCH);
					T.y_offset = GMT_convert_units (txt_b, GMT_INCH);
					break;
				case 'E':
					sscanf (&argv[i][2], "%lf/%lf", &z_project.view_azimuth, &z_project.view_elevation);
					break;
				case 'G':
					if (GMT_getfill (&argv[i][2], &T.txtfill)) {
						GMT_fill_syntax ('G');
						error++;
					}
					solid = TRUE;
					break;
				case 'L':
					list = GMT_quick = TRUE;
					break;
				case 'M':               /* Multiple line segments input: Text blocks */
					GMT_multisegment (&argv[i][2]);
					do_paragraphs = TRUE;
					break;
				case 'N':	/* Do not clip at border */
					clip = FALSE;
					break;
				case 'S':
					outline = TRUE;
					if (GMT_getpen (&argv[i][2], &T.txtpen)) {
						GMT_pen_syntax ('S');
						error++;
					}
					break;
				case 'W':
					for (j = 0; argv[i][j] && !(strchr ("cCoO", argv[i][j])); j++);
					if (argv[i][j])  {
						T.boxflag |= 1;	/* Want box outline */
						if (argv[i][j] == 'O') T.boxflag |= 4;	/* Want rounded box outline */
						if (argv[i][j] == 'c') T.boxflag |= 8;	/* Want concave box outline */
						if (argv[i][j] == 'C') T.boxflag |= 16;	/* Want convex box outline */
						if (argv[i][j+1] && GMT_getpen (&argv[i][j+1], &T.boxpen)) {
							fprintf (stderr, "%s: GMT SYNTAX ERROR -W option: Give pen after o\n", GMT_program);
							error++;
						}
						argv[i][j] = 0;
					}
					if (argv[i][2] && isdigit ((int)argv[i][2])) {	/* Also gave fill */
						if (GMT_getfill (&argv[i][2], &T.boxfill)) {
							GMT_fill_syntax ('W');
							error++;
						}
						T.boxflag |= 2;
					}
					if (!(T.boxflag | 31)) {
						fprintf (stderr, "%s: GMT SYNTAX ERROR -W option: Must give arguments\n", GMT_program);
						error++;
					}
					break;
				case 'Z':
					if (argv[i][2]) {
						new_z_level = atof (&argv[i][2]);
						set_z = TRUE;
					}
					break;
					
				/* Options not recognized */
						
				default:
					error = TRUE;
					GMT_default_error (argv[i][1]);
					break;
			}
		}
		else
			n_files++;
	}
	
	if (argc == 1 || GMT_quick) {
		fprintf (stderr, "pstext %s - To plot text on maps\n\n", GMT_VERSION);
		fprintf (stderr, "usage: pstext <txtfile> -J<params> -R<west>/<east>/<south>/<north>\n");
		fprintf (stderr, "\t[-B<tickinfo>] [-C<dx>/<dy>] [-D[j]<dx>/<dy>[v[<pen>]] [-Eaz/el] [-G<color>]\n");
		fprintf (stderr, "\t[-H[<nrec>]] [-K] [-L] [-M[<flag>]] [-N] [-O] [-P] [-S<pen>] [-U] [-V] [-W[<fill>][o|O|c[<pen>]]]\n");
		fprintf (stderr, "\t[-X<x_shift>] [-Y<y_shift>] [-Z<zlevel>] [-c<ncopies>] [-:]\n\n");
		fprintf (stderr, "\tReads (x,y,size,angle,fontno,justify,text) from <txtfile> [or stdin]\n");
		fprintf (stderr, "\tOR (with -M) one or more text paragraphs with formatting info in the segment header.\n");
		fprintf (stderr, "\tjustify is of the form [T|M|B][L|C|R] (top/middle/bottom/left/center/right)\n");
		fprintf (stderr, "\tBuilt-in escape sequences:\n");
		fprintf (stderr, "\t   @~ toggles between current font and Symbol font\n");
		fprintf (stderr, "\t   @%%<no>%% switches to font number <no>; @%%%% resets font\n");
		fprintf (stderr, "\t   @+ toggles between normal and superscript mode\n");
		fprintf (stderr, "\t   @- toggles between normal and subscript mode\n");
		fprintf (stderr, "\t   @# toggles between normal and Small Caps mode\n");
		fprintf (stderr, "\t   @!<char1><char2> makes one composite character\n");
		fprintf (stderr, "\t   @@ prints the @ sign itself\n");
		fprintf (stderr, "\t   @e, @o, @a, @E, @O, @A give the accented Scandinavian characters\n");
		fprintf (stderr, "\t   In addition, paragraph text (-M) also understands these sequences:\n");
		fprintf (stderr, "\t   @:<size>: switches font size; @:: resets font size\n");
		fprintf (stderr, "\t   @;<r/g/b>; switches font color; @;; resets font color\n");
		fprintf (stderr, "\t   @_ toggles between normal and underlined text\n");
		fprintf (stderr, "\t(See manual page for more information)\n");
		
		if (list) {	/* List fonts */
			fprintf (stderr, "\n\tFont #	Font Name\n");
			fprintf (stderr, "\t------------------------------------\n");
			for (i = 0; i < N_FONTS; i++)
				fprintf (stderr, "\t%3d\t%s\n", i, GMT_font_name[i]);
		}
			
		if (GMT_quick) exit (EXIT_FAILURE);
		
		GMT_explain_option ('j');
		GMT_explain_option ('R');
		fprintf (stderr, "\n\tOPTIONS:\n");
		GMT_explain_option ('b');
		fprintf (stderr, "\t-C sets the clearance between characters and surrounding box [0.05i/0.05i]\n");
		fprintf (stderr, "\t   Only used if -W has been set\n");
		fprintf (stderr, "\t-D adds <add_x>,<add_y> to the text origin AFTER projecting with -J. [0/0]\n");
		fprintf (stderr, "\t   Use -Dj to move text origin away from point (direction determined by text's justification)\n");
		fprintf (stderr, "\t   Append v[<pen>] to draw line from text to original point (-M only)\n");
		fprintf (stderr, "\t-E set azimuth and elevation of viewpoint for 3-D perspective [180/90]\n");
		fprintf (stderr, "\t-G set the color (red/green/blue (0-255)) for solid text [0/0/0]\n");
		GMT_explain_option ('H');
		GMT_explain_option ('K');
		GMT_explain_option ('M');
		fprintf (stderr, "\t   Expects (x y size angle fontno justify linespace parwidth parjust) in segment header\n");
		fprintf (stderr, "\t   followed by lines with one or more paragraphs of text.\n");
		fprintf (stderr, "\t   parjust is one of (l)eft, (c)enter, (r)ight, or (j)ustified.\n");
		fprintf (stderr, "\t-N Do Not clip text that exceeds the map boundaries [Default will clip]\n");
		GMT_explain_option ('O');
		GMT_explain_option ('P');
		fprintf (stderr, "\t-S draw outline of characters.  Append pen attributes\n");
		GMT_explain_option ('U');
		GMT_explain_option ('V');
		fprintf (stderr, "\t-W paints and outlines a rectangle underneath the text.  [Default is no rectangle]\n");
		fprintf (stderr, "\t   Append fill color [none].  To draw outline, append o, O, or c, and optionally a pen [No outline]\n");
		fprintf (stderr, "\t   Lower case o will draw rectangular outline\n");
		fprintf (stderr, "\t   Upper case O will draw rectangle with rounded corners (-M only)\n");
		fprintf (stderr, "\t   Lower case c will draw concave rectangle (-M only)\n");
		fprintf (stderr, "\t   Upper case C will draw convex rectangle (-M only)\n");
		GMT_explain_option ('X');
		fprintf (stderr, "\t-Z For 3-D plots: Set the z-level of map [0]\n");
		GMT_explain_option ('c');
		GMT_explain_option (':');
		GMT_explain_option ('.');
		exit (EXIT_FAILURE);
	}

	/* Check that the options selected are mutually consistant */
	
	if (T.x_space < 0.0 || T.y_space < 0.0) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR -C option:  clearances cannot be negative!\n", GMT_program);
		error++;
	}
	if (T.x_space == 0.0 && T.y_space == 0.0 && (T.boxflag & 4)) {
		fprintf (stderr, "%s: Warning: -WO requires a nonzero -C\n", GMT_program);
		error++;
	}
	if (T.x_space == 0.0 && T.y_space == 0.0 && (T.boxflag & 8 || T.boxflag & 16)) {
		fprintf (stderr, "%s: Warning: -Wc|C requires a nonzero -C\n", GMT_program);
		error++;
	}
	if ((T.boxflag & 32) && !do_paragraphs) {
		fprintf (stderr, "%s: Warning: -D<x/y>v only applies to -M\n", GMT_program);
		error++;
	}
	if (!(z_project.view_azimuth == 180.0 && z_project.view_elevation == 90.0) && do_paragraphs) {
		fprintf (stderr, "%s: -E not implemented with -M yet\n", GMT_program);
		error++;
	}
	if (T.x_offset == 0.0 && T.y_offset == 0.0 && (T.boxflag & 32)) {
		fprintf (stderr, "%s: Warning: -D<x/y>v requires one nonzero <x/y>\n", GMT_program);
		error++;
	}
	if (z_project.view_azimuth > 360.0 || z_project.view_elevation <= 0.0 || z_project.view_elevation > 90.0) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR -E option:  Enter azimuth in 0-360 range, elevation in 0-90 range\n", GMT_program);
		error++;
	}
	if (!project_info.region_supplied) {
		fprintf (stderr, "%s: GMT SYNTAX ERROR:  Must specify -R option\n", GMT_program);
		error++;
	}

	if (error) exit (EXIT_FAILURE);

	GMT_put_history (argc, argv);	/* Update .gmtcommands */

	add = !(T.x_offset == 0.0 && T.y_offset == 0.0);
	if (add && offset_outward) T.boxflag |= 64;
	if (n_files > 0)
		nofile = FALSE;
	else
		n_files = 1;
	n_args = (argc > 1) ? argc : 2;
	
	GMT_map_setup (west, east, south, north);
	
	ps_plotinit (CNULL, gmtdefs.overlay, gmtdefs.page_orientation, gmtdefs.x_origin, gmtdefs.y_origin,
		gmtdefs.global_x_scale, gmtdefs.global_y_scale, gmtdefs.n_copies,
		gmtdefs.dpi, GMT_INCH, gmtdefs.paper_width, gmtdefs.page_rgb, GMT_epsinfo (argv[0]));
		
	GMT_echo_command (argc, argv);
	if (gmtdefs.unix_time) GMT_timestamp (argc, argv);
	if (project_info.three_D) ps_transrotate (-z_project.xmin, -z_project.ymin, 0.0);
	
	if (set_z) {
		project_info.z_level = new_z_level;
		GMT_z_to_zz (new_z_level, &z_level);
	}
	GMT_setpen (&T.txtpen);

	if (clip) GMT_map_clip_on (GMT_no_rgb, 3);
	
	last_font = 0;
	ix = (gmtdefs.xy_toggle);	iy = 1 - ix;
	
	/* if (draw_box && project_info.three_D) draw_box = FALSE; */	/* Not implemented yet */
	
	/* Mark used fonts */
	
	if (gmtdefs.want_euro_font) {
		memset ((void *)use_font, 0, (size_t)(N_FONTS * sizeof (int)));
		if (gmtdefs.unix_time) use_font[0] = use_font[1] = TRUE;
		use_font[gmtdefs.anot_font] = TRUE;
		if (frame_info.header[0]) use_font[gmtdefs.header_font] = TRUE;
		if (frame_info.label[0][0] || frame_info.label[1][0] || frame_info.label[2][0]) use_font[gmtdefs.label_font] = TRUE;
	}

	if (do_paragraphs) {
		n_alloc = GMT_CHUNK;
		word = (char **) GMT_memory (VNULL, n_alloc, sizeof (char), GMT_program);
	}

	old_GMT_world_map = GMT_world_map;
	GMT_world_map = TRUE;

	for (fno = 1; !done && fno < n_args; fno++) {	/* Loop over all input files */
		if (!nofile && argv[fno][0] == '-') continue;
		if (nofile) {
			fp = GMT_stdin;
			done = TRUE;
		}
		else if ((fp = fopen (argv[fno], "r")) == NULL) {
			fprintf (stderr, "psxy: Cannot open file %s\n", argv[fno]);
			continue;
		}

		if (!nofile && gmtdefs.verbose) fprintf (stderr, "pstext: Working on file %s\n", argv[fno]);
		if (gmtdefs.io_header) for (i = 0; i < gmtdefs.n_header_recs; i++) fgets (line, BUFSIZ, fp);

		while (fgets (line, BUFSIZ, fp)) {

			if (line[0] == '\0' || (line[0] == '#' && GMT_io.EOF_flag != '#')) continue;	/* Skip blank lines or # comments */
			
			if (do_paragraphs) {	/* Paragraph mode */
				if (line[0] == GMT_io.EOF_flag) {

					skip_text_records = FALSE;
					if (n_processed) {	/* Must output what we got */
						GMT_putwords (plot_x, plot_y, word, n_words, &T);
						n_processed = n_words = 0;
						n_paragraphs++;
					}

					nscan = sscanf (&line[2], "%s %s %d %lf %s %s %s %s %c\n", txt_x, txt_y, &T.font_size, &T.paragraph_angle, this_font, just_key, txt_a, txt_b, &pjust);
					if (!(GMT_scanf (txt_x, &xy[ix]) && GMT_scanf (txt_y, &xy[iy]))) {
						fprintf (stderr, "%s: Record %d had bad x and/or y coordinates, must exit)\n", GMT_program, n_read);
						exit (EXIT_FAILURE);
					}
					
					if (nscan != 9) {
						fprintf (stderr, "%s: Record %d had incomplete paragraph information, must exit)\n", GMT_program, n_read);
						exit (EXIT_FAILURE);
					}
					GMT_geo_to_xy (xy[0], xy[1], &plot_x, &plot_y);
					if (clip) {
						skip_text_records = TRUE;	/* If this record should be skipped we must skip the whole paragraph */
						GMT_map_outside (xy[0], xy[1]);
						if ( abs (GMT_x_status_new) > 1 || abs (GMT_y_status_new) > 1) continue;
						skip_text_records = FALSE;	/* Since we got here we do not want to skip */
					}
					T.text_justify = (int)pjust;
					T.block_justify = (isdigit ((int)just_key[0])) ? atoi (just_key) : GMT_just_decode (just_key);
					if (T.block_justify == -99) {
						fprintf (stderr, "%s: Record %d had bad justification info (set to LB)\n", GMT_program, n_read);
						T.block_justify = 1;
					}
					if (this_font[0] >= '0' && this_font[0] <= '9')	/* Gave a font # */
						T.paragraph_font = atoi (this_font);
					else {	/* Gave a font name */
						T.paragraph_font = GMT_key_lookup (this_font, GMT_font_name, N_FONTS);
						if (T.paragraph_font == N_FONTS) {
							fprintf (stderr, "%s: Record %d had bad font (set to %s (0))\n", GMT_program, n_read, GMT_font_name[0]);
							T.paragraph_font = 0;
						}
					}
					if (!use_font[T.paragraph_font] && gmtdefs.want_euro_font) {	/* Must reencode this font */
						ps_def_euro_font (T.paragraph_font);
						use_font[T.paragraph_font] = TRUE;
					}
					T.line_spacing = GMT_convert_units (txt_a, GMT_INCH);
					T.paragraph_width  = GMT_convert_units (txt_b, GMT_INCH);
					master_record = TRUE;
				}
				else {	/* Text block record */
					if (!master_record) {
						fprintf (stderr, "%s: Text record line %d not preceeded by paragraph information, must exit)\n", GMT_program, n_read);
						exit (EXIT_FAILURE);
					}

					line[strlen(line)-1] = 0;

					if (line[0] == 0) {	/* Blank line */
						word[n_words] = (char *) GMT_memory (VNULL, (size_t)(1), sizeof (char), GMT_program);
						n_words++;
						if ((size_t)n_words == n_alloc) {
							n_alloc += GMT_CHUNK;
							word = (char **) GMT_memory (VNULL, n_alloc, sizeof (char *), GMT_program);
						}
					}

					p = strtok (line, " ");
					while (p) {
						word[n_words] = (char *) GMT_memory (VNULL, (size_t)(strlen(p)+1), sizeof (char), GMT_program);
						strcpy (word[n_words], p);
						n_words++;
						if ((size_t)n_words == n_alloc) {
							n_alloc += GMT_CHUNK;
							word = (char **) GMT_memory (VNULL, n_alloc, sizeof (char *), GMT_program);
						}
						p = strtok (NULL, " ");
					}

					n_processed++;
				}
				n_read++;
			}
			else {	/* Old-style pstext input */
				if (skip_text_records) continue;	/* Skip all records for this paragraph */
				nscan = sscanf (line, "%s %s %d %lf %s %s %[^\n]\n", txt_x, txt_y, &T.font_size, &T.paragraph_angle, this_font, just_key, text);
				if (!(GMT_scanf (txt_x, &xy[ix]) && GMT_scanf (txt_y, &xy[iy]))) {
					fprintf (stderr, "%s: Record %d had bad x and/or y coordinates, must exit)\n", GMT_program, n_read);
					exit (EXIT_FAILURE);
				}
				if (nscan != 7) {
					fprintf (stderr, "%s: Record %d is incomplete (skipped)\n", GMT_program, n_read);
					continue;
				}
				n_read++;
				GMT_geo_to_xy (xy[0], xy[1], &plot_x, &plot_y);
				if (clip) {
					GMT_map_outside (xy[0], xy[1]);
					if ( abs (GMT_x_status_new) > 1 || abs (GMT_y_status_new) > 1) continue;
				}
			
				T.block_justify = (isdigit ((int)just_key[0])) ? atoi (just_key) : GMT_just_decode (just_key);
				if (T.block_justify == -99) {
					fprintf (stderr, "%s: Record %d had bad justification info (skipped)\n", GMT_program, n_read);
					continue;
				}
				if (add) {
					if (offset_outward) {	/* Smart offset according to justification (from Dave Huang) */
						switch (T.block_justify & 3) {
							case 1:
								plot_x += T.x_offset;
								break;
							case 3:
								plot_x -= T.x_offset;
								break;
						}
						switch (T.block_justify >> 2) {
							case 0:
								plot_y += T.y_offset;
								break;
							case 2:
								plot_y -= T.y_offset;
								break;
						}
					} else {	/* Default hard offset */
						plot_x += T.x_offset;
						plot_y += T.y_offset;
					}
				}
				if (this_font[0] >= '0' && this_font[0] <= '9')	/* Gave a font # */
					T.paragraph_font = atoi (this_font);
				else {	/* Gave a font name */
					T.paragraph_font = GMT_key_lookup (this_font, GMT_font_name, N_FONTS);
					if (T.paragraph_font == N_FONTS) {
						fprintf (stderr, "%s: Record %d had bad font (set to %s (0))\n", GMT_program, n_read, GMT_font_name[0]);
						T.paragraph_font = 0;
					}
				}
				n_paragraphs++;
				
				if (T.paragraph_font != last_font) {
					ps_setfont (T.paragraph_font);
					last_font = T.paragraph_font;
				}
				if (!use_font[T.paragraph_font] && gmtdefs.want_euro_font) {	/* Must reencode this font */
					ps_def_euro_font (T.paragraph_font);
					use_font[T.paragraph_font] = TRUE;
				}
				if (T.boxflag) {
					GMT_setpen (&T.boxpen);
					GMT_textbox3d (plot_x, plot_y, z_level, T.font_size, T.paragraph_font, text, T.paragraph_angle, T.block_justify, (T.boxflag & 1), T.x_space, T.y_space, T.boxfill.rgb);
					GMT_setpen (&T.txtpen);
				}
				if (outline) {
					GMT_setpen (&T.txtpen);
					GMT_text3d (plot_x, plot_y, z_level, T.font_size, T.paragraph_font, text, T.paragraph_angle, T.block_justify, TRUE);
				}
				if (solid) {
					ps_setpaint (T.txtfill.rgb);
					GMT_text3d (plot_x, plot_y, z_level, T.font_size, T.paragraph_font, text, T.paragraph_angle, T.block_justify, FALSE);
				}
			}
		} /* Go read next line */

		if (do_paragraphs && n_processed) {	/* Must output what we got */
			GMT_putwords (plot_x, plot_y, word, n_words, &T);
			n_processed = n_words = 0;
			n_paragraphs++;
		}
		if (fp != GMT_stdin) fclose (fp);
	}
	
 	if (do_paragraphs) GMT_free ((void *)word);

	if (clip) GMT_map_clip_off (); 

	GMT_world_map = old_GMT_world_map;

	if (frame_info.plot) GMT_map_basemap ();
		
	if (project_info.three_D) ps_rotatetrans (z_project.xmin, z_project.ymin, 0.0);

	ps_plotend (gmtdefs.last_page);

	if (gmtdefs.verbose) {
		if (do_paragraphs)
			fprintf (stderr, "pswords: Plotted %d textblocks\n", n_paragraphs);
		else
			fprintf (stderr, "pstext: Plotted %d text-strings\n", n_paragraphs);
	}
	
	GMT_end (argc, argv);
}

int GMT_just_decode (char *key)
{
	int i, j, k;

	/* Converts justification info like LL (lower left) to justification indeces */

	for (k = 0; k < (int)strlen (key); k++) {
		switch (key[k]) {
			case 'b':	/* Bottom baseline */
			case 'B':
				j = 0;
				break;
			case 'm':	/* Middle baseline */
			case 'M':
				j = 4;
				break;
			case 't':	/* Top baseline */
			case 'T':
				j = 8;
				break;
			case 'l':	/* Left Justified */
			case 'L':
				i = 1;
				break;
			case 'c':	/* Center Justified */
			case 'C':
				i = 2;
				break;
			case 'r':	/* Right Justified */
			case 'R':
				i = 3;
				break;
			default:
				return (-99);
		}
	}

	return (j + i);
}

void GMT_putwords (double x, double y, char **text, int n_words, struct GMT_TEXT *T) {
	char *boxpen_texture = CNULL, *vecpen_texture = CNULL;
	int boxpen_width, boxpen_offset, boxpen_rgb[3];
	int vecpen_width, vecpen_offset, vecpen_rgb[3];
	int i;

	boxpen_texture = GMT_convertpen (&T->boxpen, &boxpen_width, &boxpen_offset, boxpen_rgb);
	vecpen_texture = GMT_convertpen (&T->vecpen, &vecpen_width, &vecpen_offset, vecpen_rgb);

	ps_words (x, y, text, n_words, T->line_spacing, T->paragraph_width, T->text_justify, T->paragraph_font, T->font_size,
		T->paragraph_angle, T->txtfill.rgb, T->block_justify, T->boxflag, T->x_offset, T->y_offset,
		T->x_space, T->y_space,
		boxpen_width, boxpen_texture, boxpen_offset, boxpen_rgb,
		vecpen_width, vecpen_texture, vecpen_offset, vecpen_rgb,
		T->boxfill.rgb);

	if (boxpen_texture) GMT_free ((void *)boxpen_texture);
	if (vecpen_texture) GMT_free ((void *)vecpen_texture);

	for (i = 0; i < n_words; i++) GMT_free ((void *)text[i]);
}
