File o_text_basic.c
*******************

File header
===========

     <<o_text_basic.c : *>>=
     <<o_text_basic.c : copyright and license -- o_text_basic.c - copyright and license>>

     /* DO NOT read or edit this file ! Use ../noweb/o_text_basic.nw instead */

     <<o_text_basic.c : include directives -- o_text_basic.c - include directives>>
     <<o_text_basic.c : macros -- o_text_basic.c - macros>>
     <<o_text_basic.c : global variable -- o_text_basic.c - global variable>>
     <<o_text_basic.c : get_text_bounds() -- o_text_basic.c - get_text_bounds()>>
     <<o_text_basic.c : world_get_text_bounds() -- o_text_basic.c - world_get_text_bounds()>>
     <<o_text_basic.c : o_text_add_head() -- o_text_basic.c - o_text_add_head()>>
     <<o_text_basic.c : o_text_init() -- o_text_basic.c - o_text_init()>>
     <<o_text_basic.c : o_text_print_set() -- o_text_basic.c - o_text_print_set()>>
     <<o_text_basic.c : o_text_load_font() -- o_text_basic.c - o_text_load_font()>>
     <<o_text_basic.c : o_text_num_lines() -- o_text_basic.c - o_text_num_lines()>>
     <<o_text_basic.c : o_text_height() -- o_text_basic.c - o_text_height()>>
     <<o_text_basic.c : o_text_width() -- o_text_basic.c - o_text_width()>>
     <<o_text_basic.c : o_text_create_string() -- o_text_basic.c - o_text_create_string()>>
     <<o_text_basic.c : o_text_add() -- o_text_basic.c - o_text_add()>>
     <<o_text_basic.c : o_text_recalc() -- o_text_basic.c - o_text_recalc()>>
     <<o_text_basic.c : o_text_read() -- o_text_basic.c - o_text_read()>>
     <<o_text_basic.c : o_text_set_info_font() -- o_text_basic.c - o_text_set_info_font()>>
     <<o_text_basic.c : o_text_save() -- o_text_basic.c - o_text_save()>>
     <<o_text_basic.c : o_text_recreate() -- o_text_basic.c - o_text_recreate()>>
     <<o_text_basic.c : o_text_translate_world() -- o_text_basic.c - o_text_translate_world()>>
     <<o_text_basic.c : o_text_copy() -- o_text_basic.c - o_text_copy()>>
     <<o_text_basic.c : o_text_freeallfonts() -- o_text_basic.c - o_text_freeallfonts()>>
     <<o_text_basic.c : o_text_print_text_width() -- o_text_basic.c - o_text_print_text_width()>>
     <<o_text_basic.c : o_text_print_text_height() -- o_text_basic.c - o_text_print_text_height()>>
     <<o_text_basic.c : o_text_print_text_height_full() -- o_text_basic.c - o_text_print_text_height_full()>>
     <<o_text_basic.c : o_text_print_text_string() -- o_text_basic.c - o_text_print_text_string()>>
     <<o_text_basic.c : o_text_print() -- o_text_basic.c - o_text_print()>>
     <<o_text_basic.c : o_text_rotate_lowlevel() -- o_text_basic.c - o_text_rotate_lowlevel()>>
     <<o_text_basic.c : o_text_rotate_world() -- o_text_basic.c - o_text_rotate_world()>>
     <<o_text_basic.c : o_text_rotate() -- o_text_basic.c - o_text_rotate()>>

     #if 0 /* code which is no longer needed, replaced by new functions below */
     <<o_text_basic.c : o_text_mirror_old() -- o_text_basic.c - o_text_mirror_old()>>
     <<o_text_basic.c : o_text_mirror_world_old() -- o_text_basic.c - o_text_mirror_world_old()>>
     #endif

     #if 0 /* interesting, but currently unused code */
     <<o_text_basic.c : o_text_return_center() -- o_text_basic.c - o_text_return_center()>>
     <<o_text_basic.c : o_text_change_angle() -- o_text_basic.c - o_text_change_angle()>>
     #endif

     <<o_text_basic.c : o_text_mirror_world() -- o_text_basic.c - o_text_mirror_world()>>
     <<o_text_basic.c : o_text_mirror() -- o_text_basic.c - o_text_mirror()>>

     <<o_text_basic.c : copyright and license>>=
     /* gEDA - GPL Electronic Design Automation
      * libgeda - gEDA's library
      * Copyright (C) 1998-2000 Ales V. Hvezda
      *
      * 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; either version 2 of the License, or
      * (at your option) any later version.
      *
      * 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.
      *
      * You should have received a copy of the GNU General Public License
      * along with this program; if not, write to the Free Software
      * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
      */

     <<o_text_basic.c : include directives>>=
     #include <config.h>

     #include <stdio.h>
     #include <math.h>
     #include <sys/stat.h>
     #include <assert.h>
     #ifdef HAVE_STRING_H
     #include <string.h>
     #endif
     #ifdef HAVE_UNISTD_H
     #include <unistd.h>
     #endif

     #include <gtk/gtk.h>
     #include <libguile.h>

     #include "defines.h"
     #include "struct.h"
     #include "globals.h"
     #include "o_types.h"
     #include "colors.h"
     #include "funcs.h"

     #include "../include/prototype.h"

     #ifdef HAVE_LIBDMALLOC
     #include <dmalloc.h>
     #endif

     <<o_text_basic.c : macros>>=
     #define WINONLY 1
     #define BACKING 2

     /* font storage and friends are staying global so that all can access */

          Defines :
          .
          .
          .

     }

     <<o_text_basic.c : global variable>>=
     /* Hashtable storing font_character (string) as a key, and pointer to data */
     GHashTable *font_loaded = NULL;

     /* Hashtable storing mapping between character and font definition file */
     GHashTable *font_char_to_file = NULL;

     /* Size of a tab in characters */
     int tab_in_chars = 8;

          Defines :
          font_char_to_file,
           -- o_text_basic.c - o_text_init(),
           -- o_text_basic.c - o_text_load_font(),
           -- o_text_basic.c - o_text_freeallfonts().
          .
          tab_in_chars,
           -- o_text_basic.c - o_text_width(),
           -- o_text_basic.c - o_text_create_string().

     }

Function get_text_bounds()
==========================

 -- Function: get_text_bounds w_current o_current left top right bottom

     <<o_text_basic.c : get_text_bounds()>>=
     void
     get_text_bounds(TOPLEVEL *w_current, OBJECT *o_current, int *left, int *top,
                     int *right, int *bottom)
     {
       get_complex_bounds(w_current, o_current->text->prim_objs, left, top,
                          right, bottom);
     }

          Defines :
          get_text_bounds,
           -- o_text_basic.c - o_text_add(),
           -- o_text_basic.c - o_text_translate_world().

     }

Function world_get_text_bounds()
================================

 -- Function: world_get_text_bounds w_current o_current left top right bottom

     <<o_text_basic.c : world_get_text_bounds()>>=
     void
     world_get_text_bounds(TOPLEVEL *w_current, OBJECT *o_current, int *left,
                           int *top, int *right, int *bottom)
     {
       world_get_complex_bounds(w_current, o_current->text->prim_objs,
                                left, top, right, bottom);
     }

          Defines :
          .

     }

Function o_text_add_head()
==========================

 -- Function: o_text_add_head

     <<o_text_basic.c : o_text_add_head()>>=
     OBJECT *
     o_text_add_head(void)
     {
       OBJECT *new_node=NULL;

       new_node = s_basic_init_object("text_head");
       new_node->type = OBJ_HEAD;

       /* don't need to do this for head nodes */
       /* ret = s_basic_link_object(new_node, NULL);*/
       return(new_node);
     }

          Defines :
          o_text_add_head,
           -- o_text_basic.c - o_text_add(),
           -- o_text_basic.c - o_text_recreate(),
           -- o_text_basic.c - o_text_load_font().

     }

Function o_text_init()
======================

 -- Function: o_text_init

     <<o_text_basic.c : o_text_init()>>=
     void
     o_text_init(void)
     {
       if (font_loaded == NULL) {
         font_loaded = g_hash_table_new_full (g_direct_hash,
                                              g_direct_equal,
                                              NULL,
                                              g_free);
       } else {
         fprintf (stderr, "o_text_init: Tried to initialize an already initialized font_loaded hash table!!\n");
       }
       if (font_char_to_file == NULL) {
         font_char_to_file = g_hash_table_new_full (g_direct_hash,
                                                    g_direct_equal,
                                                    NULL,
                                                    g_free);
       }
       else {
         fprintf (stderr, "o_text_init: Tried to initialize an already initialized font_char_to_file hash table!!\n");
       }

       return;

     }

          Defines :
          .

          Uses :
          font_char_to_file,  -- o_text_basic.c - global variable.

     }

Function o_text_print_set()
===========================

 -- Function: o_text_print_set

     <<o_text_basic.c : o_text_print_set()>>=
     void
     o_text_print_set(void)
     {
       OBJECT *o_current, *o_font_set;
       char i;

       for (i = 'A' ; i < 'Z'+1; i++) {
         o_font_set = g_hash_table_lookup (font_loaded,
                                           GUINT_TO_POINTER ((gunichar)i));
         if (o_font_set != NULL) {
           printf("%c: LOADED\n", i);
           for (o_current=return_tail(o_font_set->font_prim_objs); o_current;
                o_current=o_current->prev)
           {
             printf("  %s\n", o_current->name);
           }
         } else {
           printf("%c: unloaded\n", i);
         }
       }
     }

          Defines :
          .

     }

Function o_text_load_font()
===========================

 -- Function: o_text_load_font w_current needed_char

     <<o_text_basic.c : o_text_load_font()>>=
     OBJECT *
     o_text_load_font(TOPLEVEL *w_current, gunichar needed_char)
     {
       gchar *temp_string = NULL;
       OBJECT *temp_parent, *o_font_set;
       int not_found = FALSE;
       gchar *aux_str2;

       /* retrieve the name of the file where the char is defined */
       aux_str2 = g_hash_table_lookup (font_char_to_file,
                                       GUINT_TO_POINTER (needed_char));
       if (aux_str2 == NULL) {
           /* this is needed since WinNT file systems are
            * case insensitive, and cannot tell the difference
            * between A.sym and a.sym.  So we create a_.sym -
            * z_.sym, this loads up the chars
            */
           if (needed_char >= 'a' && needed_char <= 'z') {
             temp_string = g_strdup_printf("%s%c%c_.sym",
                     w_current->font_directory, G_DIR_SEPARATOR,
                     needed_char);
           } else {
             temp_string = g_strdup_printf("%s%c%c.sym",
                     w_current->font_directory, G_DIR_SEPARATOR,
                     needed_char);
           }
       } else {
         temp_string = g_strdup_printf("%s", aux_str2);
       }
       aux_str2 = NULL;

       if ( access(temp_string, R_OK) != 0 ) {
         gchar outbuf[7];
         gint l;

         /* convert needed_char to a utf-8 string */
         l = g_unichar_to_utf8 (needed_char, outbuf);
         outbuf[l] = '\0';
         s_log_message("Could not find character '%s' definition.\n", outbuf);

         g_free (temp_string);
         temp_string = g_strdup_printf("%s%cquest.sym", w_current->font_directory, G_DIR_SEPARATOR);
         if ( access(temp_string, R_OK) != 0 ) {
           fprintf(stderr, "Could not load question font char -- check font-directory keyword\n");
           exit(-1);
         }
         not_found = TRUE;
       }

       /* Make new object for the font set list */
       o_font_set = (OBJECT*)g_new (OBJECT, 1);

       o_font_set->font_prim_objs = NULL;
       o_font_set->font_text_size = 100;

       o_font_set->name = g_strdup_printf ("%c", needed_char);
       o_font_set->font_prim_objs = o_text_add_head();

       /* Add it to the list and hash table. Some functions will need it */
       g_hash_table_insert (font_loaded,
                            GUINT_TO_POINTER (needed_char), o_font_set);

       if (not_found == TRUE) {
         /* set the font text size (width) to the question mark's size */
         /* yes, the question mark character was loaded instead of the real char */
         /* 63 == question mark character */

         OBJECT *aux_obj;

         aux_obj = g_hash_table_lookup (font_loaded,
                                        GUINT_TO_POINTER ((gunichar)'?'));
         if (aux_obj == NULL) {
           o_text_load_font(w_current, (gunichar) '?');
           aux_obj = g_hash_table_lookup (font_loaded,
                                          GUINT_TO_POINTER ((gunichar)'?'));
         }

         o_font_set->font_text_size = aux_obj->font_text_size;
       }

       temp_parent = w_current->page_current->object_parent;
       /* set the addition of attributes to the head node */
       w_current->page_current->object_parent = o_font_set->font_prim_objs;

       o_font_set->font_prim_objs = o_read(w_current, o_font_set->font_prim_objs,
                                           temp_string);
       w_current->page_current->object_parent = temp_parent;

       o_font_set->font_prim_objs = return_head(o_font_set->font_prim_objs);

       return(o_font_set->font_prim_objs);
     }

          Defines :
          o_text_load_font,
           -- o_text_basic.c - o_text_width(),
           -- o_text_basic.c - o_text_create_string().

          Uses :
          font_char_to_file,  -- o_text_basic.c - global variable.
          o_text_add_head,  -- o_text_basic.c - o_text_add_head().

     }

Function o_text_num_lines()
===========================

 -- Function: o_text_num_lines string

     <<o_text_basic.c : o_text_num_lines()>>=
     int
     o_text_num_lines(char *string)
     {
       int line_count = 0;
       gchar *aux;
       gunichar current_char;

       if (string == NULL) {
         return 0;
       }

       /* if it's not null, then we have at least one line */
       line_count++;
       /* Count how many \n are in the string */
       aux = string;
       while (aux && ((gunichar) (*aux) != 0) ) {
         current_char = g_utf8_get_char_validated(aux, -1);
         if (current_char == '\n')
           line_count++;
         aux = g_utf8_find_next_char(aux, NULL);
       }

       return (line_count);
     }

          Defines :
          o_text_num_lines,
           -- o_text_basic.c - o_text_print_text_width(),
           -- o_text_basic.c - o_text_print_text_height_full(),
           -- o_text_basic.c - o_text_save(),
           -- o_text_basic.c - o_text_print(),
           -- o_text_basic.c - o_text_height().

     }

Function o_text_height()
========================

 -- Function: o_text_height size
     You do not need to divide the size in half here.

     <<o_text_basic.c : o_text_height()>>=
     int
     o_text_height(char *string, int size)
     {
       int line_count = 0;

       if (string == NULL) {
         return 0;
       }

       /* Get the number of lines in the string */
       line_count = o_text_num_lines(string);

       /* 26 is the height of a single char (in mils) */
       /* which represents a character which is 2 pts high */
       /* So size has to be divided in half */
       /* and it's added the LINE_SPACING*character_height of each line */
       return(26*size/2*(1+LINE_SPACING*(line_count-1)));
     }

          Defines :
          o_text_height,
           -- o_text_basic.c - o_text_return_center(),
           -- o_text_basic.c - o_text_add(),
           -- o_text_basic.c - o_text_mirror_world_old(),
           -- o_text_basic.c - o_text_print(),
           -- o_text_basic.c - o_text_create_string(),
           -- o_text_basic.c - o_text_recreate(),
           -- o_text_basic.c - o_text_mirror_old().

          Uses :
          o_text_num_lines,  -- o_text_basic.c - o_text_num_lines().

     }

Function o_text_width()
=======================

 -- Function: o_text_width w_current string size
     You need to divide the size in half here.  FIXME consistancy.

     <<o_text_basic.c : o_text_width()>>=
     int
     o_text_width(TOPLEVEL *w_current, char *string, int size)
     {
       int width=0, max_width=0;
       int size_of_tab_in_coord;
       OBJECT *o_font_set;
       gchar *ptr;

       if (string == NULL) {
         return 0;
       }

       /* Make sure TAB_CHAR_MODEL is loaded before trying to use its text */
       /* size */
       o_font_set = g_hash_table_lookup (
         font_loaded, GUINT_TO_POINTER ((gunichar)TAB_CHAR_MODEL[0]));
       if (o_font_set == NULL) {
         o_text_load_font(w_current, (gunichar) TAB_CHAR_MODEL[0]);
         o_font_set = (OBJECT *) g_hash_table_lookup (
           font_loaded, GUINT_TO_POINTER ((gunichar)TAB_CHAR_MODEL[0]));
       }

       /* Get the maximum tab width's in coordinates */
       size_of_tab_in_coord = tab_in_chars * size * o_font_set->font_text_size;

       for (ptr = string;
            ptr != NULL && *ptr != 0;
            ptr = g_utf8_find_next_char (ptr, NULL))
       {
         gunichar c = g_utf8_get_char_validated (ptr, -1);

         switch (c) {
             case ((gunichar)'\n'):
               width = 0;
               break;
             case ((gunichar)'\t'):
               width += (size_of_tab_in_coord - (width % size_of_tab_in_coord));
               break;
             default:
               /* find current_char */
               o_font_set = g_hash_table_lookup (font_loaded,
                                                 GUINT_TO_POINTER (c));
               if (o_font_set == NULL) {
                 o_text_load_font (w_current, (gunichar)c);
                 /* let do a new search for character c */
                 o_font_set = g_hash_table_lookup (font_loaded,
                                                   GUINT_TO_POINTER (c));
               }

               if (o_font_set != NULL) {
                 width = width + size*o_font_set->font_text_size;
               }

               if (width > max_width) {
                 max_width = width;
               }
         }
       }

       /* the size is a fudge factor */
       /* Changed the return value according to a suggestion of Ales. */
       /* Yes, the -size*10 fudge factor should be removed. */
       /* return(max_width - size*10); */
       return max_width;
     }

          Defines :
          o_text_width,
           -- o_text_basic.c - o_text_return_center(),
           -- o_text_basic.c - o_text_add(),
           -- o_text_basic.c - o_text_mirror_world_old(),
           -- o_text_basic.c - o_text_create_string(),
           -- o_text_basic.c - o_text_recreate(),
           -- o_text_basic.c - o_text_mirror_old().

          Uses :
          o_text_load_font,  -- o_text_basic.c - o_text_load_font().
          tab_in_chars,  -- o_text_basic.c - global variable.

     }

Function o_text_create_string()
===============================

 -- Function: o_text_create_string w_current object_list string size color x y alignment angle

     <<o_text_basic.c : o_text_create_string()>>=
     OBJECT *
     o_text_create_string(TOPLEVEL *w_current, OBJECT *object_list,
                          char *string, int size, int color, int x, int y,
                          int alignment, int angle)
     {
       OBJECT *temp_tail=NULL;
       OBJECT *temp_list;
       OBJECT *start_of_char;
       int x_offset;
       int y_offset;
       int text_width;
       int text_height;
       int char_height;
       int line_start_x, line_start_y;
       int sign=1;
       gchar *ptr;
       OBJECT *o_font_set;

       temp_list = object_list;

       /* error condition hack */
       if (string == NULL) {
         return(NULL);
       }

       /* now read in the chars */
       temp_tail = w_current->page_current->object_tail;

       text_height = o_text_height(string, size);
       char_height = o_text_height("a", size);
       text_width = o_text_width(w_current, string, size/2);

       switch(angle) {
         case(0):
           sign = -1;
           break;
         case(90):
           sign = 1;
           break;
         case(180):
           sign = 1;
           break;
         case(270):
           sign = -1;
           break;
       }

       if (angle == 0 || angle == 180) {
         y = y - o_text_height("a", size) + text_height;

         switch(alignment) {

           case(LOWER_LEFT):
           x_offset = x;
           y_offset = y;
           break;

           case(MIDDLE_LEFT):
           x_offset = x;
           y_offset = y + sign*0.5*text_height;
           break;

           case(UPPER_LEFT):
           x_offset = x;
           y_offset = y + sign*text_height;
           break;

           case(LOWER_MIDDLE):
           x_offset = x + sign*0.5*text_width;
           y_offset = y;
           break;

           case(MIDDLE_MIDDLE):
           x_offset = x + sign*0.5*text_width;
           y_offset = y + sign*0.5*text_height;
           break;

           case(UPPER_MIDDLE):
           x_offset = x + sign*0.5*text_width;
           y_offset = y + sign*text_height;

           break;

           case(LOWER_RIGHT):
           x_offset = x + sign*text_width;
           y_offset = y;
           break;

           case(MIDDLE_RIGHT):
           x_offset = x + sign*text_width;
           y_offset = y + sign*0.5*text_height;
           break;

           case(UPPER_RIGHT):
           x_offset = x + sign*text_width;
           y_offset = y + sign*text_height;
           break;

           default:
           fprintf(stderr, "Got an invalid text alignment [%d]\n",
                   alignment);
           fprintf(stderr, "Defaulting to Lower Left");
           alignment = LOWER_LEFT;
           x_offset = x;
           y_offset = y;
           break;
         }
       } else { /* angle is 90 or 270 */
         x = x + sign*(o_text_height("a", size) - text_height);

         switch(alignment) {

           case(LOWER_LEFT):
           x_offset = x;
           y_offset = y;
           break;

           case(MIDDLE_LEFT):
           x_offset = x + sign*0.5*text_height;
           y_offset = y;
           break;

           case(UPPER_LEFT):
           x_offset = x + sign*text_height;
           y_offset = y;
           break;

           case(LOWER_MIDDLE):
           x_offset = x;
           y_offset = y - sign*0.5*text_width;
           break;

           case(MIDDLE_MIDDLE):
           x_offset = x + sign*0.5*text_height;
           y_offset = y - sign*0.5*text_width;
           break;

           case(UPPER_MIDDLE):
           x_offset = x + sign*text_height;
           y_offset = y - sign*0.5*text_width;

           break;

           case(LOWER_RIGHT):
           x_offset = x;
           y_offset = y - sign*text_width;
           break;

           case(MIDDLE_RIGHT):
           x_offset = x + sign*0.5*text_height;
           y_offset = y - sign*text_width;
           break;

           case(UPPER_RIGHT):
           x_offset = x + sign*text_height;
           y_offset = y - sign*text_width;
           break;

           default:
           fprintf(stderr, "Got an invalid text alignment [%d]\n",
                   alignment);
           fprintf(stderr, "Defaulting to Lower Left");
           alignment = LOWER_LEFT;
           x_offset = x;
           y_offset = y;
           break;
         }

       }

       switch(angle) {
         case(180):
         x_offset = x_offset - text_width;
         y_offset = y_offset - text_height;
         angle = 0;
         break;
       }

     #if DEBUG
       printf("width: %d\n", o_text_width(w_current, string, size/2));
       printf("1 %d %d\n", x_offset, y_offset);
     #endif

       line_start_x = x_offset;
       line_start_y = y_offset;

       for (ptr = string;
            ptr != NULL && *ptr != 0;
            ptr = g_utf8_find_next_char (ptr, NULL)) {
         gunichar c = g_utf8_get_char_validated (ptr, -1);

         o_font_set = g_hash_table_lookup (font_loaded, GUINT_TO_POINTER (c));
         if (o_font_set == NULL) {
           o_text_load_font(w_current, (gunichar) c);
           o_font_set = g_hash_table_lookup (font_loaded, GUINT_TO_POINTER (c));
         }

         start_of_char = temp_list;

         if (o_font_set->font_prim_objs->next != NULL) {
           int rel_char_coord;
           int size_of_tab_in_coord;
           OBJECT *o_font_set_aux;

           /* Make sure TAB_CHAR_MODEL is loaded before trying to use its text */
           /* size */
           o_font_set_aux = g_hash_table_lookup (
             font_loaded, GUINT_TO_POINTER ((gunichar)TAB_CHAR_MODEL[0]));
           if (o_font_set_aux == NULL) {
              o_text_load_font(w_current, (gunichar) TAB_CHAR_MODEL[0]);
              o_font_set_aux = g_hash_table_lookup (
                font_loaded, GUINT_TO_POINTER ((gunichar)TAB_CHAR_MODEL[0]));
            }

           /* Get the maximum tab width's in coordinates */
           size_of_tab_in_coord = tab_in_chars *
                      o_text_width(w_current, TAB_CHAR_MODEL, size/2);

           if (c != '\n' && c != '\t') {
              /* only add the character if it is not a newline or tab character */
              temp_list = o_list_copy_all(w_current,
                                          o_font_set->font_prim_objs->next,
                                          temp_list, NORMAL_FLAG);
              if (start_of_char != NULL)
                start_of_char = start_of_char->next;
           }

           /* if the character is a newline or tab, this code will "continue" */
           switch (c) {
           case '\n':
             switch (angle) {
                 case 0:
                   x_offset = line_start_x;
                   y_offset = line_start_y - char_height * LINE_SPACING;
                   line_start_x = x_offset;
                   line_start_y = y_offset;
                   continue;
                   break;
                 case 90:
                   x_offset = line_start_x + char_height * LINE_SPACING;
                   y_offset = line_start_y;
                   line_start_x = x_offset;
                   line_start_y = y_offset;
                   continue;
                   break;
                 case 180:
                   x_offset = line_start_x;
                   y_offset = line_start_y + char_height * LINE_SPACING;
                   line_start_x = x_offset;
                   line_start_y = y_offset;
                   continue;
                   break;
                 case 270:
                   x_offset = line_start_x - char_height * LINE_SPACING;
                   y_offset = line_start_y;
                   line_start_x = x_offset;
                   line_start_y = y_offset;
                   continue;
                   break;
                 default:
                   fprintf(stderr, "o_text_create_string: Angle not supported\n");
                   break;
             }
               case '\t':
     #if DEBUG
                 printf("Found tab character.\n");
                 printf("Tab size in coord: %i\n", size_of_tab_in_coord);
                 printf("Line start: %i,%i\n", line_start_x, line_start_y);
                 printf("Position: %i, %i\n", x_offset, y_offset);
     #endif
                 switch (angle) {
                     case 0:
                     case 180:
                       rel_char_coord = x_offset - line_start_x;
     #if DEBUG
                       printf("Add: %i\n", (size_of_tab_in_coord - (rel_char_coord % size_of_tab_in_coord)));
     #endif
                       x_offset += (size_of_tab_in_coord - (rel_char_coord % size_of_tab_in_coord));
                       continue;
                       break;
                     case 90:
                       rel_char_coord = y_offset - line_start_y;
     #if DEBUG
                       printf("Add: %i\n", (size_of_tab_in_coord - (rel_char_coord % size_of_tab_in_coord)));
     #endif
                       y_offset += (size_of_tab_in_coord - (rel_char_coord % size_of_tab_in_coord));
                       continue;
                       break;
                     case 270:
                       rel_char_coord = line_start_y - y_offset;
     #if DEBUG
                       printf("Add: %i\n", (size_of_tab_in_coord - (rel_char_coord % size_of_tab_in_coord)));
     #endif
                       y_offset -= (size_of_tab_in_coord - (rel_char_coord % size_of_tab_in_coord));
                       continue;
                       break;
                     default:
                       fprintf(stderr, "o_text_create_string: Angle not supported\n");
                       break;
                 }
           }

           /* does not get here if the character was a newline or tab.  This is */
           /* correct. */
           o_complex_set_color(start_of_char, color);
           o_scale(w_current, start_of_char, size/2, size/2);

           /* do this if you want to stack chars */
           /* we don't want to do that for now */
           o_text_rotate_lowlevel(w_current, x, y, angle, start_of_char);
           o_complex_world_translate(w_current,
                                     x_offset, y_offset,
                                     start_of_char);
         }

         switch(angle) {
           case(0):
             x_offset = (x_offset) +
               size/2*o_font_set->font_text_size;
             break;

           case(90):
             y_offset = (y_offset) +
               size/2*o_font_set->font_text_size;
             break;

           case(180):
             x_offset = (x_offset) -
               size/2*o_font_set->font_text_size;
             break;

           case(270):
             y_offset = (y_offset) -
               size/2*o_font_set->font_text_size;
             break;
         }
       }

       /* don't set the head */

       w_current->page_current->object_tail = temp_tail;

     #if DEBUG
       printf("2 %d %d\n", x_offset, y_offset);
     #endif
       return(object_list);
     }

          Defines :
          o_text_create_string,
           -- o_text_basic.c - o_text_add(),
           -- o_text_basic.c - o_text_print(),
           -- o_text_basic.c - o_text_recreate().

          Uses :
          o_text_height,  -- o_text_basic.c - o_text_height().
          o_text_load_font,  -- o_text_basic.c - o_text_load_font().
          o_text_rotate_lowlevel,  -- o_text_basic.c - o_text_rotate_lowlevel().
          o_text_width,  -- o_text_basic.c - o_text_width().
          tab_in_chars,  -- o_text_basic.c - global variable.

     }

Function o_text_add()
=====================

 -- Function: o_text_add w_current object_list type color x y alignment angle string size visibility show_name_value

     <<o_text_basic.c : o_text_add()>>=
     OBJECT *
     o_text_add(TOPLEVEL *w_current, OBJECT *object_list,
                char type, int color, int x, int y, int alignment,
                int angle, char *string, int size,
                int visibility, int show_name_value)
     {
       OBJECT *new_node=NULL;
       OBJECT *temp_list=NULL;
       OBJECT *temp_parent=NULL;
       TEXT *text;
       int left, right, top, bottom;
       char *name = NULL;
       char *value = NULL;
       char *output_string = NULL;

       if (string == NULL) {
         return(NULL);
       }

       new_node = s_basic_init_object("text");
       new_node->type = type;

       text = (TEXT *) malloc(sizeof(TEXT));

       text->string = g_strdup (string);
       text->length = strlen(string);
       text->size = size;
       text->alignment = alignment;
       text->x = x;
       text->y = y;
       WORLDtoSCREEN(w_current, x, y, &text->screen_x, &text->screen_y);
       text->angle = angle;

       new_node->text = text;

       /* TODO: questionable cast */
       new_node->draw_func = (void *) text_draw_func;
       /* TODO: questionable cast */
       new_node->sel_func = (void *) select_func;

       new_node->color = color;
       new_node->visibility = visibility;
       new_node->show_name_value = show_name_value;

       /* create the object in the main list */
       /* object_list points to the object */
       /* I use it below as a sanity check to make sure it was linked */
       /* properly */
       object_list = (OBJECT *) s_basic_link_object(new_node, object_list);

       /* fix up actual string here */
       if (o_attrib_get_name_value(string, &name, &value)) {

         switch(show_name_value) {
           case(SHOW_NAME_VALUE):
             output_string = g_strdup(string);
             break;

           case(SHOW_NAME):
             if (name[0] != '\0') {
               output_string = g_strdup(name);
             } else {
               /* you probably can remove this now... */
               /* since improper attributes will never get here */
               fprintf(stderr,
                       "Got an improper attribute: %s\n",
                       string);
               output_string = g_strdup("invalid");

             }
             break;

           case(SHOW_VALUE):
             if (value[0] != '\0') {
               output_string = g_strdup (value);
             } else {
               /* you probably can remove this now... */
               /* since improper attributes will never get here */
               fprintf(stderr,
                       "Got an improper attribute: %s\n",
                       string);
               output_string = g_strdup("invalid");
             }
             break;
         }
       } else {
         output_string = g_strdup(string);
       }

       /* now start working on the complex */
       temp_list = o_text_add_head();

       temp_parent = w_current->page_current->object_parent;
       /* set the addition of attributes to the head node */
       w_current->page_current->object_parent = temp_list;

       if (visibility == VISIBLE ||
           (visibility == INVISIBLE && w_current->show_hidden_text)) {
         object_list->text->prim_objs =
           o_text_create_string(w_current, temp_list,
                                output_string, size, color,
                                x, y, alignment, angle);
         object_list->text->displayed_width = o_text_width(w_current,
                                                           output_string, size/2);
         object_list->text->displayed_height = o_text_height(output_string, size);
       } else {
         object_list->text->prim_objs = NULL;
         object_list->text->displayed_width = 0;
         object_list->text->displayed_height = 0;
         s_delete(w_current, temp_list);
       }

       w_current->page_current->object_parent = temp_parent;

       get_text_bounds(w_current, object_list, &left, &top, &right, &bottom);

       /* set the new object's bounding box */
       object_list->left = left;
       object_list->top = top;
       object_list->right = right;
       object_list->bottom = bottom;

       if (name) free(name);
       if (value) free(value);
       if (output_string) free(output_string);
       return(object_list);
     }

          Defines :
          o_text_add,
           -- o_text_basic.c - o_text_copy(),
           -- o_text_basic.c - o_text_read().

          Uses :
          get_text_bounds,  -- o_text_basic.c - get_text_bounds().
          o_text_add_head,  -- o_text_basic.c - o_text_add_head().
          o_text_create_string,  -- o_text_basic.c - o_text_create_string().
          o_text_height,  -- o_text_basic.c - o_text_height().
          o_text_width,  -- o_text_basic.c - o_text_width().

     }

Function o_text_recalc()
========================

 -- Function: o_text_recalc w_current o_current

     <<o_text_basic.c : o_text_recalc()>>=
     void
     o_text_recalc(TOPLEVEL *w_current, OBJECT *o_current)
     {
       int left, right, top, bottom;

       if (o_current->visibility == INVISIBLE && !w_current->show_hidden_text) {
         return;
       }

       get_complex_bounds(w_current, o_current->text->prim_objs,
                          &left, &top, &right, &bottom);
       o_current->left = left;
       o_current->top = top;
       o_current->right = right;
       o_current->bottom = bottom;

       WORLDtoSCREEN(w_current, o_current->text->x,
                     o_current->text->y,
                     &o_current->text->screen_x,
                     &o_current->text->screen_y);
     }

          Defines :
          .

     }

Function o_text_read()
======================

 -- Function: o_text_read w_current object_list buf string version

     <<o_text_basic.c : o_text_read()>>=
     OBJECT *
     o_text_read(TOPLEVEL *w_current, OBJECT *object_list,
                 char buf[], FILE *fp, unsigned int release_ver,
                 unsigned int fileformat_ver)
     {
       char type;
       int x, y;
       int color;
       int size;
       int visibility;
       int show_name_value;
       int angle;
       int alignment;
       int num_lines = 0;
       int i;
       char* string = NULL;
       GString *textstr;

       if (fileformat_ver == 1) {
         sscanf(buf, "%c %d %d %d %d %d %d %d %d %d\n", &type, &x, &y,
                &color, &size,
                &visibility, &show_name_value,
                &angle, &alignment, &num_lines);
       } else if (release_ver < VERSION_20000220) {
         /* yes, above less than (not less than and equal) is correct. The format */
         /* change occurred in 20000220 */
         sscanf(buf, "%c %d %d %d %d %d %d %d\n", &type, &x, &y,
                &color, &size,
                &visibility, &show_name_value,
                &angle);
         alignment = LOWER_LEFT; /* older versions didn't have this */
         num_lines = 1; /* only support a single line */
       } else {
         sscanf(buf, "%c %d %d %d %d %d %d %d %d\n", &type, &x, &y,
                &color, &size,
                &visibility, &show_name_value,
                &angle, &alignment);
         num_lines = 1; /* only support a single line */
       }

       if (size == 0) {
         fprintf(stderr, "Found a zero size text string [ %c %d %d %d %d %d %d %d %d ]\n", type, x, y, color, size, visibility, show_name_value, angle, alignment);
         s_log_message("Found a zero size text string [ %c %d %d %d %d %d %d %d %d ]\n", type, x, y, color, size, visibility, show_name_value, angle, alignment);
       }

       switch(angle) {

         case(0):
         case(90):
         case(180):
         case(270):
         break;

         default:
         fprintf(stderr, "Found an unsupported text angle [ %c %d %d %d %d %d %d %d %d ]\n", type, x, y, color, size, visibility, show_name_value, angle, alignment);
         s_log_message("Found an unsupported text angle [ %c %d %d %d %d %d %d %d %d ]\n", type, x, y, color, size, visibility, show_name_value, angle, alignment);
         s_log_message("Setting angle to 0\n");
         angle=0;
         break;

       }

       switch(alignment) {
         case(LOWER_LEFT):
         case(MIDDLE_LEFT):
         case(UPPER_LEFT):
         case(LOWER_MIDDLE):
         case(MIDDLE_MIDDLE):
         case(UPPER_MIDDLE):
         case(LOWER_RIGHT):
         case(MIDDLE_RIGHT):
         case(UPPER_RIGHT):

         break;

         default:
         fprintf(stderr, "Found an unsupported text alignment [ %c %d %d %d %d %d %d %d %d ]\n", type, x, y, color, size, visibility, show_name_value, angle, alignment);
         s_log_message("Found an unsupported text alignment [ %c %d %d %d %d %d %d %d %d ]\n", type, x, y, color, size, visibility, show_name_value, angle, alignment);
         s_log_message("Setting alignment to LOWER_LEFT\n");
         alignment = LOWER_LEFT;
         break;
       }

       if (color < 0 || color > MAX_COLORS) {
         fprintf(stderr, "Found an invalid color [ %s ]\n", buf);
         s_log_message("Found an invalid color [ %s ]\n", buf);
         s_log_message("Setting color to WHITE\n");
         color = WHITE;
       }

       assert(num_lines && num_lines > 0);

       textstr = g_string_new ("");
       for (i = 0; i < num_lines; i++) {
         gchar buffer[MAX_TEXT_LINE_LENGTH];

         fgets (buffer, MAX_TEXT_LINE_LENGTH, fp);

         textstr = g_string_append (textstr, buffer);
       }
       /* retrieve the character string from the GString */
       string = g_string_free (textstr, FALSE);

       string = remove_last_nl(string);

       /* convert the character string to UTF-8 if necessary */
       if (!g_utf8_validate (string, -1, NULL)) {
         /* if it is not utf-8, it is ISO_8859-15 */
         gchar *tmp = g_convert (string, strlen (string),
                                 "UTF-8", "ISO_8859-15",
                                 NULL, NULL, NULL);
         if (tmp == NULL) {
           fprintf (stderr, "Failed to convert text string to UTF-8: %s.\n",
                    string);
         } else {
           /* successfully converted string, now use tmp as string */
           g_free (string);
           string = tmp;
         }
       }

       object_list = o_text_add(w_current, object_list, type, color, x, y,
                                alignment, angle, string,
                                size, visibility, show_name_value);
       g_free(string);

       return(object_list);
     }

          Defines :
          .

          Uses :
          o_text_add,  -- o_text_basic.c - o_text_add().

     }

Function o_text_set_info_font()
===============================

 -- Function: o_text_set_info_font buf

     <<o_text_basic.c : o_text_set_info_font()>>=
     void
     o_text_set_info_font(char buf[])
     {
       char type;
       int width;
       gunichar character=0;
       gchar *buf_ptr;
       int special=0;
       char *string;
       OBJECT *o_font_set;

       string = remove_nl (buf);

       /* parse the font info: */
       buf_ptr = (gchar*)string;
       /*   - type */
       type = *buf_ptr++;
       g_assert (type == INFO_FONT);
       while (buf_ptr != NULL && *buf_ptr == ' ') buf_ptr++;
       /*   - character */
       if (buf_ptr != NULL && *buf_ptr != '\0') {
         character = g_utf8_get_char_validated (buf_ptr, -1);
         if (character == (gunichar)-1) {
           s_log_message (
             "Failed to validate utf-8 character in font definition: \"%s\".\n",
             string);
           return;
         }
         /* move buf_ptr just after character */
         buf_ptr = g_utf8_find_next_char (buf_ptr, NULL);
       }
       while (buf_ptr != NULL && *buf_ptr == ' ') buf_ptr++;
       /*   - width and special */
       if (buf_ptr != NULL) {
         sscanf (buf_ptr, "%d %d\n", &width, &special);
       }

       /* deal with special characters */
       if (special == 1) {
         switch (character) {
           case ((gunichar)'_'):
           /* space */
           character = (gunichar)' ';
           break;
           case ((gunichar)'n'):
           /* newline */
           character = (gunichar)'\n';
           break;
         }
       }

       o_font_set = g_hash_table_lookup (font_loaded,
                                         GUINT_TO_POINTER ((gunichar)character));
       if (o_font_set != NULL) {
         o_font_set->font_text_size = width;
       } else {
         gchar outbuf[7];
         gint l = g_unichar_to_utf8 (character, outbuf);
         outbuf[l] = '\0';
         fprintf(stderr,
                 "o_text_set_info_font: character %s not found!!!\n", outbuf);
       }

     }

          Defines :
          .

     }

Function o_text_save()
======================

 -- Function: o_text_save buf object

     <<o_text_basic.c : o_text_save()>>=
     char *
     o_text_save(OBJECT *object)
     {
       int x, y;
       int color;
       int size;
       char *string;
       char *buf;
       int num_lines;

       x = object->text->x;
       y = object->text->y;

       string = object->text->string;
       size = object->text->size;

       /* Use the right color */
       if (object->saved_color == -1) {
         color = object->color;
       } else {
         color = object->saved_color;
       }

       /* string can have multiple lines (seperated by \n's) */
       num_lines = o_text_num_lines(string);

       buf = g_strdup_printf("%c %d %d %d %d %d %d %d %d %d\n%s", object->type,
                             x, y, color, size, object->visibility,
                             object->show_name_value, object->text->angle,
                             object->text->alignment, num_lines, string);

       return(buf);
     }

          Defines :
          .

          Uses :
          o_text_num_lines,  -- o_text_basic.c - o_text_num_lines().

     }

Function o_text_recreate()
==========================

 -- Function: o_text_recreate w_current o_current

     <<o_text_basic.c : o_text_recreate()>>=
     void
     o_text_recreate(TOPLEVEL *w_current, OBJECT *o_current)
     {
       OBJECT *temp_parent;
       char *name = NULL;
       char *value = NULL;
       char *output_string = NULL;

       if (o_attrib_get_name_value(o_current->text->string, &name, &value)) {
         switch(o_current->show_name_value) {
           case(SHOW_NAME_VALUE):
             output_string = g_strdup(o_current->text->string);
             break;

           case(SHOW_NAME):
             if (name[0] != '\0') {
               output_string = g_strdup(name);
             } else {
               /* you probably can remove this now... */
               /* since improper attributes will never get here */
               fprintf(stderr,
                       "Got an improper attribute: %s\n",
                       o_current->text->string);
               output_string = g_strdup("invalid");
             }
             break;

           case(SHOW_VALUE):
             if (value[0] != '\0') {
               output_string = g_strdup(value);
             } else {
               /* you probably can remove this now... */
               /* since improper attributes will never get here */
               fprintf(stderr,
                       "Got an improper attribute: %s\n",
                       o_current->text->string);
               output_string = g_strdup("invalid");
             }
             break;
         }
       } else {
         output_string = g_strdup(o_current->text->string);
       }

       o_list_delete_rest(w_current, o_current->text->prim_objs);

       temp_parent = w_current->page_current->object_parent;
       /* set the addition of attributes to the head node */
       w_current->page_current->object_parent = o_current->text->prim_objs;

       if (o_current->visibility == VISIBLE ||
           (o_current->visibility == INVISIBLE && w_current->show_hidden_text)) {

         /* need to create that head node if complex is null */
         if (o_current->text->prim_objs == NULL) {
           o_current->text->prim_objs = o_text_add_head();
         }

         o_current->text->prim_objs =
           o_text_create_string(w_current,
                                o_current->text->prim_objs,
                                output_string,
                                o_current->text->size,
                                o_current->color,
                                o_current->text->x,
                                o_current->text->y,
                                o_current->text->alignment,
                                o_current->text->angle);

         o_complex_set_saved_color_only(o_current->text->prim_objs,
                                        o_current->saved_color);
         o_current->text->displayed_width = o_text_width(w_current,
                                                         output_string,
                                                         o_current->text->size/2);
         o_current->text->displayed_height = o_text_height(output_string,
                                                           o_current->text->size);
       } else {
         /* make sure list is truely free */
         s_delete_list_fromstart(w_current, o_current->text->prim_objs);
         o_current->text->prim_objs = NULL;
         o_current->text->displayed_width = 0;
         o_current->text->displayed_height = 0;
       }

       w_current->page_current->object_parent = temp_parent;
       if (name) free(name);
       if (value) free(value);
       if (output_string) free(output_string);
     }

          Defines :
          o_text_recreate,
           -- o_text_basic.c - o_text_change_angle(),
           -- o_text_basic.c - o_text_rotate(),
           -- o_text_basic.c - o_text_mirror_world_old(),
           -- o_text_basic.c - o_text_rotate_world(),
           -- o_text_basic.c - o_text_mirror_world(),
           -- o_text_basic.c - o_text_mirror_old().

          Uses :
          o_text_add_head,  -- o_text_basic.c - o_text_add_head().
          o_text_create_string,  -- o_text_basic.c - o_text_create_string().
          o_text_height,  -- o_text_basic.c - o_text_height().
          o_text_width,  -- o_text_basic.c - o_text_width().

     }

Function o_text_translate_world()
=================================

 -- Function: o_text_translate_world w_current x1 y1 o_current

     <<o_text_basic.c : o_text_translate_world()>>=
     void
     o_text_translate_world(TOPLEVEL *w_current, int x1, int y1, OBJECT *o_current)
     {
       int screen_x, screen_y;
       int left, right, top, bottom;

       o_current->text->x = o_current->text->x + x1;
       o_current->text->y = o_current->text->y + y1;

       /* update screen coords */
       WORLDtoSCREEN(w_current, o_current->text->x, o_current->text->y,
                     &screen_x, &screen_y);

       o_current->text->screen_x = screen_x;
       o_current->text->screen_y = screen_y;

       o_complex_world_translate(w_current, x1, y1, o_current->text->prim_objs);

       /* update bounding box */
       /* do it */
       get_text_bounds(w_current, o_current, &left, &top, &right, &bottom);

       o_current->left = left;
       o_current->top = top;
       o_current->right = right;
       o_current->bottom = bottom;
     }

          Defines :
          o_text_translate_world,
           -- o_text_basic.c - o_text_change_angle(),
           -- o_text_basic.c - o_text_rotate_lowlevel(),
           -- o_text_basic.c - o_text_rotate(),
           -- o_text_basic.c - o_text_mirror_world_old(),
           -- o_text_basic.c - o_text_rotate_world(),
           -- o_text_basic.c - o_text_mirror_old().

          Uses :
          get_text_bounds,  -- o_text_basic.c - get_text_bounds().

     }

Function o_text_copy()
======================

 -- Function: o_text_copy w_current list_tail o_current

     <<o_text_basic.c : o_text_copy()>>=
     OBJECT *
     o_text_copy(TOPLEVEL *w_current, OBJECT *list_tail, OBJECT *o_current)
     {
       OBJECT *new_obj;
       int color;

       if (o_current->saved_color == -1) {
         color = o_current->color;
       } else {
         color = o_current->saved_color;
       }

       new_obj = o_text_add(w_current, list_tail, OBJ_TEXT,
                            color,
                            o_current->text->x, o_current->text->y,
                            o_current->text->alignment,
                            o_current->text->angle,
                            o_current->text->string,
                            o_current->text->size,
                            o_current->visibility,
                            o_current->show_name_value);

       return(new_obj);
     }

          Defines :
          .

          Uses :
          o_text_add,  -- o_text_basic.c - o_text_add().

     }

Function o_text_freeallfonts()
==============================

 -- Function: o_text_freeallfonts w_current

     <<o_text_basic.c : o_text_freeallfonts()>>=
     static gboolean
     delete_font_set (gpointer key, gpointer value, gpointer user_data)
     {
       OBJECT *tmp = (OBJECT*)value;
       TOPLEVEL *toplevel = (TOPLEVEL*)user_data;

       if (tmp != NULL) {
         if (tmp->font_prim_objs != NULL) {
           s_delete_list_fromstart (toplevel, tmp->font_prim_objs);
           tmp->font_prim_objs = NULL;
         }
         /* do not use s_delete() as tmp is not fully initialized */
         g_free (tmp->name);

         /* Do not free tmp here since it will be freed with the function */
         /* that was specified when the hash table was created. */
       }

       return TRUE;
     }

     void
     o_text_freeallfonts(TOPLEVEL *w_current)
     {
       /* destroy the char-to-objects hastable */
       g_hash_table_foreach_remove (font_loaded,
                                    delete_font_set,
                                    w_current);
       g_hash_table_destroy (font_loaded);
       font_loaded = NULL;

       /* destroy the font-to-filename hashtable */
       g_hash_table_destroy (font_char_to_file);
       font_char_to_file = NULL;

     }

          Defines :
          .

          Uses :
          font_char_to_file,  -- o_text_basic.c - global variable.

     }

Function o_text_print_text_width()
==================================

 -- Function: o_text_print_text_width fp output_string

     <<o_text_basic.c : o_text_print_text_width()>>=
     void
     o_text_print_text_width(FILE *fp, char *output_string)
     {
       int len, i, j;
       int starting_character = 0;
       int num_lines, line_number;
       int max_len = -1;
       char *single_line = NULL;
       char *max_length_line = NULL;
       int single_len;

       /* break up the string and find the longest string */
       num_lines = o_text_num_lines(output_string);
       single_line = g_strdup (output_string);  /* larger than needed */
       len = strlen(output_string);
       for (line_number = 0; line_number < num_lines; line_number++)
       {
         j = 0;
         /* break up the string into lines */
         for (i = starting_character; i < len; i++)
         {
           if (output_string[i] != '\n' && output_string[i] != '\0')
           {
             single_line[j] = output_string[i];
           }
           else
           {
             starting_character = i + 1;
             break;
           }
           j++;
         }
         single_line[j] = '\0';

         single_len = strlen(single_line);
         if (single_len > max_len)
         {
           max_len = strlen(single_line);
           if (max_length_line) free(max_length_line);
           max_length_line = g_strdup (single_line);
         }
       }

       fprintf(fp, "(");
       len = strlen(max_length_line);
       for (i = 0 ; i < len; i++) {
         if (max_length_line[i] == '(' || max_length_line[i] == ')' || max_length_line[i] == '\\' ) {
           fprintf(fp, "\\");
         }

         fprintf(fp, "%c", max_length_line[i]);
       }

       /* convert width to mils */
       /* .95 is a fudge factor */
       fprintf(fp, ") stringwidth pop\n");

       if (single_line) free(single_line);
       if (max_length_line) free(max_length_line);
     }

          Defines :
          o_text_print_text_width,
           -- o_text_basic.c - o_text_print().

          Uses :
          o_text_num_lines,  -- o_text_basic.c - o_text_num_lines().

     }

Function o_text_print_text_height()
===================================

 -- Function: o_text_print_text_height fp size

     <<o_text_basic.c : o_text_print_text_height()>>=
     void
     o_text_print_text_height(FILE *fp, int size)
     {
       fprintf(fp, "%f\n", (float) size);
     }

          Defines :
          o_text_print_text_height,
           -- o_text_basic.c - o_text_print().

     }

Function o_text_print_text_height_full()
========================================

 -- Function: o_text_print_text_height_full fp string size

     <<o_text_basic.c : o_text_print_text_height_full()>>=
     void
     o_text_print_text_height_full(FILE *fp, char *string, int size)
     {
       int num_lines = o_text_num_lines(string);
       fprintf(fp, "%f\n", (float) (size*num_lines +
               size * LINE_SPACING * (num_lines - 1)));
     }

          Defines :
          o_text_print_text_height_full,
           -- o_text_basic.c - o_text_print().

          Uses :
          o_text_num_lines,  -- o_text_basic.c - o_text_num_lines().

     }

Function o_text_print_text_string()
===================================

 -- Function: o_text_print_text_string w_current fp o_current origin_x origin_y

     <<o_text_basic.c : o_text_print_text_string()>>=
     void
     o_text_print_text_string(FILE *fp, char *string)
     {
       int len, i;

       if (!string)
       {
         return;
       }

       len = strlen(string);

       fprintf(fp, "(");
       for (i = 0 ; i < len; i++)
       {
         if (string[i] == '(' || string[i] == ')' || string[i] == '\\' )
         {
           fprintf(fp, "\\");
         }

         fprintf(fp, "%c", string[i]);
       }

       fprintf(fp, ") show\n");
     }

          Defines :
          o_text_print_text_string,
           -- o_text_basic.c - o_text_print().

     }

Function o_text_print()
=======================

 -- Function: o_text_print w_current fp o_current origin_x origin_y

     <<o_text_basic.c : o_text_print()>>=
     void
     o_text_print(TOPLEVEL *w_current, FILE *fp, OBJECT *o_current,
                  int origin_x, int origin_y)
     {
       char *output_string = NULL;
       char *name = NULL;
       char *value = NULL;
       int sign=1;
       int len;
       int i, j;
       int x, y;
       int num_lines, line_number, starting_character;
       int full_text_height;
       char *single_line = NULL;

       if (!o_current->text->string) {
         return;
       }

       if (w_current->print_color) {
         f_print_set_color(fp, o_current->color);
       }

       fprintf(fp, "/Helvetica findfont\n");
       fprintf(fp, "%f scalefont\n", (float) o_current->text->size*1.4);
       fprintf(fp, "setfont\n");
       fprintf(fp, "\n");

       /* fprintf(fp, "newpath\n");*/

       if (o_attrib_get_name_value(o_current->text->string, &name, &value)) {
         switch(o_current->show_name_value) {
           case(SHOW_NAME_VALUE):
             output_string = g_strdup(o_current->text->string);
             break;

           case(SHOW_NAME):
             if (name[0] != '\0') {
               output_string = g_strdup(name);
             } else {
               fprintf(stderr,
                       "Got an improper attribute: %s\n",
                       o_current->text->string);
               output_string = g_strdup("invalid");
             }
             break;

           case(SHOW_VALUE):
             if (value[0] != '\0') {
               output_string = g_strdup(value);
             } else {
               /* you probably can remove this now... */
               /* since improper attributes will never get here */
               fprintf(stderr,
                       "Got an improper attribute: %s\n",
                       o_current->text->string);
               output_string = g_strdup("invalid");
             }
             break;
         }
       } else {
         output_string = g_strdup(o_current->text->string);
       }

       switch(o_current->text->angle) {
         case(0):
         sign = -1;
         break;
         case(90):
         sign = 1;
         break;
         case(180):
         sign = 1;
         break;
         case(270):
         sign = -1;
         break;
       }

       full_text_height = o_text_height(output_string, o_current->text->size);
       num_lines = o_text_num_lines(output_string);

       x = o_current->text->x;
       y = o_current->text->y;
       if (o_current->text->angle == 0 || o_current->text->angle == 180 ) {

         /* not quite sure about the second part of next statement. */
         /* full_text_height should take the spacing into account, but it doesn't */
         y = y - o_text_height("a", o_current->text->size) + full_text_height +
           (num_lines-1)*o_current->text->size*LINE_SPACING;

         switch(o_current->text->alignment) {
           case(LOWER_LEFT):
             fprintf(fp, "%d mils %d mils moveto\n", x, y);
             /*                              x_offset = x; */
             /*                              y_offset = y; */
             break;

           case(MIDDLE_LEFT):
             fprintf(fp, "%d mils %d mils\n", x, y);
             o_text_print_text_height_full(fp, output_string,
                                            o_current->text->size);
             fprintf(fp, ".714285 mul\n"); /* height adjustment factor */
             fprintf(fp, ".5 mul %d mul add moveto\n", sign);
             /*                              x_offset = x; */
             /*                              y_offset = y + sign*0.5*text_height; */
             break;

           case(UPPER_LEFT):
             fprintf(fp, "%d mils %d mils\n", x, y);
             o_text_print_text_height_full(fp, output_string,
                                           o_current->text->size);
             fprintf(fp, ".714285 mul\n"); /* height adjustment factor */
             fprintf(fp, "%d mul add moveto\n", sign);
             /*                              x_offset = x; */
             /*                              y_offset = y + sign*text_height; */
             break;

           case(LOWER_MIDDLE):
             fprintf(fp, "%d mils ", x);

             o_text_print_text_width(fp, output_string);
             fprintf(fp, ".5 mul %d mul add\n", sign);
             fprintf(fp, "%d mils\n", y);
             fprintf(fp, "moveto\n");
             /*                              x_offset = x + sign*0.5*text_width; */
             /*                              y_offset = y; */
             break;

           case(MIDDLE_MIDDLE):
             fprintf(fp, "%d mils ", x);

             o_text_print_text_width(fp, output_string);
             fprintf(fp, ".5 mul %d mul add\n", sign);
             fprintf(fp, "%d mils\n", y);
             o_text_print_text_height_full(fp, output_string,
                                           o_current->text->size);
             fprintf(fp, ".714285 mul\n"); /* height adjustment factor */
             fprintf(fp, ".5 mul %d mul add moveto\n", sign);
             /*                              x_offset = x + sign*0.5*text_width; */
             /*                              y_offset = y + sign*0.5*text_height; */
             break;

           case(UPPER_MIDDLE):
             fprintf(fp, "%d mils ", x);

             o_text_print_text_width(fp, output_string);
             fprintf(fp, ".5 mul %d mul add\n", sign);
             fprintf(fp, "%d mils\n", y);
             o_text_print_text_height_full(fp, output_string,
                                           o_current->text->size);
             fprintf(fp, ".714285 mul\n"); /* height adjustment factor */
             fprintf(fp, "%d mul add moveto\n", sign);
             /*                              x_offset = x + sign*0.5*text_width; */
             /*                              y_offset = y + sign*text_height; */
             break;

           case(LOWER_RIGHT):
             fprintf(fp, "%d mils ", x);

             o_text_print_text_width(fp, output_string);
             fprintf(fp, "%d mul add\n", sign);
             fprintf(fp, "%d mils\n", y);
             fprintf(fp, "moveto\n");
             /*                              x_offset = x + sign*text_width; */
             /*                              y_offset = y; */
             break;

           case(MIDDLE_RIGHT):
             fprintf(fp, "%d mils ", x);

             o_text_print_text_width(fp, output_string);
             fprintf(fp, "%d mul add\n", sign);
             fprintf(fp, "%d mils\n", y);
             o_text_print_text_height_full(fp, output_string,
                                           o_current->text->size);
             fprintf(fp, ".714285 mul\n"); /* height adjustment factor */
             fprintf(fp, ".5 mul %d mul add moveto\n", sign);
             /*                              x_offset = x + sign*text_width; */
             /*                              y_offset = y + sign*0.5*text_height; */
             break;

           case(UPPER_RIGHT):
             fprintf(fp, "%d mils ", x);

             o_text_print_text_width(fp, output_string);
             fprintf(fp, "%d mul add\n", sign);
             fprintf(fp, "%d mils\n", y);
             o_text_print_text_height_full(fp, output_string,
                                           o_current->text->size);
             fprintf(fp, ".714285 mul\n"); /* height adjustment factor */
             fprintf(fp, "%d mul add moveto\n", sign);
             /*                              x_offset = x + sign*text_width; */
             /*                              y_offset = y + sign*text_height; */
             break;
         }

       } else if (o_current->text->angle == 90 || o_current->text->angle == 270) {

         /* not quite sure about the second part of next statement. */
         /* full_text_height should take the spacing into account, but it doesn't */
         x = x + sign*(o_text_height("a", o_current->text->size) -
                       full_text_height - (num_lines-1)*o_current->text->size*LINE_SPACING);

         switch(o_current->text->alignment) {

           case(LOWER_LEFT):
           fprintf(fp, "%d mils %d mils moveto\n", x, y);
           /*                                x_offset = x; */
           /*                                y_offset = y; */
           break;

           case(MIDDLE_LEFT):
           fprintf(fp, "%d mils\n", x);

           o_text_print_text_height_full(fp, output_string, o_current->text->size);
           fprintf(fp, ".714285 mul\n"); /* height adjustment factor */
           fprintf(fp, ".5 mul %d mul add\n", sign);
           fprintf(fp, "%d mils moveto\n", y);
           /*                                x_offset = x + sign*0.5*text_height; */
           /*                                y_offset = y; */
           break;

           case(UPPER_LEFT):
           fprintf(fp, "%d mils\n", x);

           o_text_print_text_height_full(fp, output_string, o_current->text->size);
           fprintf(fp, ".714285 mul\n"); /* height adjustment factor */
           fprintf(fp, "%d mul add\n", sign);
           fprintf(fp, "%d mils moveto\n", y);
           /*                                x_offset = x + sign*text_height; */
           /*                                y_offset = y; */
           break;

           case(LOWER_MIDDLE):
           fprintf(fp, "%d mils\n", x);
           fprintf(fp, "%d mils\n", y);
           o_text_print_text_width(fp, output_string);
           fprintf(fp, "0.5 mul %d mul sub moveto\n", sign);
           /*                                x_offset = x; */
           /*                                y_offset = y - sign*0.5*text_width; */
           break;

           case(MIDDLE_MIDDLE):
           fprintf(fp, "%d mils\n", x);
           o_text_print_text_height_full(fp, output_string, o_current->text->size);
           fprintf(fp, ".714285 mul\n"); /* height adjustment factor */
           fprintf(fp, "0.5 mul %d mul add\n", sign);
           fprintf(fp, "%d mils\n", y);
           o_text_print_text_width(fp, output_string);
           fprintf(fp, "0.5 mul %d mul sub moveto\n", sign);
           /*                                x_offset = x + sign*0.5*text_height; */
           /*                                y_offset = y - sign*0.5*text_width; */
           break;

           case(UPPER_MIDDLE):
           fprintf(fp, "%d mils\n", x);
           o_text_print_text_height_full(fp, output_string, o_current->text->size);
           fprintf(fp, ".714285 mul\n"); /* height adjustment factor */
           fprintf(fp, "%d mul add\n", sign);
           fprintf(fp, "%d mils\n", y);
           o_text_print_text_width(fp, output_string);
           fprintf(fp, "0.5 mul %d mul sub moveto\n", sign);
           /*                                x_offset = x + sign*text_height; */
           /*                                y_offset = y - sign*0.5*text_width; */

           break;

           case(LOWER_RIGHT):
           fprintf(fp, "%d mils\n", x);
           fprintf(fp, "%d mils\n", y);
           o_text_print_text_width(fp, output_string);
           fprintf(fp, "%d mul sub moveto\n", sign);
           /*                                x_offset = x; */
           /*                                y_offset = y - sign*text_width; */
           break;

           case(MIDDLE_RIGHT):
           fprintf(fp, "%d mils\n", x);
           o_text_print_text_height_full(fp, output_string, o_current->text->size);
           fprintf(fp, ".714285 mul\n"); /* height adjustment factor */
           fprintf(fp, "0.5 mul %d mul add\n", sign);
           fprintf(fp, "%d mils\n", y);
           o_text_print_text_width(fp, output_string);
           fprintf(fp, "%d mul sub moveto\n", sign);
           /*                                x_offset = x + sign*0.5*text_height; */
           /*                                y_offset = y - sign*text_width; */
           break;

           case(UPPER_RIGHT):
           fprintf(fp, "%d mils\n", x);
           o_text_print_text_height_full(fp, output_string, o_current->text->size);
           fprintf(fp, ".714285 mul\n"); /* height adjustment factor */
           fprintf(fp, "%d mul add\n", sign);
           fprintf(fp, "%d mils\n", y);
           o_text_print_text_width(fp, output_string);
           fprintf(fp, "%d mul sub moveto\n", sign);
           /*                                x_offset = x + sign*text_height; */
           /*                                y_offset = y - sign*text_width; */
           break;
         }

       }

       /* correct 180 degree rotated text so it is in the right position */
       if (o_current->text->angle == 180)
       {
         o_text_print_text_width(fp, output_string);
         fprintf(fp, "-1.0 mul\n"); /* x distance back */
         o_text_print_text_height_full(fp, output_string, o_current->text->size);
         fprintf(fp, ".714285 mul\n"); /* height adjustment factor */
         fprintf(fp, "-1.0 mul\n"); /* y distance down */
         fprintf(fp, "rmoveto\n");
       }

       starting_character = 0;
       single_line = g_strdup (output_string);  /* larger than needed */
       len = strlen(output_string);
       for (line_number = 0; line_number < num_lines; line_number++)
       {

         j = 0;
         /* break up the string into lines */
         for (i = starting_character; i < len; i++)
         {
           if (output_string[i] != '\n' && output_string[i] != '\0')
           {
             single_line[j] = output_string[i];
           }
           else
           {
             starting_character = i + 1;
             break;
           }
           j++;
         }
         single_line[j] = '\0';

         /* Push the current position onto the stack */
         fprintf(fp, "currentpoint\n");

         if (o_current->text->angle == 90 || o_current->text->angle == 270) {
           fprintf(fp, "gsave\n%d rotate\n", o_current->text->angle);
         }

         /* output the single line */
         o_text_print_text_string(fp, single_line);

         if (o_current->text->angle == 90 || o_current->text->angle == 270) {
           fprintf(fp, "grestore\n");
         }

         /* Correct the position on the stack to go to the next line */
         if (line_number != num_lines - 1)
         {
           /* only do this if there are more text lines to output */

           switch (o_current->text->angle) {

             case 0:
               /* stack: x y */
               o_text_print_text_height(fp, o_current->text->size);
               fprintf(fp, "-1 mul %f mul add moveto\n", LINE_SPACING);
               /* stack: empty */

               /* x = line_start_x; */
               /* y = line_start_y - char_height * 2; */
               break;

             case 90:
               fprintf(fp, "exch\n"); /* stack: y x */
               o_text_print_text_height(fp, o_current->text->size);
               fprintf(fp, "%f mul add exch moveto\n", LINE_SPACING);
               /* stack: empty */
               /* x = line_start_x + char_height * 2; */
               /* y = line_start_y; */
               break;

             case 180:
               /* stack: x y */
               o_text_print_text_height(fp, o_current->text->size);
               fprintf(fp, "%f mul sub moveto\n", LINE_SPACING);
               /* stack: empty */

               /* x = line_start_x; */
               /* y = line_start_y + char_height * 2; */
               break;

             case 270:
               fprintf(fp, "exch\n"); /* stack: y x */
               o_text_print_text_height(fp, o_current->text->size);
               fprintf(fp, "%f mul sub exch moveto\n", LINE_SPACING);
               /* stack: empty */

               /* x = line_start_x - char_height * 2; */
               /* y = line_start_y; */
               break;

             default:
               fprintf(stderr, "o_text_create_string: Angle not supported\n");
               break;
           }

         }
         else
         {
           /* get rid of the last unused current point */
           fprintf(fp, "pop pop\n");

         }
       }

       if (output_string) free(output_string);
       if (name) free(name);
       if (value) free(value);
       if (single_line) free(single_line);
     }

          Defines :
          .

          Uses :
          o_text_create_string,  -- o_text_basic.c - o_text_create_string().
          o_text_height,  -- o_text_basic.c - o_text_height().
          o_text_num_lines,  -- o_text_basic.c - o_text_num_lines().
          o_text_print_text_height,  -- o_text_basic.c - o_text_print_text_height().
          o_text_print_text_height_full,  -- o_text_basic.c - o_text_print_text_height_full().
          o_text_print_text_string,  -- o_text_basic.c - o_text_print_text_string().
          o_text_print_text_width,  -- o_text_basic.c - o_text_print_text_width().

     }

Function o_text_rotate_lowlevel()
=================================

 -- Function: o_text_rotate_lowlevel w_current world_centerx world_centery angle object

     <<o_text_basic.c : o_text_rotate_lowlevel()>>=
     /* takes world coords as the center point as well as a true angle */
     void
     o_text_rotate_lowlevel(TOPLEVEL *w_current, int world_centerx,
                            int world_centery, int angle, OBJECT *object)
     {
       OBJECT *o_current=NULL;

       /* translate object to origin */
       /* o_text_translate_world(w_current, -world_centerx, -world_centery, object);*/

       /* rotate_point_90(object->text->x, object->text->y, &newx, &newy);*/

       /* o_text_translate_world(w_current, world_centerx, world_centery, object);*/

       o_current = object;

       while ( o_current != NULL ) {
         switch(o_current->type) {
           case(OBJ_LINE):
             o_line_rotate_world(w_current, 0, 0, angle, o_current);
             break;
         }
         o_current=o_current->next;
       }
     }

          Defines :
          o_text_rotate_lowlevel,
           -- o_text_basic.c - o_text_change_angle(),
           -- o_text_basic.c - o_text_create_string().

          Uses :
          o_text_translate_world,  -- o_text_basic.c - o_text_translate_world().

     }

Function o_text_rotate_world()
==============================

 -- Function: o_text_rotate_world w_current world_centerx world_centery angle angle_change object

     <<o_text_basic.c : o_text_rotate_world()>>=
     void
     o_text_rotate_world(TOPLEVEL *w_current, int world_centerx, int world_centery,
                         int angle, int angle_change, OBJECT *object)
     {
       int newx, newy;
       int origx, origy;
       int x, y;

       origx = object->text->x;
       origy = object->text->y;

       object->text->angle = ( object->text->angle + angle_change ) % 360;

     #if DEBUG
       printf("rotating text angle: %d\n", angle);
       printf("rotating text angle_change: %d\n", angle_change);
       printf("final text angle: %d\n",  object->text->angle);
     #endif

       x = origx + (-world_centerx);
       y = origy + (-world_centery);

       rotate_point_90(x, y, angle_change, &newx, &newy);

       x = newx + (world_centerx);
       y = newy + (world_centery);

       o_text_translate_world(w_current, x-object->text->x, y-object->text->y, object);

       o_text_recreate(w_current, object);
     }

          Defines :
          o_text_rotate_world,
           -- o_text_basic.c - o_text_change_angle().

          Uses :
          o_text_recreate,  -- o_text_basic.c - o_text_recreate().
          o_text_translate_world,  -- o_text_basic.c - o_text_translate_world().

     }

Function o_text_rotate()
========================

 -- Function: o_text_rotate w_current centerx centery angle angle_change object

     <<o_text_basic.c : o_text_rotate()>>=
     void
     o_text_rotate(TOPLEVEL *w_current, int centerx, int centery, int angle,
                   int angle_change, OBJECT *object)
     {
       int newx, newy;
       int origx, origy;
       int world_centerx, world_centery;
       int x, y;

       SCREENtoWORLD(w_current, centerx, centery,
                     &world_centerx,
                     &world_centery);

       origx = object->text->x;
       origy = object->text->y;

       object->text->angle = angle;

       x = origx + (-world_centerx);
       y = origy + (-world_centery);

       rotate_point_90(x, y, angle_change, &newx, &newy);

       x = newx + (world_centerx);
       y = newy + (world_centery);

       o_text_translate_world(w_current, x-object->text->x, y-object->text->y, object);

       o_text_recreate(w_current, object);
     }

          Defines :
          .

          Uses :
          o_text_recreate,  -- o_text_basic.c - o_text_recreate().
          o_text_translate_world,  -- o_text_basic.c - o_text_translate_world().

     }

Function o_text_mirror_old()
============================

 -- Function: o_text_mirror_old w_current centerx centery object

     <<o_text_basic.c : o_text_mirror_old()>>=
     void
     o_text_mirror_old(TOPLEVEL *w_current, int centerx, int centery,
                       OBJECT *object)
     {
       int newx=0, newy=0;
       int origx, origy;
       int world_centerx, world_centery;
       int x, y;
       char *output_string = NULL;
       char *name = NULL;
       char *value = NULL;
       int height_mod=0;
       int sign=1;

       SCREENtoWORLD(w_current, centerx, centery,
                     &world_centerx,
                     &world_centery);

       origx = object->text->x;
       origy = object->text->y;

       x = origx + (-world_centerx);
       y = origy + (-world_centery);

       if (o_attrib_get_name_value(object->text->string, &name, &value)) {
         switch(object->show_name_value) {
           case(SHOW_NAME_VALUE):
             output_string = g_strdup(object->text->string);
             break;

           case(SHOW_NAME):
             if (name[0] != '\0') {
               output_string = g_strdup(name);
             } else {
               /* you probably can remove this now... */
               /* since improper attributes will never get here */
               fprintf(stderr,
                       "Got an improper attribute: %s\n",
                       object->text->string);
               output_string = g_strdup("invalid");
             }
             break;

           case(SHOW_VALUE):
             if (value[0] != '\0') {
               output_string = g_strdup(value);
             } else {
               /* you probably can remove this now... */
               /* since improper attributes will never get here */
               fprintf(stderr,
                       "Got an improper attribute: %s\n",
                       object->text->string);
               output_string = g_strdup("invalid");
             }
             break;
         }
       } else {
         output_string = g_strdup(object->text->string);
       }

       switch(object->text->alignment) {
         case(LOWER_LEFT):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = 1;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           height_mod = 0;
         }
         break;

         case(MIDDLE_LEFT):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = 1;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           height_mod = o_text_height(object->text->string,
                                      object->text->size);
         }
         break;

         case(UPPER_LEFT):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = 1;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           height_mod = 2*o_text_height(object->text->string,
                                        object->text->size);
         }
         break;

         case(LOWER_MIDDLE):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = 0.5;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           sign = 1;
           height_mod = 0;
         }
         break;

         case(MIDDLE_MIDDLE):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = 0.5;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           sign = 1;
           height_mod = o_text_height(object->text->string,
                                      object->text->size);
         }
         break;

         case(UPPER_MIDDLE):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = 0.5;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           sign = 1;
           height_mod = 2*o_text_height(object->text->string,
                                        object->text->size);
         }
         break;

         case(LOWER_RIGHT):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = -1;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           sign = 1;
           height_mod = 0;
         }
         break;

         case(MIDDLE_RIGHT):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = -1;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           sign = 1;
           height_mod = o_text_height(object->text->string,
                                      object->text->size);
         }
         break;

         case(UPPER_RIGHT):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = -1;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           sign = 1;
           height_mod = 2*o_text_height(object->text->string,
                                        object->text->size);
         }
         break;
       }

       switch (object->text->angle) {

         case(0):
         newx = -(x + sign*o_text_width(w_current,
                                        output_string,
                                        object->text->size/2));
         break;

         case(90):
         newx = -(x - sign*o_text_height(object->text->string,
                                         object->text->size)+
                  height_mod);
         break;

         case(180):
         newx = -(x - sign*o_text_width(w_current,
                                        output_string,
                                        object->text->size/2));
         break;

         case(270):
         newx = -(x + sign*o_text_height(object->text->string,
                                         object->text->size)-
                  height_mod);
         break;

         default:
         fprintf(stderr, "Invalid angle specified!\n");
         return;
         break;

       }

       newy = y;

       x = newx + (world_centerx);
       y = newy + (world_centery);

       /* don't know if this is needed? */
       o_text_translate_world(w_current, x-object->text->x, y-object->text->y, object);

       o_text_recreate(w_current, object);
       if (output_string) free(output_string);
       if (name) free(name);
       if (value) free(value);
     }

          Defines :
          .

          Uses :
          o_text_height,  -- o_text_basic.c - o_text_height().
          o_text_recreate,  -- o_text_basic.c - o_text_recreate().
          o_text_translate_world,  -- o_text_basic.c - o_text_translate_world().
          o_text_width,  -- o_text_basic.c - o_text_width().

     }

Function o_text_mirror_world_old()
==================================

 -- Function: o_text_mirror_world_old w_current world_centerx world_centery object

     <<o_text_basic.c : o_text_mirror_world_old()>>=
     void
     o_text_mirror_world_old(TOPLEVEL *w_current, int world_centerx,
                             int world_centery, OBJECT *object)
     {
       int newx=0, newy=0;
       int origx, origy;
       int x, y;
       char *output_string = NULL;
       char *name = NULL;
       char *value = NULL;
       int sign=1;
       int height_mod=0;

       origx = object->text->x;
       origy = object->text->y;

       /* translate to origin */
       x = origx + (-world_centerx);
       y = origy + (-world_centery);

       if (o_attrib_get_name_value(object->text->string, &name, &value)) {
         switch(object->show_name_value) {
           case(SHOW_NAME_VALUE):
             output_string = g_strdup(object->text->string);
             break;

           case(SHOW_NAME):
             if (name[0] != '\0') {
               output_string = g_strdup(name);
             } else {
               /* you probably can remove this now... */
               /* since improper attributes will never get here */
               fprintf(stderr,
                       "Got an improper attribute: %s\n",
                       object->text->string);
               output_string = g_strdup("invalid");
             }
             break;

           case(SHOW_VALUE):
             if (value[0] != '\0') {
               output_string = g_strdup(value);
             } else {
               /* you probably can remove this now... */
               /* since improper attributes will never get here */
               fprintf(stderr,
                       "Got an improper attribute: %s\n",
                       object->text->string);
               output_string = g_strdup("invalid");
             }
             break;
         }
       } else {
         output_string = g_strdup(object->text->string);
       }

       switch(object->text->alignment) {
         case(LOWER_LEFT):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = 1;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           height_mod = 0;
         }
         break;

         case(MIDDLE_LEFT):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = 1;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           height_mod = o_text_height(object->text->string,
                                      object->text->size);
         }
         break;

         case(UPPER_LEFT):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = 1;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           height_mod = 2*o_text_height(object->text->string,
                                        object->text->size);
         }
         break;

         case(LOWER_MIDDLE):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = 0.5;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           sign = 1;
           height_mod = 0;
         }
         break;

         case(MIDDLE_MIDDLE):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = 0.5;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           sign = 1;
           height_mod = o_text_height(object->text->string,
                                      object->text->size);
         }
         break;

         case(UPPER_MIDDLE):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = 0.5;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           sign = 1;
           height_mod = 2*o_text_height(object->text->string,
                                        object->text->size);
         }
         break;

         case(LOWER_RIGHT):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = -1;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           sign = 1;
           height_mod = 0;
         }
         break;

         case(MIDDLE_RIGHT):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = -1;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           sign = 1;
           height_mod = o_text_height(object->text->string,
                                      object->text->size);
         }
         break;

         case(UPPER_RIGHT):
         if (object->text->angle == 0 || object->text->angle == 180) {
           sign = -1;
           height_mod = 0;
         } else if (object->text->angle == 90 || object->text->angle == 270) {
           sign = 1;
           height_mod = 2*o_text_height(object->text->string,
                                        object->text->size);
         }
         break;
       }

       switch (object->text->angle) {

         case(0):
         newx = -(x + sign*o_text_width(w_current,
                                        output_string,
                                        object->text->size/2));

         break;

         case(90):
         newx = -(x - sign*o_text_height(object->text->string,
                                         object->text->size)+height_mod);
         break;

         case(180):
         newx = -(x - sign*o_text_width(w_current,
                                        output_string,
                                        object->text->size/2));
         break;

         case(270):
         newx = -(x + sign*o_text_height(object->text->string,
                                         object->text->size)+height_mod);
         break;

         default:
         fprintf(stderr, "Invalid angle specified!\n");
         return;
         break;

       }

       newy = y;

       x = newx + (world_centerx);
       y = newy + (world_centery);

       object->text->x = x;
       object->text->y = y;

       /* don't know if this is needed ?*/
       /* o_text_translate_world(w_current, x-object->text->x, y-object->text->y, object);*/
       o_text_recreate(w_current, object);
       if (output_string) free(output_string);
       if (name) free(name);
       if (value) free(value);
     }

          Defines :
          .

          Uses :
          o_text_height,  -- o_text_basic.c - o_text_height().
          o_text_recreate,  -- o_text_basic.c - o_text_recreate().
          o_text_translate_world,  -- o_text_basic.c - o_text_translate_world().
          o_text_width,  -- o_text_basic.c - o_text_width().

     }

Function o_text_return_center()
===============================

 -- Function: o_text_return_center w_current o_current centerx centery

     <<o_text_basic.c : o_text_return_center()>>=
     void
     o_text_return_center(TOPLEVEL *w_current, OBJECT *o_current,
                          int *centerx, int *centery)
     {
       int text_height;
       int text_width;

       text_height = o_text_height(o_current->text->string,
                                   o_current->text->size);

       /* this will NOT NOT NOT work with attributes */
       text_width = o_text_width(w_current, o_current->text->string,
                                 o_current->text->size/2);

       switch(o_current->text->angle) {
         case(0):
           *centerx = o_current->text->x + text_width/2;
           *centery = o_current->text->y + text_height/2;
           break;

         case(90):
           *centerx = o_current->text->x - text_height/2;
           *centery = o_current->text->y + text_width/2;
           break;

         case(180):
           *centerx = o_current->text->x - text_width/2;
           *centery = o_current->text->y - text_height/2;
           break;

         case(270):
           *centerx = o_current->text->x + text_height/2;
           *centery = o_current->text->y - text_width/2;
           break;
       }
     }

          Defines :
          o_text_return_center,
           -- o_text_basic.c - o_text_change_angle().

          Uses :
          o_text_height,  -- o_text_basic.c - o_text_height().
          o_text_width,  -- o_text_basic.c - o_text_width().

     }

Function o_text_change_angle()
==============================

 -- Function: o_text_change_angle w_current prim_objs new_angle

     <<o_text_basic.c : o_text_change_angle()>>=
     /* the complex here is the complex of a complex object */
     o_text_change_angle(TOPLEVEL *w_current, OBJECT *prim_objs, int new_angle)
     {
       OBJECT *o_current;
       int centerx, centery;

       o_current = prim_objs;

       while (o_current != NULL) {
         if (o_current->type == OBJ_TEXT) {
           o_current->text->angle = new_angle;

           /* change world to non */
           o_text_return_center(w_current, o_current, &centerx, &centery);

           o_text_translate_world(w_current,
                                  -centerx, -centery, o_current);

           o_text_mirror_world(w_current, 0, 0,
                               o_current);
           /*
              o_text_rotate_world(w_current, 0, 0,
              new_angle, 180, o_current);
           */

           /*                        o_text_rotate_world(w_current, 0, 0, new_angle,
                                     180, o_current)*/

           o_text_translate_world(w_current,
                                  centerx, centery, o_current);

           /*                        o_text_rotate_lowlevel(w_current,
                                     0, 0, new_angle, 180, o_current->text->prim_objs);*/

     #if 0
           w_current->override_color =
             w_current->background_color;
           o_text_draw(w_current, o_current);
           w_current->override_color = -1;
     #endif

           o_text_recreate(w_current, o_current);
         }
         o_current = o_current->next;
       }
     }

          Defines :
          .

          Uses :
          o_text_mirror_world,  -- o_text_basic.c - o_text_mirror_world().
          o_text_recreate,  -- o_text_basic.c - o_text_recreate().
          o_text_return_center,  -- o_text_basic.c - o_text_return_center().
          o_text_rotate_lowlevel,  -- o_text_basic.c - o_text_rotate_lowlevel().
          o_text_rotate_world,  -- o_text_basic.c - o_text_rotate_world().
          o_text_translate_world,  -- o_text_basic.c - o_text_translate_world().

     }

Function o_text_mirror_world()
==============================

 -- Function: o_text_mirror_world w_current world_centerx world_centery object

     <<o_text_basic.c : o_text_mirror_world()>>=
     void
     o_text_mirror_world(TOPLEVEL *w_current, int world_centerx, int world_centery,
                         OBJECT *object)
     {
       int origx, origy;
       int x, y;

       origx = object->text->x;
       origy = object->text->y;

       x = origx + (-world_centerx);
       y = origy + (-world_centery);

       if ((object->text->angle%180)==0) {
         switch(object->text->alignment) {
           case(LOWER_LEFT):
             object->text->alignment=LOWER_RIGHT;
             break;

           case(MIDDLE_LEFT):
             object->text->alignment=MIDDLE_RIGHT;
             break;

           case(UPPER_LEFT):
             object->text->alignment=UPPER_RIGHT;
             break;

           case(LOWER_RIGHT):
             object->text->alignment=LOWER_LEFT;
             break;

           case(MIDDLE_RIGHT):
             object->text->alignment=MIDDLE_LEFT;
             break;

           case(UPPER_RIGHT):
             object->text->alignment=UPPER_LEFT;
             break;

           default:
             break;
         }
       } else {
         switch(object->text->alignment) {
           case(LOWER_LEFT):
           object->text->alignment=UPPER_LEFT;
           break;

           case(UPPER_LEFT):
           object->text->alignment=LOWER_LEFT;
           break;

           case(LOWER_RIGHT):
           object->text->alignment=UPPER_RIGHT;
           break;

           case(UPPER_RIGHT):
           object->text->alignment=LOWER_RIGHT;
           break;

           case(LOWER_MIDDLE):
           object->text->alignment=UPPER_MIDDLE;
           break;

           case(UPPER_MIDDLE):
           object->text->alignment=LOWER_MIDDLE;
           break;

           default:
           break;
         }
       }

       object->text->x = -x + (world_centerx);
       object->text->y =  y + (world_centery);

       o_text_recreate(w_current, object);
     }

          Defines :
          o_text_mirror_world,
           -- o_text_basic.c - o_text_change_angle(),
           -- o_text_basic.c - o_text_mirror().

          Uses :
          o_text_recreate,  -- o_text_basic.c - o_text_recreate().

     }

Function o_text_mirror()
========================

 -- Function: o_text_mirror w_current centerx centery object

     <<o_text_basic.c : o_text_mirror()>>=
     void
     o_text_mirror(TOPLEVEL *w_current, int centerx, int centery, OBJECT *object)
     {
       int world_centerx, world_centery;

       SCREENtoWORLD(w_current, centerx, centery,
                     &world_centerx,
                     &world_centery);

       o_text_mirror_world(w_current, world_centerx, world_centery, object);
     }

          Defines :
          .

          Uses :
          o_text_mirror_world,  -- o_text_basic.c - o_text_mirror_world().

     }