Expose permanent debugging code to the C compiler.
[geda-gaf/berndj.git] / libgeda / src / o_text_basic.c
blobe6d0e14b4b03b824a324d85cdd7ac2b307da80f1
1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2007 Ales Hvezda
4 * Copyright (C) 1998-2007 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
21 /*! \file o_text_basic.c
22 * \brief functions for the text and fonts
24 * \par The font definitions
26 * Each letter of the font is defined in a single font symbol file. In
27 * the font symbol file, the character width is defined in the second
28 * line. The first line contains the file format version.
30 * All remaining lines are basic graphical lines. They build the
31 * appearance of the character.
33 * \image html o_text_font_overview.png
34 * \image latex o_text_font_overview.pdf "font overview" width=14cm
36 * The height of capital characters in the font files is 26. The size
37 * of small letters is 16. The space below the zero line is used by
38 * characters like <b>g</b>, <b>p</b> or <b>q</b>. The space above 26
39 * is used by diacritic marks like accents, breve, circumflex mostly in
40 * european characters.
42 * When loading a font definition the basic line objects are stored in
43 * <b>OBJECT->font_prim_objs</b> as a list of OBJECTs.
45 * All font objects are stored in the hash table #font_loaded when they
46 * are loaded.
48 * \par The text definitions
50 * The text is stored and printed in several different representations.
52 * In the gEDA files the text is just a string. It is stored unmodified
53 * in <b>OBJECT->text->string</b>.
55 * If the string is an attribute with an equal sign as delimiter between
56 * an attribute name and an attribute value, then it is possible to
57 * hide some parts of the text. The still visible part of an attribute
58 * is stored in <b>OBJECT->text->disp_string</b>.
60 * \image html o_text_text_overview.png
61 * \image latex o_text_text_overview.pdf "text overview" width=14cm
63 * To draw the text in gschem, the string is interpreted and converted
64 * to a list of basic graphical objects. The basic line objects are
65 * collected from the font character objects.
66 * All basic graphical objects are stored in
67 * <b>OBJECT->text->prim_objs</b>.
70 #include <config.h>
72 #include <stdio.h>
73 #include <math.h>
74 #include <sys/stat.h>
75 #ifdef HAVE_STRING_H
76 #include <string.h>
77 #endif
78 #ifdef HAVE_UNISTD_H
79 #include <unistd.h>
80 #endif
82 #include "factory.h"
83 #include "libgeda_priv.h"
85 #ifdef HAVE_LIBDMALLOC
86 #include <dmalloc.h>
87 #endif
89 static void o_text_recalc(OBJECT *o_current);
90 static void o_text_visit(OBJECT *o_current,
91 void (*fn)(OBJECT *, void *),
92 void *context);
93 static void o_text_destroy(OBJECT *o_current);
94 static OBJECT *o_text_copy(TOPLEVEL *toplevel, OBJECT *o_current);
96 /*! Default setting for text draw function. */
97 void (*text_draw_func)() = NULL;
99 /*! Hashtable storing font_character (string) as a key, and pointer to data
100 * \note
101 * This table stays global, thus all functions can access it.
103 GHashTable *font_loaded = NULL;
105 /*! Hashtable storing mapping between character and font definition file
106 * \note
107 * This table stays global, thus all functions can access it.
109 GHashTable *font_char_to_file = NULL;
111 /*! Size of a tab in characters */
112 int tab_in_chars = 8;
115 /*! \brief update the visible part of a string
116 * \par Function Description
117 * If a string is an attribute, then it is possible to hide
118 * the name or the value part of the attribute string.
119 * This functions updates the text->disp_string according
120 * to the object->show_name_value settings
122 * \param [in] o The OBJECT to update
124 static void update_disp_string(OBJECT *o)
126 char *name = NULL;
127 char *value = NULL;
128 TEXT *text = o->text;
130 g_free (text->disp_string);
132 if (o_attrib_get_name_value (text->string, &name, &value)) {
133 switch (o->show_name_value) {
134 case (SHOW_NAME_VALUE):
135 text->disp_string = g_strdup (text->string);
136 break;
138 case (SHOW_NAME):
139 text->disp_string = g_strdup (name);
140 break;
142 case (SHOW_VALUE):
143 text->disp_string = g_strdup(value);
144 break;
146 /* free the strings allocated by o_attrib_get_name_value */
147 g_free(name);
148 g_free(value);
149 } else {
150 text->disp_string = g_strdup (text->string);
154 /*! \brief calculate and return the boundaries of a text object
155 * \par Function Description
156 * This function calculates the object boudaries of a text \a object.
158 * \param [in] toplevel The TOPLEVEL object.
159 * \param [in] o_current a text object
160 * \param [out] left the left world coord
161 * \param [out] top the top world coord
162 * \param [out] right the right world coord
163 * \param [out] bottom the bottom world coord
165 int world_get_text_bounds(OBJECT *o_current, int *left,
166 int *top, int *right, int *bottom)
168 return world_get_object_list_bounds(o_current->text->prim_objs,
169 left, top, right, bottom);
172 /*! \brief create a new text head object
173 * \par Function Description
174 * This function creates a <b>text_head</b> OBJECT. This OBJECT
175 * is just a special empty object. This object is never modified.
177 * \return new text head OBJECT
179 OBJECT *o_text_new_head(TOPLEVEL *toplevel)
181 OBJECT *new_node=NULL;
183 new_node = s_toplevel_new_object(toplevel, OBJ_HEAD, "text_head");
185 /* don't need to do this for head nodes */
186 /* ret = s_basic_link_object(new_node, NULL);*/
187 return new_node;
190 /*! \brief initialize the hash tables for the fonts
191 * \par Function Description
192 * This function initializes the two global hash tables <b>font_loaded</b>
193 * and <b>font_char_to_file</b> that are used to store the fonts characters.
195 void o_text_init(void)
197 if (font_loaded == NULL) {
198 font_loaded = g_hash_table_new_full (g_direct_hash,
199 g_direct_equal,
200 NULL,
201 g_free);
202 } else {
203 fprintf (stderr, "o_text_init: Tried to initialize an already initialized font_loaded hash table!!\n");
205 if (font_char_to_file == NULL) {
206 font_char_to_file = g_hash_table_new_full (g_direct_hash,
207 g_direct_equal,
208 NULL,
209 g_free);
211 else {
212 fprintf (stderr, "o_text_init: Tried to initialize an already initialized font_char_to_file hash table!!\n");
215 return;
219 /*! \brief print informations about some characters
220 * \note
221 * This is a debugging function. Do not use it in regular code.
223 void o_text_print_set(void)
225 OBJECT *o_current, *o_font_set;
226 char i;
228 for (i = 'A' ; i < 'Z'+1; i++) {
229 o_font_set = g_hash_table_lookup (font_loaded,
230 GUINT_TO_POINTER ((gunichar)i));
231 if (o_font_set != NULL) {
232 printf("%c: LOADED\n", i);
233 for (o_current=return_tail(o_font_set->font_prim_objs); o_current;
234 o_current=o_current->prev)
236 printf(" %s\n", o_current->name);
238 } else {
239 printf("%c: unloaded\n", i);
244 /*! \brief load a font character into an object
245 * \par Function Description
246 * This function loads a character form a font symbol file.
248 * \param [in] toplevel The TOPLEVEL object
249 * \param [in] needed_char unicode character to load
250 * return a character OBJECT
252 OBJECT *o_text_load_font(TOPLEVEL *toplevel, gunichar needed_char)
254 gchar *temp_string = NULL;
255 OBJECT *o_font_set;
256 int not_found = FALSE;
257 gchar *aux_str2;
258 GError *err = NULL;
260 /* retrieve the name of the file where the char is defined */
261 aux_str2 = g_hash_table_lookup (font_char_to_file,
262 GUINT_TO_POINTER (needed_char));
263 if (aux_str2 == NULL) {
264 /* this is needed since WinNT file systems are
265 * case insensitive, and cannot tell the difference
266 * between A.sym and a.sym. So we create a_.sym -
267 * z_.sym, this loads up the chars
269 if (needed_char >= 'a' && needed_char <= 'z') {
270 temp_string = g_strdup_printf("%s%c%c_.sym",
271 toplevel->font_directory, G_DIR_SEPARATOR,
272 needed_char);
273 } else {
274 temp_string = g_strdup_printf("%s%c%c.sym",
275 toplevel->font_directory, G_DIR_SEPARATOR,
276 needed_char);
278 } else {
279 temp_string = g_strdup_printf("%s", aux_str2);
281 aux_str2 = NULL;
283 if ( access(temp_string, R_OK) != 0 ) {
284 gchar outbuf[7];
285 gint l;
287 /* convert needed_char to a utf-8 string */
288 l = g_unichar_to_utf8 (needed_char, outbuf);
289 outbuf[l] = '\0';
290 s_log_message(_("Could not find character '%s' definition.\n"), outbuf);
292 g_free (temp_string);
293 temp_string = g_build_filename (toplevel->font_directory, "quest.sym", NULL);
294 if ( access(temp_string, R_OK) != 0 ) {
295 fprintf(stderr, _("Could not load question font char -- check font-directory keyword\n"));
296 exit(-1);
298 not_found = TRUE;
301 /* Make new object for the font set list */
302 o_font_set = (OBJECT*)g_new (OBJECT, 1);
304 o_font_set->font_prim_objs = NULL;
305 o_font_set->font_text_size = 100;
307 o_font_set->name = g_strdup_printf ("%c", needed_char);
308 o_font_set->font_prim_objs = o_text_new_head(toplevel);
310 /* Add it to the list and hash table. Some functions will need it */
311 g_hash_table_insert (font_loaded,
312 GUINT_TO_POINTER (needed_char), o_font_set);
314 if (not_found == TRUE) {
315 /* set the font text size (width) to the question mark's size */
316 /* yes, the question mark character was loaded instead of the real char */
317 /* 63 == question mark character */
319 OBJECT *aux_obj;
321 aux_obj = g_hash_table_lookup (font_loaded,
322 GUINT_TO_POINTER ((gunichar)'?'));
323 if (aux_obj == NULL) {
324 o_text_load_font(toplevel, (gunichar) '?');
325 aux_obj = g_hash_table_lookup (font_loaded,
326 GUINT_TO_POINTER ((gunichar)'?'));
329 o_font_set->font_text_size = aux_obj->font_text_size;
332 o_font_set->font_prim_objs = o_read(toplevel, o_font_set->font_prim_objs,
333 temp_string, &err);
334 if (o_font_set->font_prim_objs == NULL) {
335 g_assert (err != NULL);
336 g_warning ("o_text_basic.c: Failed to read font file: %s\n",
337 err->message);
338 g_error_free (err);
341 o_font_set->font_prim_objs = return_head(o_font_set->font_prim_objs);
343 g_free(temp_string);
345 return(o_font_set->font_prim_objs);
348 /*! \brief count the lines of a text string
349 * \par Function Description
350 * This function just counts the number of lines that are
351 * in the \a string.
353 * \param [in] string text string to count the lines
354 * \return the number of lines
356 int o_text_num_lines(const char *string)
358 int line_count = 0;
359 const gchar *aux;
360 gunichar current_char;
362 if (string == NULL) {
363 return 0;
366 /* if it's not null, then we have at least one line */
367 line_count++;
368 /* Count how many \n are in the string */
369 aux = string;
370 while (aux && ((gunichar) (*aux) != 0) ) {
371 current_char = g_utf8_get_char_validated(aux, -1);
372 if (current_char == '\n')
373 line_count++;
374 aux = g_utf8_find_next_char(aux, NULL);
377 return (line_count);
380 /*! \brief calculates the height of a text string
381 * \par Function Description
382 * This function calculates the height of a \a string depending
383 * on it's text \a size. The number of lines and the spacing
384 * between the lines are taken into account.
386 * \param [in] string the text string
387 * \param [in] size the text size of the character
388 * \return the total height of the text string
390 int o_text_height(const char *string, int size)
392 int line_count = 0;
394 if (string == NULL || font_loaded == NULL) {
395 return 0;
398 /* Get the number of lines in the string */
399 line_count = o_text_num_lines(string);
401 /* 26 is the height of a single char (in mils) */
402 /* which represents a character which is 2 pts high */
403 /* So size has to be divided in half */
404 /* and it's added the LINE_SPACING*character_height of each line */
405 return(26*size/2*(1+LINE_SPACING*(line_count-1)));
408 /*! \brief calculate the width of a text
409 * \par Function Description
410 * This function calculates the width of a text \a string
411 * depending on the text \a size and the width of the individual
412 * characters that are in the text string.
414 * \param [in] toplevel The TOPLEVEL object
415 * \param [in] string The text string
416 * \param [in] size The text size
417 * \return the total width of the text.
419 int o_text_width(TOPLEVEL *toplevel, char const *string, int size)
421 int width=0, max_width=0;
422 int size_of_tab_in_coord;
423 OBJECT *o_font_set;
424 gchar const *ptr;
425 gunichar previous_char;
426 gunichar c = 0;
427 if (string == NULL || font_loaded == NULL) {
428 return 0;
431 /* Make sure TAB_CHAR_MODEL is loaded before trying to use its text */
432 /* size */
433 o_font_set = g_hash_table_lookup (
434 font_loaded, GUINT_TO_POINTER ((gunichar)TAB_CHAR_MODEL[0]));
435 if (o_font_set == NULL) {
436 o_text_load_font(toplevel, (gunichar) TAB_CHAR_MODEL[0]);
437 o_font_set = g_hash_table_lookup (
438 font_loaded, GUINT_TO_POINTER ((gunichar)TAB_CHAR_MODEL[0]));
441 /* Get the maximum tab width's in coordinates */
442 size_of_tab_in_coord = tab_in_chars * size * o_font_set->font_text_size;
444 for (ptr = string;
445 ptr != NULL && *ptr != 0;
446 ptr = g_utf8_find_next_char (ptr, NULL))
448 previous_char = c;
449 c = g_utf8_get_char_validated (ptr, -1);
451 if ( (c == (gunichar) '\\') &&
452 (previous_char != (gunichar) '\\') ) {
453 continue;
455 if ( (c == (gunichar) '_') &&
456 (previous_char == (gunichar) '\\') ) {
457 continue;
459 switch (c) {
460 case ((gunichar)'\n'):
461 width = 0;
462 break;
463 case ((gunichar)'\t'):
464 width += (size_of_tab_in_coord - (width % size_of_tab_in_coord));
465 break;
466 default:
467 /* find current_char */
468 o_font_set = g_hash_table_lookup (font_loaded,
469 GUINT_TO_POINTER (c));
470 if (o_font_set == NULL) {
471 o_text_load_font (toplevel, (gunichar)c);
472 /* let do a new search for character c */
473 o_font_set = g_hash_table_lookup (font_loaded,
474 GUINT_TO_POINTER (c));
477 if (o_font_set != NULL) {
478 width = width + size*o_font_set->font_text_size;
481 if (width > max_width) {
482 max_width = width;
487 /* the size is a fudge factor */
488 /* Changed the return value according to a suggestion of Ales. */
489 /* Yes, the -size*10 fudge factor should be removed. */
490 /* return(max_width - size*10); */
491 return max_width;
494 /*! \brief create a complex text object from a string
495 * \par Function Description
496 * This function converts the \a string into a list of basic objects.
497 * All basic objects are appendend to the \a object_list.
498 * The basic objects are collected from the basic font definition
499 * of each character of they are created as lines for the overbar feature.
501 * \param [in] toplevel The TOPLEVEL object
502 * \param [in] object_list The list to append the basic objects
503 * \param [in] string The string to create the object list from
504 * \param [in] size The size of the text object
505 * \param [in] color The color of the text object
506 * \param [in] x The x coord of the text object
507 * \param [in] y The y coord of the text object
508 * \param [in] alignment The alignment of the text object
509 * \param [in] angle The angle of the text object (in 90 degree steps)
511 * \return the object list of the primary text objects
513 OBJECT *o_text_create_string(TOPLEVEL *toplevel, OBJECT *object_list,
514 char *string, int size, int color, int x, int y,
515 int alignment, int angle)
517 OBJECT *temp_tail=NULL;
518 OBJECT *temp_list;
519 OBJECT *temp_obj;
520 OBJECT *start_of_char;
521 int x_offset;
522 int y_offset;
523 int text_width;
524 int text_height;
525 int char_height;
526 int line_start_x, line_start_y;
527 int sign=1;
528 int overbar_startx=0, overbar_starty=0;
529 int overbar_endx=0, overbar_endy=0;
530 int overbar_height_offset = 0;
531 gchar *ptr;
532 OBJECT *o_font_set;
533 gunichar current_char;
534 gboolean escape = FALSE, overbar_started = FALSE;
535 gboolean finish_overbar, start_overbar, leave_parser = FALSE;
536 gboolean draw_character, draw_tabulator, draw_newline;
538 temp_list = object_list;
540 /* error condition hack */
541 if (string == NULL || font_loaded == NULL) {
542 return(NULL);
545 /* now read in the chars */
546 temp_tail = toplevel->page_current->object_tail;
548 text_height = o_text_height(string, size);
549 char_height = o_text_height("a", size);
550 text_width = o_text_width(toplevel, string, size/2);
552 switch(angle) {
553 case(0):
554 sign = -1;
555 break;
556 case(90):
557 sign = 1;
558 break;
559 case(180):
560 sign = 1;
561 break;
562 case(270):
563 sign = -1;
564 break;
567 if (angle == 0 || angle == 180) {
568 y = y - o_text_height("a", size) + text_height;
570 switch(alignment) {
572 case(LOWER_LEFT):
573 x_offset = x;
574 y_offset = y;
575 break;
577 case(MIDDLE_LEFT):
578 x_offset = x;
579 y_offset = y + sign*0.5*text_height;
580 break;
582 case(UPPER_LEFT):
583 x_offset = x;
584 y_offset = y + sign*text_height;
585 break;
587 case(LOWER_MIDDLE):
588 x_offset = x + sign*0.5*text_width;
589 y_offset = y;
590 break;
592 case(MIDDLE_MIDDLE):
593 x_offset = x + sign*0.5*text_width;
594 y_offset = y + sign*0.5*text_height;
595 break;
597 case(UPPER_MIDDLE):
598 x_offset = x + sign*0.5*text_width;
599 y_offset = y + sign*text_height;
601 break;
603 case(LOWER_RIGHT):
604 x_offset = x + sign*text_width;
605 y_offset = y;
606 break;
608 case(MIDDLE_RIGHT):
609 x_offset = x + sign*text_width;
610 y_offset = y + sign*0.5*text_height;
611 break;
613 case(UPPER_RIGHT):
614 x_offset = x + sign*text_width;
615 y_offset = y + sign*text_height;
616 break;
618 default:
619 fprintf(stderr, "Got an invalid text alignment [%d]\n",
620 alignment);
621 fprintf(stderr, "Defaulting to Lower Left");
622 alignment = LOWER_LEFT;
623 x_offset = x;
624 y_offset = y;
625 break;
627 } else { /* angle is 90 or 270 */
628 x = x + sign*(o_text_height("a", size) - text_height);
630 switch(alignment) {
632 case(LOWER_LEFT):
633 x_offset = x;
634 y_offset = y;
635 break;
637 case(MIDDLE_LEFT):
638 x_offset = x + sign*0.5*text_height;
639 y_offset = y;
640 break;
642 case(UPPER_LEFT):
643 x_offset = x + sign*text_height;
644 y_offset = y;
645 break;
647 case(LOWER_MIDDLE):
648 x_offset = x;
649 y_offset = y - sign*0.5*text_width;
650 break;
652 case(MIDDLE_MIDDLE):
653 x_offset = x + sign*0.5*text_height;
654 y_offset = y - sign*0.5*text_width;
655 break;
657 case(UPPER_MIDDLE):
658 x_offset = x + sign*text_height;
659 y_offset = y - sign*0.5*text_width;
661 break;
663 case(LOWER_RIGHT):
664 x_offset = x;
665 y_offset = y - sign*text_width;
666 break;
668 case(MIDDLE_RIGHT):
669 x_offset = x + sign*0.5*text_height;
670 y_offset = y - sign*text_width;
671 break;
673 case(UPPER_RIGHT):
674 x_offset = x + sign*text_height;
675 y_offset = y - sign*text_width;
676 break;
678 default:
679 fprintf(stderr, "Got an invalid text alignment [%d]\n",
680 alignment);
681 fprintf(stderr, "Defaulting to Lower Left");
682 alignment = LOWER_LEFT;
683 x_offset = x;
684 y_offset = y;
685 break;
690 switch(angle) {
691 case(180):
692 x_offset = x_offset - text_width;
693 y_offset = y_offset - text_height;
694 angle = 0;
695 break;
698 if (GEDA_DEBUG) {
699 printf("width: %d\n", o_text_width(toplevel, string, size/2));
700 printf("1 %d %d\n", x_offset, y_offset);
703 line_start_x = x_offset;
704 line_start_y = y_offset;
706 /* the overbar is 1/4 above the char height. */
707 overbar_height_offset = char_height + char_height/4;
709 for (ptr = string;
710 ptr != NULL && !leave_parser;
711 ptr = g_utf8_find_next_char (ptr, NULL)) {
712 current_char = g_utf8_get_char_validated (ptr, -1);
714 /* reset all actions */
715 finish_overbar = FALSE;
716 start_overbar = FALSE;
717 leave_parser = FALSE;
718 draw_character = FALSE;
719 draw_tabulator = FALSE;
720 draw_newline = FALSE;
722 /* state machine to interpret the string:
723 * there are two independent state variables overbar_started and escape.
724 * The actions are set according to the current character and
725 * the two state variables.
727 switch (current_char) {
728 case '\0':
729 /* end of the string */
730 if (overbar_started)
731 finish_overbar = TRUE;
732 leave_parser = TRUE;
733 break;
734 case '\\':
735 if (escape == TRUE) {
736 draw_character = TRUE;
737 escape = FALSE;
738 } else {
739 escape = TRUE;
741 break;
742 case '_':
743 if (escape == TRUE) {
744 escape = FALSE;
745 if (overbar_started == TRUE) {
746 finish_overbar = TRUE;
747 overbar_started = FALSE;
748 } else {
749 start_overbar = TRUE;
750 overbar_started = TRUE;
752 } else {
753 draw_character = TRUE;
755 break;
756 case '\n':
757 draw_newline = TRUE;
758 if (overbar_started == TRUE) {
759 finish_overbar = TRUE;
760 start_overbar = TRUE;
762 escape = FALSE;
763 break;
764 case '\t':
765 draw_tabulator = TRUE;
766 escape = FALSE;
767 break;
768 default:
769 draw_character = TRUE;
770 escape = FALSE;
773 /* execute all actions set by the state machine
774 * Note: It's important that the three actions
775 * finish_overbar, draw_newline, start_overbar are executed
776 * in exacly that order. It's required to continue overbars
777 * over newlines.
779 if (draw_character) {
780 /* get the character from the hash table */
781 o_font_set = g_hash_table_lookup (font_loaded,
782 GUINT_TO_POINTER (current_char));
783 if (o_font_set == NULL) {
784 o_text_load_font(toplevel, (gunichar) current_char);
785 o_font_set = g_hash_table_lookup (font_loaded,
786 GUINT_TO_POINTER (current_char));
789 /* Only add the character if there are primary object.
790 e.g. the space character doesn't have those */
791 if (o_font_set->font_prim_objs->next != NULL) {
792 start_of_char = temp_list;
793 temp_list = o_list_copy_all(toplevel,
794 o_font_set->font_prim_objs->next,
795 temp_list, NORMAL_FLAG);
797 if (start_of_char != NULL)
798 start_of_char = start_of_char->next;
800 o_complex_set_color(start_of_char, color);
801 o_scale(start_of_char, size/2, size/2);
803 /* Rotate and translate the character to its world position */
804 o_list_rotate_world(0, 0, angle, start_of_char);
805 o_list_translate_world(x_offset, y_offset, start_of_char);
808 /* Calculate the position of the next character */
809 switch(angle) {
810 case(0):
811 x_offset = (x_offset) + size/2*o_font_set->font_text_size;
812 break;
813 case(90):
814 y_offset = (y_offset) + size/2*o_font_set->font_text_size;
815 break;
816 case(180):
817 x_offset = (x_offset) - size/2*o_font_set->font_text_size;
818 break;
819 case(270):
820 y_offset = (y_offset) - size/2*o_font_set->font_text_size;
821 break;
825 if (draw_tabulator) {
826 gint size_of_tab_in_coord;
827 gint rel_char_coord;
828 /* Get the maximum tab width's in coordinates */
829 size_of_tab_in_coord = (tab_in_chars *
830 o_text_width(toplevel, TAB_CHAR_MODEL, size/2));
832 switch (angle) {
833 case 0:
834 case 180:
835 rel_char_coord = x_offset - line_start_x;
836 x_offset += (size_of_tab_in_coord -
837 (rel_char_coord % size_of_tab_in_coord));
838 break;
839 case 90:
840 rel_char_coord = y_offset - line_start_y;
841 y_offset += (size_of_tab_in_coord -
842 (rel_char_coord % size_of_tab_in_coord));
843 break;
844 case 270:
845 rel_char_coord = line_start_y - y_offset;
846 y_offset -= (size_of_tab_in_coord -
847 (rel_char_coord % size_of_tab_in_coord));
848 break;
849 default:
850 fprintf(stderr, "o_text_create_string: Angle not supported\n");
851 break;
855 if (finish_overbar) {
856 switch (angle) {
857 case 0:
858 overbar_endx = x_offset;
859 overbar_endy = y_offset + overbar_height_offset;
860 break;
861 case 90:
862 overbar_endx = x_offset - overbar_height_offset;
863 overbar_endy = y_offset;
864 break;
865 case 180:
866 overbar_endx = x_offset;
867 overbar_endy = y_offset - overbar_height_offset;
868 break;
869 case 270:
870 overbar_endx = x_offset + overbar_height_offset;
871 overbar_endy = y_offset;
872 break;
873 default:
874 fprintf(stderr, "o_text_create_string: Angle not supported\n");
875 break;
877 /* Now add the overbar (if it is not a zero length overbar) */
878 if ((overbar_startx != overbar_endx)
879 || (overbar_starty != overbar_endy)) {
880 temp_obj = o_line_new(toplevel, OBJ_LINE, color,
881 overbar_startx, overbar_starty,
882 overbar_endx, overbar_endy);
883 temp_list = s_basic_link_object(temp_obj, temp_list);
887 if (draw_newline) {
888 switch (angle) {
889 case 0:
890 x_offset = line_start_x;
891 y_offset = line_start_y - char_height * LINE_SPACING;
892 break;
893 case 90:
894 x_offset = line_start_x + char_height * LINE_SPACING;
895 y_offset = line_start_y;
896 break;
897 case 180:
898 x_offset = line_start_x;
899 y_offset = line_start_y + char_height * LINE_SPACING;
900 break;
901 case 270:
902 x_offset = line_start_x - char_height * LINE_SPACING;
903 y_offset = line_start_y;
904 break;
905 default:
906 fprintf(stderr, "o_text_create_string: Angle not supported\n");
907 break;
909 line_start_x = x_offset;
910 line_start_y = y_offset;
913 if (start_overbar) {
914 switch (angle) {
915 case 0:
916 overbar_startx = x_offset;
917 overbar_starty = y_offset + overbar_height_offset;
918 break;
919 case 90:
920 overbar_startx = x_offset - overbar_height_offset;
921 overbar_starty = y_offset;
922 break;
923 case 180:
924 overbar_startx = x_offset;
925 overbar_starty = y_offset - overbar_height_offset;
926 break;
927 case 270:
928 overbar_startx = x_offset + overbar_height_offset;
929 overbar_starty = y_offset;
930 break;
931 default:
932 fprintf(stderr, "o_text_create_string: Angle not supported\n");
933 break;
938 toplevel->page_current->object_tail = temp_tail;
940 return object_list;
943 static OBJECT *o_text_new_at_xy(TOPLEVEL *toplevel,
944 char type, int color, int x, int y)
946 OBJECT *new_node=NULL;
947 TEXT *text;
949 new_node = s_toplevel_new_object(toplevel, type, "text");
950 new_node->color = color;
952 text = g_malloc(sizeof (TEXT));
953 new_node->text = text;
955 text->x = x;
956 text->y = y;
957 text->toplevel = g_object_ref(toplevel);
958 text->string = NULL;
959 text->prim_objs = NULL;
961 new_node->copy_func = &o_text_copy;
962 new_node->bounds_recalc_func = o_text_recalc;
963 new_node->draw_func = text_draw_func;
964 new_node->visit_func = &o_text_visit;
965 new_node->destroy_func = &o_text_destroy;
967 return new_node;
970 /*! \brief Creates a text OBJECT and the graphical objects representing it
971 * \par Function Description
972 * Create an OBJECT of type OBJ_TEXT.
973 * Also add the OBJECTs forming the graphical representation of the visible
974 * string, to the text OBJECT's prim_objs list.
976 * \param [in] toplevel The TOPLEVEL object.
977 * \param [in] type OBJ_TEXT (TODO: why bother)
978 * \param [in] color The color of the text.
979 * \param [in] x World x coord of text.
980 * \param [in] y World y coord of text.
981 * \param [in] alignment How text bounding box aligns on (x, y).
982 * \param [in] angle Angle at which text will appear.
983 * \param [in] string The text.
984 * \param [in] size Text size.
985 * \param [in] visibility VISIBLE or INVISIBLE.
986 * \param [in] show_name_value SHOW_NAME_VALUE or friends.
987 * \return Pointer to text OBJECT.
989 * \note
990 * Caller is responsible for string; this function allocates its own copy.
992 OBJECT *o_text_new(TOPLEVEL *toplevel,
993 char type, int color, int x, int y, int alignment,
994 int angle, const char *string, int size,
995 int visibility, int show_name_value)
997 OBJECT *new_node=NULL;
998 OBJECT *temp_list=NULL;
999 TEXT *text;
1000 char *name = NULL;
1001 char *value = NULL;
1003 if (string == NULL) {
1004 return(NULL);
1007 new_node = o_text_new_at_xy(toplevel, type, color, x, y);
1008 text = new_node->text;
1010 text->string = g_strdup (string);
1011 text->disp_string = NULL; /* We'll fix this up later */
1012 text->length = strlen(string);
1013 text->size = size;
1014 text->alignment = alignment;
1015 text->angle = angle;
1017 new_node->visibility = visibility;
1018 new_node->show_hidden = toplevel->show_hidden_default;
1019 new_node->show_name_value = show_name_value;
1021 update_disp_string (new_node);
1023 /* now start working on the complex */
1024 temp_list = o_text_new_head(toplevel);
1026 if (visibility == VISIBLE ||
1027 (visibility == INVISIBLE && toplevel->show_hidden_default)) {
1028 new_node->text->prim_objs =
1029 o_text_create_string(toplevel, temp_list,
1030 text->disp_string, size, color,
1031 x, y, alignment, angle);
1032 new_node->text->displayed_width = o_text_width(toplevel,
1033 text->disp_string, size/2);
1034 new_node->text->displayed_height = o_text_height(text->disp_string, size);
1035 } else {
1036 new_node->text->prim_objs = NULL;
1037 new_node->text->displayed_width = 0;
1038 new_node->text->displayed_height = 0;
1039 s_delete(toplevel, temp_list);
1042 /* Update bounding box */
1043 o_text_recalc(new_node);
1045 g_free(name);
1046 g_free(value);
1047 return new_node;
1050 /*! \brief update the visual boundaries of the text object
1051 * \par Function Description
1052 * This function updates the boundaries of the object \a o_current.
1054 * \param [in] toplevel The TOPLEVEL object
1055 * \param [in] o_current The OBJECT to update
1057 static void o_text_recalc(OBJECT *o_current)
1059 int left, right, top, bottom;
1061 if (o_current->visibility == INVISIBLE && !o_current->show_hidden) {
1062 return;
1065 if (o_current->text->x == -1 && o_current->text->y == -1) {
1066 /* Off-schematic text has no bounding box. */
1067 return;
1070 if (!world_get_text_bounds(o_current, &left, &top, &right, &bottom))
1071 return;
1073 o_current->w_left = left;
1074 o_current->w_top = top;
1075 o_current->w_right = right;
1076 o_current->w_bottom = bottom;
1077 o_current->w_bounds_valid = TRUE;
1080 static void o_text_visit(OBJECT *o_current,
1081 void (*fn)(OBJECT *, void *),
1082 void *context)
1084 OBJECT *prim_obj;
1086 g_return_if_fail(o_current->type == OBJ_TEXT);
1088 if (!o_current->complex->prim_objs) {
1089 /* Text is invisible. */
1090 return;
1093 for (prim_obj = o_current->text->prim_objs->next;
1094 prim_obj != NULL;
1095 prim_obj = prim_obj->next) {
1096 (*fn)(prim_obj, context);
1100 static void o_text_destroy(OBJECT *o_current)
1102 g_free(o_current->text->string);
1103 g_free(o_current->text->disp_string);
1105 if (o_current->text->prim_objs) {
1106 s_delete_list_fromstart(o_current->text->toplevel,
1107 o_current->text->prim_objs);
1110 g_object_unref(o_current->text->toplevel);
1112 g_free(o_current->text);
1115 /*! \brief read a text object from a char buffer
1116 * \par Function Description
1117 * This function reads a text object from the textbuffer \a tb and
1118 * the text starting with the line \a firstline.
1120 * \param [in] toplevel The TOPLEVEL object
1121 * \param [in] first_line the first line of the text
1122 * \param [in] tb a text buffer (usually a line of a schematic file)
1123 * \param [in] release_ver The release number gEDA
1124 * \param [in] fileformat_ver a integer value of the file format
1125 * \return The object list
1127 OBJECT *o_text_read(TOPLEVEL *toplevel,
1128 const char *first_line,
1129 TextBuffer *tb,
1130 unsigned int release_ver,
1131 unsigned int fileformat_ver)
1133 OBJECT *new_obj;
1134 char type;
1135 int x, y;
1136 int color;
1137 int size;
1138 int visibility;
1139 int show_name_value;
1140 int angle;
1141 int alignment;
1142 int num_lines = 0;
1143 int i;
1144 char* string = NULL;
1145 GString *textstr;
1147 if (fileformat_ver >= 1) {
1148 sscanf(first_line, "%c %d %d %d %d %d %d %d %d %d\n", &type, &x, &y,
1149 &color, &size,
1150 &visibility, &show_name_value,
1151 &angle, &alignment, &num_lines);
1152 } else if (release_ver < VERSION_20000220) {
1153 /* yes, above less than (not less than and equal) is correct. The format */
1154 /* change occurred in 20000220 */
1155 sscanf(first_line, "%c %d %d %d %d %d %d %d\n", &type, &x, &y,
1156 &color, &size,
1157 &visibility, &show_name_value,
1158 &angle);
1159 alignment = LOWER_LEFT; /* older versions didn't have this */
1160 num_lines = 1; /* only support a single line */
1161 } else {
1162 sscanf(first_line, "%c %d %d %d %d %d %d %d %d\n", &type, &x, &y,
1163 &color, &size,
1164 &visibility, &show_name_value,
1165 &angle, &alignment);
1166 num_lines = 1; /* only support a single line */
1169 if (size == 0) {
1170 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);
1173 switch(angle) {
1175 case(0):
1176 case(90):
1177 case(180):
1178 case(270):
1179 break;
1181 default:
1182 s_log_message(_("Found an unsupported text angle [ %c %d %d %d %d %d %d %d %d ]\n"),
1183 type, x, y, color, size, visibility, show_name_value, angle, alignment);
1184 s_log_message(_("Setting angle to 0\n"));
1185 angle=0;
1186 break;
1190 switch(alignment) {
1191 case(LOWER_LEFT):
1192 case(MIDDLE_LEFT):
1193 case(UPPER_LEFT):
1194 case(LOWER_MIDDLE):
1195 case(MIDDLE_MIDDLE):
1196 case(UPPER_MIDDLE):
1197 case(LOWER_RIGHT):
1198 case(MIDDLE_RIGHT):
1199 case(UPPER_RIGHT):
1201 break;
1203 default:
1204 s_log_message(_("Found an unsupported text alignment [ %c %d %d %d %d %d %d %d %d ]\n"),
1205 type, x, y, color, size, visibility, show_name_value, angle, alignment);
1206 s_log_message(_("Setting alignment to LOWER_LEFT\n"));
1207 alignment = LOWER_LEFT;
1208 break;
1211 if (color < 0 || color > MAX_COLORS) {
1212 s_log_message(_("Found an invalid color [ %s ]\n"), first_line);
1213 s_log_message(_("Setting color to WHITE\n"));
1214 color = WHITE;
1217 g_assert(num_lines && num_lines > 0);
1219 textstr = g_string_new ("");
1220 for (i = 0; i < num_lines; i++) {
1221 gchar *line;
1223 line = s_textbuffer_next_line (tb);
1225 if (line != NULL)
1227 textstr = g_string_append (textstr, line);
1230 /* retrieve the character string from the GString */
1231 string = g_string_free (textstr, FALSE);
1233 string = remove_last_nl(string);
1235 /* convert the character string to UTF-8 if necessary */
1236 if (!g_utf8_validate (string, -1, NULL)) {
1237 /* if it is not utf-8, it is ISO_8859-15 */
1238 gchar *tmp = g_convert (string, strlen (string),
1239 "UTF-8", "ISO_8859-15",
1240 NULL, NULL, NULL);
1241 if (tmp == NULL) {
1242 fprintf (stderr, "Failed to convert text string to UTF-8: %s.\n",
1243 string);
1244 } else {
1245 /* successfully converted string, now use tmp as string */
1246 g_free (string);
1247 string = tmp;
1251 new_obj = o_text_new(toplevel, type, color, x, y,
1252 alignment, angle, string,
1253 size, visibility, show_name_value);
1254 g_free(string);
1256 return new_obj;
1259 /*! \brief read and set infos of a font object
1260 * \par Function Description
1261 * This function reads the font definition buffer \a buf and sets
1262 * the width of a character. This function also deals with the special,
1263 * invisible character space and newline.
1265 * \param [in] buf the font definition according to the geda file format
1266 * \todo Investigate why the TAB character is not defined here.
1268 void o_text_set_info_font(char buf[])
1270 char type;
1271 int width;
1272 gunichar character=0;
1273 gchar *buf_ptr;
1274 int special=0;
1275 char *string;
1276 OBJECT *o_font_set;
1278 string = remove_nl (buf);
1280 /* parse the font info: */
1281 buf_ptr = (gchar*)string;
1282 /* - type */
1283 type = *buf_ptr++;
1284 if (type != INFO_FONT) {
1285 g_critical ("o_text_set_info_font: Bad font type '%c', expected '%c'\n",
1286 type, INFO_FONT);
1287 return;
1290 while (buf_ptr != NULL && *buf_ptr == ' ') buf_ptr++;
1291 /* - character */
1292 if (buf_ptr != NULL && *buf_ptr != '\0') {
1293 character = g_utf8_get_char_validated (buf_ptr, -1);
1294 if (character == (gunichar)-1) {
1295 s_log_message (_("Failed to validate utf-8 character in font definition: \"%s\".\n"),
1296 string);
1297 return;
1299 /* move buf_ptr just after character */
1300 buf_ptr = g_utf8_find_next_char (buf_ptr, NULL);
1302 while (buf_ptr != NULL && *buf_ptr == ' ') buf_ptr++;
1303 /* - width and special */
1304 if (buf_ptr != NULL) {
1305 sscanf (buf_ptr, "%d %d\n", &width, &special);
1308 /* deal with special characters */
1309 if (special == 1) {
1310 switch (character) {
1311 case ((gunichar)'_'):
1312 /* space */
1313 character = (gunichar)' ';
1314 break;
1315 case ((gunichar)'n'):
1316 /* newline */
1317 character = (gunichar)'\n';
1318 break;
1322 o_font_set = g_hash_table_lookup (font_loaded,
1323 GUINT_TO_POINTER ((gunichar)character));
1324 if (o_font_set != NULL) {
1325 o_font_set->font_text_size = width;
1326 } else {
1327 gchar outbuf[7];
1328 gint l = g_unichar_to_utf8 (character, outbuf);
1329 outbuf[l] = '\0';
1330 fprintf(stderr,
1331 "o_text_set_info_font: character %s not found!!!\n", outbuf);
1336 /*! \brief Create a string representation of the text object
1337 * \par Function Description
1338 * This function takes a text \a object and return a string
1339 * according to the file format definition.
1341 * \param [in] object a text OBJECT
1342 * \return the string representation of the text OBJECT
1344 char *o_text_save(OBJECT *object)
1346 int x, y;
1347 int color;
1348 int size;
1349 char *string;
1350 char *buf;
1351 int num_lines;
1353 x = object->text->x;
1354 y = object->text->y;
1356 string = object->text->string;
1357 size = object->text->size;
1359 /* Use the right color */
1360 if (object->saved_color == -1) {
1361 color = object->color;
1362 } else {
1363 color = object->saved_color;
1366 /* string can have multiple lines (separated by \n's) */
1367 num_lines = o_text_num_lines(string);
1369 buf = g_strdup_printf("%c %d %d %d %d %d %d %d %d %d\n%s", object->type,
1370 x, y, color, size, object->visibility,
1371 object->show_name_value, object->text->angle,
1372 object->text->alignment, num_lines, string);
1374 return(buf);
1377 /*! \brief recreate the graphics of a text object
1378 * \par Function Description
1379 * This function updates the underlying primary of the text object
1380 * \a o_current.
1382 * \param o_current The text object to update
1384 void o_text_recreate(OBJECT *o_current)
1386 char *name = NULL;
1387 char *value = NULL;
1388 TEXT *text = o_current->text;
1390 update_disp_string (o_current);
1392 o_list_delete_rest(text->toplevel, text->prim_objs);
1394 if (o_current->visibility == VISIBLE ||
1395 (o_current->visibility == INVISIBLE && o_current->show_hidden)) {
1397 /* need to create that head node if complex is null */
1398 if (text->prim_objs == NULL) {
1399 text->prim_objs = o_text_new_head(text->toplevel);
1402 text->prim_objs = o_text_create_string (text->toplevel,
1403 text->prim_objs,
1404 text->disp_string,
1405 text->size,
1406 o_current->color,
1407 text->x,
1408 text->y,
1409 text->alignment,
1410 text->angle);
1412 o_complex_set_saved_color_only(text->prim_objs,
1413 o_current->saved_color);
1414 text->displayed_width = o_text_width (text->toplevel,
1415 text->disp_string,
1416 text->size/2);
1417 text->displayed_height = o_text_height (text->disp_string,
1418 text->size);
1419 } else {
1420 /* make sure list is truly free */
1421 s_delete_list_fromstart(text->toplevel, text->prim_objs);
1422 text->prim_objs = NULL;
1423 text->displayed_width = 0;
1424 text->displayed_height = 0;
1427 o_text_recalc(o_current);
1429 g_free(name);
1430 g_free(value);
1433 /*! \brief move a text object
1434 * \par Function Description
1435 * This function changes the position of a text object \a o_current.
1437 * \param [in] dx The x-distance to move the object
1438 * \param [in] dy The y-distance to move the object
1439 * \param [in] o_current The text OBJECT to be moved
1441 void o_text_translate_world(int dx, int dy, OBJECT *o_current)
1443 o_current->text->x = o_current->text->x + dx;
1444 o_current->text->y = o_current->text->y + dy;
1446 o_list_translate_world(dx, dy, o_current->text->prim_objs);
1448 /* Update bounding box */
1449 o_text_recalc(o_current);
1452 /*! \brief create a copy of a text object
1453 * \par Function Description
1454 * This function creates a copy of the text object \a o_current.
1456 * \param [in] toplevel The TOPLEVEL object
1457 * \param [in] o_current The object that is copied
1458 * \return a new text object
1460 static OBJECT *o_text_copy(TOPLEVEL *toplevel, OBJECT *o_current)
1462 OBJECT *new_obj;
1463 int color;
1465 if (o_current->saved_color == -1) {
1466 color = o_current->color;
1467 } else {
1468 color = o_current->saved_color;
1471 new_obj = o_text_new (toplevel, OBJ_TEXT, color,
1472 o_current->text->x, o_current->text->y,
1473 o_current->text->alignment,
1474 o_current->text->angle,
1475 o_current->text->string,
1476 o_current->text->size,
1477 o_current->visibility,
1478 o_current->show_name_value);
1480 return new_obj;
1483 /*! \brief delete a font set
1484 * \par Function Description
1485 * This is a GHRFunc function that deletes a single font set.
1487 * \param [in] key The hash key (the font character)
1488 * \param [in] value The value of the hash table (the font object)
1489 * \param [in] user_data Data supplied by the user (the TOPLEVEL object)
1491 static gboolean delete_font_set (gpointer key G_GNUC_UNUSED, gpointer value,
1492 gpointer user_data)
1494 OBJECT *tmp = (OBJECT*)value;
1495 TOPLEVEL *toplevel = (TOPLEVEL*)user_data;
1497 if (tmp != NULL) {
1498 if (tmp->font_prim_objs != NULL) {
1499 s_delete_list_fromstart (toplevel, tmp->font_prim_objs);
1500 tmp->font_prim_objs = NULL;
1502 /* do not use s_delete() as tmp is not fully initialized */
1503 g_free (tmp->name);
1505 /* Do not free tmp here since it will be freed with the function */
1506 /* that was specified when the hash table was created. */
1509 return TRUE;
1512 /*! \brief free the font hash tables
1513 * \par Function Description
1514 * This function destroys the two global font hash tables
1515 * <b>font_loaded</b> and <b>font_char_to_file</b>
1517 void o_text_freeallfonts(TOPLEVEL *toplevel)
1519 /* destroy the char-to-objects hastable */
1520 g_hash_table_foreach_remove (font_loaded,
1521 delete_font_set,
1522 toplevel);
1523 g_hash_table_destroy (font_loaded);
1524 font_loaded = NULL;
1526 /* destroy the font-to-filename hashtable */
1527 g_hash_table_destroy (font_char_to_file);
1528 font_char_to_file = NULL;
1532 /*! \brief write a text string to a postscript file
1533 * \par Function Description
1534 * This function writes the single \a string into the postscript file \a fp.
1536 * \param [in] fp pointer to a FILE structure
1537 * \param [in] string The string to print
1538 * \param [in] unicode_count Number of items in the unicode table
1539 * \param [in] unicode_table Table of unicode items
1541 * \todo investigate whether the TAB character is handled correctly
1543 void o_text_print_text_string(FILE *fp, char *string, int unicode_count,
1544 gunichar *unicode_table)
1546 int j;
1547 gchar *aux;
1548 gunichar current_char, c;
1550 if (!string)
1552 return;
1555 aux = string;
1557 fprintf(fp, "(");
1559 while (aux && ((gunichar) (*aux) != 0)) {
1560 current_char = g_utf8_get_char_validated(aux, -1);
1561 if (current_char == '(' || current_char == ')' || current_char == '\\') {
1562 fprintf(fp, "\\");
1565 c = current_char;
1566 if (c >= 128) {
1567 current_char = '?';
1568 if (unicode_count) {
1569 for (j = 0; j < unicode_count; j++)
1570 if (c == unicode_table[j]) {
1571 current_char = j + 128;
1572 break;
1578 if (current_char == '\t') {
1579 /* Output eight spaces instead of the tab character */
1580 fprintf(fp, " ");
1581 } else {
1582 fprintf(fp, "%c", current_char);
1585 aux = g_utf8_find_next_char(aux, NULL);
1588 fprintf(fp,") ");
1591 /*! \brief print a text object into a postscript file
1592 * \par Function Description
1593 * This function writes the postscript representation of the text object
1594 * \a o_current into the the file \a fp.
1595 * \param [in] toplevel The TOPLEVEL object
1596 * \param [in] fp pointer to a FILE structure
1597 * \param [in] o_current The OBJECT to print
1598 * \param [in] origin_x x-coord of the postscript origin
1599 * \param [in] origin_y y-coord of the postscript origin
1600 * \param [in] unicode_count Number of items in the unicode table
1601 * \param [in] unicode_table Table of unicode items
1603 void o_text_print(TOPLEVEL *toplevel, FILE *fp, OBJECT *o_current,
1604 int origin_x, int origin_y,
1605 int unicode_count, gunichar *unicode_table)
1607 int alignment;
1608 char *centering_control = NULL;
1609 char *p,*s;
1610 char *output_string = NULL;
1611 char *name = NULL;
1612 char *value = NULL;
1613 int x, y, angle, len, char_height;
1614 float font_size;
1617 if (!o_current->text->string) {
1618 return;
1621 if (toplevel->print_color) {
1622 f_print_set_color(fp, o_current->color);
1626 if (o_attrib_get_name_value(o_current->text->string, &name, &value)) {
1627 switch(o_current->show_name_value) {
1628 case(SHOW_NAME_VALUE):
1629 output_string = g_strdup(o_current->text->string);
1630 break;
1632 case(SHOW_NAME):
1633 output_string = g_strdup(name);
1634 break;
1636 case(SHOW_VALUE):
1637 output_string = g_strdup(value);
1638 break;
1640 } else {
1641 output_string = g_strdup(o_current->text->string);
1644 /* Apply alignment map to apply when text is 180 degrees rotated.
1645 * We want the text on the printer to appear upside right, even
1646 * though mathematically it aught to be upside down. To make this
1647 * work, we will reset the angle to 0, when it's equal to 180
1648 * degrees, then apply a transformation to the origin location as if
1649 * the text was rotated about that point. E.g. if the text origin
1650 * was at the lower left, and the text was rotated by 180 degrees,
1651 * it would be as if the origin was at the upper right. The same
1652 * reasoning has been applied to all 8 other text origins.
1653 * MIDDLE_MIDDLE maps to itself.
1655 alignment = o_current->text->alignment;
1656 angle = o_current->text->angle;
1657 if(angle == 180) {
1658 angle = 0; /* reset angle to 0 to make text upright */
1659 switch(alignment) {
1660 case(LOWER_LEFT): alignment = UPPER_RIGHT;
1661 break;
1662 case(MIDDLE_LEFT): alignment = MIDDLE_RIGHT;
1663 break;
1664 case(UPPER_LEFT): alignment = LOWER_RIGHT;
1665 break;
1666 case(LOWER_MIDDLE): alignment = UPPER_MIDDLE;
1667 break;
1668 case(MIDDLE_MIDDLE): alignment = MIDDLE_MIDDLE;
1669 break;
1670 case(UPPER_MIDDLE): alignment = LOWER_MIDDLE;
1671 break;
1672 case(LOWER_RIGHT): alignment = UPPER_LEFT;
1673 break;
1674 case(MIDDLE_RIGHT): alignment = MIDDLE_LEFT;
1675 break;
1676 case(UPPER_RIGHT): alignment = LOWER_LEFT;
1677 break;
1681 /* Create an appropriate control string for the centering. */
1682 switch(alignment) {
1683 /* hcenter rjustify vcenter vjustify */
1684 case(LOWER_LEFT): centering_control = "false false false false";
1685 break;
1686 case(MIDDLE_LEFT): centering_control = "false false true false";
1687 break;
1688 case(UPPER_LEFT): centering_control = "false false false true";
1689 break;
1690 case(LOWER_MIDDLE): centering_control = "true false false false";
1691 break;
1692 case(MIDDLE_MIDDLE): centering_control = "true false true false";
1693 break;
1694 case(UPPER_MIDDLE): centering_control = "true false false true";
1695 break;
1696 case(LOWER_RIGHT): centering_control = "false true false false";
1697 break;
1698 case(MIDDLE_RIGHT): centering_control = "false true true false";
1699 break;
1700 case(UPPER_RIGHT): centering_control = "false true false true";
1701 break;
1704 char_height = o_text_height("a", o_current->text->size);
1705 fprintf(fp,"%s %f [",centering_control,(float)(char_height*LINE_SPACING));
1707 /* split the line at each newline and print them */
1708 p = output_string; /* Current point */
1709 s = output_string; /* Start of the current string */
1710 len = strlen(output_string)+1;
1711 while(len != 0) {
1712 /* Have we reached the end of a line? */
1713 if((*p == '\n') || (*p == '\0')) {
1714 /* Yes, replace the newline with a NULL and output the string */
1715 *p = '\0';
1716 o_text_print_text_string(fp,s,unicode_count,unicode_table);
1717 /* Update output string start for next string */
1718 s = p+1; /* One past the current character. */
1720 p++; /* Advance to next character */
1721 len--; /* Keep track of how many characters left to process */
1724 /* Finish up with the rest of the text print command */
1725 /* Collect pertinent info about the text location */
1726 x = o_current->text->x;
1727 y = o_current->text->y;
1728 font_size = (((float)(o_current->text->size))
1729 * toplevel->postscript_font_scale / 72.0 * 1000.0);
1730 fprintf(fp,"] %d %d %d %f text\n",angle,x,y,font_size);
1733 g_free(output_string);
1734 g_free(name);
1735 g_free(value);
1739 /*! \brief rotate a text object around a centerpoint
1740 * \par Function Description
1741 * This function rotates a text \a object around the point
1742 * (\a world_centerx, \a world_centery).
1744 * \param [in] world_centerx x-coord of the rotation center
1745 * \param [in] world_centery y-coord of the rotation center
1746 * \param [in] angle The angle to rotate the text object
1747 * \param [in] object The text object
1748 * \note only steps of 90 degrees are allowed for the \a angle
1750 void o_text_rotate_world(int world_centerx, int world_centery,
1751 int angle, OBJECT *object)
1753 int x, y;
1754 int newx, newy;
1756 g_return_if_fail(object != NULL);
1757 g_return_if_fail(object->type == OBJ_TEXT);
1759 object->text->angle = ( object->text->angle + angle ) % 360;
1761 x = object->text->x + (-world_centerx);
1762 y = object->text->y + (-world_centery);
1764 rotate_point_90(x, y, angle, &newx, &newy);
1766 x = newx + (world_centerx);
1767 y = newy + (world_centery);
1769 o_text_translate_world(x-object->text->x, y-object->text->y, object);
1771 o_text_recreate(object);
1775 /*! \brief mirror a text object horizontaly at a centerpoint
1776 * \par Function Description
1777 * This function mirrors a text \a object horizontaly at the point
1778 * (\a world_centerx, \a world_centery).
1780 * \param [in] world_centerx x-coord of the mirror position
1781 * \param [in] world_centery y-coord of the mirror position
1782 * \param [in] object The text object
1784 void o_text_mirror_world(int world_centerx, int world_centery,
1785 OBJECT *object)
1787 int origx, origy;
1788 int x, y;
1790 origx = object->text->x;
1791 origy = object->text->y;
1793 x = origx + (-world_centerx);
1794 y = origy + (-world_centery);
1796 if ((object->text->angle%180)==0) {
1797 switch(object->text->alignment) {
1798 case(LOWER_LEFT):
1799 object->text->alignment=LOWER_RIGHT;
1800 break;
1802 case(MIDDLE_LEFT):
1803 object->text->alignment=MIDDLE_RIGHT;
1804 break;
1806 case(UPPER_LEFT):
1807 object->text->alignment=UPPER_RIGHT;
1808 break;
1810 case(LOWER_RIGHT):
1811 object->text->alignment=LOWER_LEFT;
1812 break;
1814 case(MIDDLE_RIGHT):
1815 object->text->alignment=MIDDLE_LEFT;
1816 break;
1818 case(UPPER_RIGHT):
1819 object->text->alignment=UPPER_LEFT;
1820 break;
1822 default:
1823 break;
1825 } else {
1826 switch(object->text->alignment) {
1827 case(LOWER_LEFT):
1828 object->text->alignment=UPPER_LEFT;
1829 break;
1831 case(UPPER_LEFT):
1832 object->text->alignment=LOWER_LEFT;
1833 break;
1835 case(LOWER_RIGHT):
1836 object->text->alignment=UPPER_RIGHT;
1837 break;
1839 case(UPPER_RIGHT):
1840 object->text->alignment=LOWER_RIGHT;
1841 break;
1843 case(LOWER_MIDDLE):
1844 object->text->alignment=UPPER_MIDDLE;
1845 break;
1847 case(UPPER_MIDDLE):
1848 object->text->alignment=LOWER_MIDDLE;
1849 break;
1851 default:
1852 break;
1856 object->text->x = -x + (world_centerx);
1857 object->text->y = y + (world_centery);
1859 o_text_recreate(object);
1862 /*! \brief Calculates the distance between the given point and the closest
1863 * point on the text.
1865 * This function will calculate the distance to the text regardless
1866 * if the text is visible or not.
1868 * \param [in] text the text of the OBJECT
1869 * \param [in] x The x coordinate of the given point.
1870 * \param [in] y The y coordinate of the given point.
1871 * \return The shortest distance from the object to the point. If the
1872 * distance cannot be calculated, this function returns a really large
1873 * number (G_MAXDOUBLE). With an invalid parameter, this function
1874 * returns G_MAXDOUBLE.
1876 gdouble o_text_shortest_distance(TEXT const *text, gint x, gint y)
1878 gdouble distance;
1879 gdouble shortest_distance = G_MAXDOUBLE;
1880 OBJECT const *temp;
1882 if (text == NULL) {
1883 g_critical("o_text_shortest_distance(): text == NULL\n");
1884 return G_MAXDOUBLE;
1887 temp = text->prim_objs;
1889 if (temp != NULL) {
1890 temp = temp->next;
1893 while (temp != NULL) {
1894 distance = o_shortest_distance(temp, x, y);
1896 shortest_distance = min(shortest_distance, distance);
1898 temp = temp->next;
1901 return shortest_distance;
1904 /*! \brief Set the string displayed by a text object.
1905 * \par Function Description
1906 * Updates the text object with a new text string.
1908 * \param [in] obj The text object.
1909 * \param [in] new_string The new value.
1911 void o_text_set_string(OBJECT *obj, const gchar *new_string)
1913 g_return_if_fail(new_string != NULL);
1914 o_text_take_string(obj, g_strdup(new_string));
1917 void o_text_take_string(OBJECT *obj, gchar *new_string)
1919 g_return_if_fail (obj != NULL);
1920 g_return_if_fail (obj->type == OBJ_TEXT);
1921 g_return_if_fail (obj->text != NULL);
1922 g_return_if_fail (new_string != NULL);
1924 g_free (obj->text->string);
1925 obj->text->string = new_string;
1927 g_signal_emit_by_name(obj, "changed");
1928 if (obj->attached_to) {
1929 g_signal_emit_by_name(obj->attached_to, "attribs-changed");
1933 /*! \brief Get the string displayed by a text object.
1934 * \par Function Description
1935 * Retrieve the text string from a text object. The returned string
1936 * should be treated as constant.
1938 * \param [in] obj The text object.
1939 * \return The text object's string, or NULL on failure.
1941 const gchar *o_text_get_string(OBJECT const *obj)
1943 g_return_val_if_fail (obj != NULL, NULL);
1944 g_return_val_if_fail (obj->type == OBJ_TEXT, NULL);
1945 g_return_val_if_fail (obj->text != NULL, NULL);
1947 return obj->text->string;
1950 /*! \todo Finish function documentation!!!
1951 * \brief
1952 * \par Function Description
1954 * \note
1955 * The object passed in should be the REAL object, NOT any copy in any
1956 * selection list
1958 void o_text_change(OBJECT *object, char const *string, int visibility, int show)
1960 if (object == NULL) {
1961 return;
1964 if (object->type != OBJ_TEXT) {
1965 return;
1968 /* FIXME: Block changed:: until all changes are done. */
1970 /* second change the real object */
1971 o_text_set_string(object, string);
1973 object->visibility = visibility;
1974 object->show_name_value = show;
1975 g_signal_emit_by_name(object, "changed");