Use a text object as context for calculating size.
[geda-gaf/berndj.git] / libgeda / src / o_text_basic.c
blob645e602acb747c7149cfb8173e2ce3f542d39de5
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_create_string(TOPLEVEL *toplevel, OBJECT *object, char *string, int size, int color, int x, int y, int alignment, int angle);
90 static void o_text_recalc(OBJECT *o_current);
91 static enum visit_result
92 o_text_visit(OBJECT *o_current,
93 enum visit_result (*fn)(OBJECT *, void *),
94 void *context,
95 enum visit_order order, int depth);
96 static void o_text_destroy(OBJECT *o_current);
97 static OBJECT *o_text_copy(TOPLEVEL *toplevel, OBJECT *o_current);
99 /*! Default setting for text draw function. */
100 void (*text_draw_func)() = NULL;
102 /*! Hashtable storing font_character (string) as a key, and pointer to data
103 * \note
104 * This table stays global, thus all functions can access it.
106 GHashTable *font_loaded = NULL;
108 /*! Hashtable storing mapping between character and font definition file
109 * \note
110 * This table stays global, thus all functions can access it.
112 GHashTable *font_char_to_file = NULL;
114 /*! Size of a tab in characters */
115 int tab_in_chars = 8;
118 /*! \brief update the visible part of a string
119 * \par Function Description
120 * If a string is an attribute, then it is possible to hide
121 * the name or the value part of the attribute string.
122 * This functions updates the text->disp_string according
123 * to the object->show_name_value settings
125 * \param [in] o The OBJECT to update
127 static void update_disp_string(OBJECT *o)
129 char *name = NULL;
130 char *value = NULL;
131 TEXT *text = o->text;
133 g_free (text->disp_string);
135 if (o_attrib_get_name_value (text->string, &name, &value)) {
136 switch (o->show_name_value) {
137 case (SHOW_NAME_VALUE):
138 text->disp_string = g_strdup (text->string);
139 break;
141 case (SHOW_NAME):
142 text->disp_string = g_strdup (name);
143 break;
145 case (SHOW_VALUE):
146 text->disp_string = g_strdup(value);
147 break;
149 /* free the strings allocated by o_attrib_get_name_value */
150 g_free(name);
151 g_free(value);
152 } else {
153 text->disp_string = g_strdup (text->string);
157 /*! \brief calculate and return the boundaries of a text object
158 * \par Function Description
159 * This function calculates the object boudaries of a text \a object.
161 * \param [in] toplevel The TOPLEVEL object.
162 * \param [in] o_current a text object
163 * \param [out] left the left world coord
164 * \param [out] top the top world coord
165 * \param [out] right the right world coord
166 * \param [out] bottom the bottom world coord
168 int world_get_text_bounds(OBJECT *o_current, int *left,
169 int *top, int *right, int *bottom)
171 return world_get_object_list_bounds(o_current->text->prim_objs, LIST_KIND_HEAD,
172 left, top, right, bottom);
175 /*! \brief create a new text head object
176 * \par Function Description
177 * This function creates a <b>text_head</b> OBJECT. This OBJECT
178 * is just a special empty object. This object is never modified.
180 * \return new text head OBJECT
182 OBJECT *o_text_new_head(TOPLEVEL *toplevel)
184 OBJECT *new_node=NULL;
186 new_node = s_toplevel_new_object(toplevel, OBJ_HEAD, "text_head");
188 /* don't need to do this for head nodes */
189 /* ret = s_basic_link_object(new_node, NULL);*/
190 return new_node;
193 /*! \brief initialize the hash tables for the fonts
194 * \par Function Description
195 * This function initializes the two global hash tables <b>font_loaded</b>
196 * and <b>font_char_to_file</b> that are used to store the fonts characters.
198 void o_text_init(void)
200 if (font_loaded == NULL) {
201 font_loaded = g_hash_table_new_full (g_direct_hash,
202 g_direct_equal,
203 NULL,
204 g_free);
205 } else {
206 fprintf (stderr, "o_text_init: Tried to initialize an already initialized font_loaded hash table!!\n");
208 if (font_char_to_file == NULL) {
209 font_char_to_file = g_hash_table_new_full (g_direct_hash,
210 g_direct_equal,
211 NULL,
212 g_free);
214 else {
215 fprintf (stderr, "o_text_init: Tried to initialize an already initialized font_char_to_file hash table!!\n");
218 return;
222 /*! \brief print informations about some characters
223 * \note
224 * This is a debugging function. Do not use it in regular code.
226 void o_text_print_set(void)
228 OBJECT *o_current, *o_font_set;
229 char i;
231 for (i = 'A' ; i < 'Z'+1; i++) {
232 o_font_set = g_hash_table_lookup (font_loaded,
233 GUINT_TO_POINTER ((gunichar)i));
234 if (o_font_set != NULL) {
235 printf("%c: LOADED\n", i);
236 for (o_current=return_tail(o_font_set->font_prim_objs); o_current;
237 o_current=o_current->prev)
239 printf(" %s\n", o_current->name);
241 } else {
242 printf("%c: unloaded\n", i);
247 /*! \brief load a font character into an object
248 * \par Function Description
249 * This function loads a character form a font symbol file.
251 * \param [in] toplevel The TOPLEVEL object
252 * \param [in] needed_char unicode character to load
253 * return a character OBJECT
255 OBJECT *o_text_load_font(TOPLEVEL *toplevel, gunichar needed_char)
257 gchar *temp_string = NULL;
258 OBJECT *o_font_set;
259 int not_found = FALSE;
260 gchar *aux_str2;
261 GError *err = NULL;
263 /* retrieve the name of the file where the char is defined */
264 aux_str2 = g_hash_table_lookup (font_char_to_file,
265 GUINT_TO_POINTER (needed_char));
266 if (aux_str2 == NULL) {
267 /* this is needed since WinNT file systems are
268 * case insensitive, and cannot tell the difference
269 * between A.sym and a.sym. So we create a_.sym -
270 * z_.sym, this loads up the chars
272 if (needed_char >= 'a' && needed_char <= 'z') {
273 temp_string = g_strdup_printf("%s%c%c_.sym",
274 toplevel->font_directory, G_DIR_SEPARATOR,
275 needed_char);
276 } else {
277 temp_string = g_strdup_printf("%s%c%c.sym",
278 toplevel->font_directory, G_DIR_SEPARATOR,
279 needed_char);
281 } else {
282 temp_string = g_strdup_printf("%s", aux_str2);
284 aux_str2 = NULL;
286 if ( access(temp_string, R_OK) != 0 ) {
287 gchar outbuf[7];
288 gint l;
290 /* convert needed_char to a utf-8 string */
291 l = g_unichar_to_utf8 (needed_char, outbuf);
292 outbuf[l] = '\0';
293 s_log_message(_("Could not find character '%s' definition.\n"), outbuf);
295 g_free (temp_string);
296 temp_string = g_build_filename (toplevel->font_directory, "quest.sym", NULL);
297 if ( access(temp_string, R_OK) != 0 ) {
298 fprintf(stderr, _("Could not load question font char -- check font-directory keyword\n"));
299 exit(-1);
301 not_found = TRUE;
304 /* Make new object for the font set list */
305 o_font_set = (OBJECT*)g_new (OBJECT, 1);
307 o_font_set->font_prim_objs = NULL;
308 o_font_set->font_text_size = 100;
310 o_font_set->name = g_strdup_printf ("%c", needed_char);
311 o_font_set->font_prim_objs = o_text_new_head(toplevel);
313 /* Add it to the list and hash table. Some functions will need it */
314 g_hash_table_insert (font_loaded,
315 GUINT_TO_POINTER (needed_char), o_font_set);
317 if (not_found == TRUE) {
318 /* set the font text size (width) to the question mark's size */
319 /* yes, the question mark character was loaded instead of the real char */
320 /* 63 == question mark character */
322 OBJECT *aux_obj;
324 aux_obj = g_hash_table_lookup (font_loaded,
325 GUINT_TO_POINTER ((gunichar)'?'));
326 if (aux_obj == NULL) {
327 o_text_load_font(toplevel, (gunichar) '?');
328 aux_obj = g_hash_table_lookup (font_loaded,
329 GUINT_TO_POINTER ((gunichar)'?'));
332 o_font_set->font_text_size = aux_obj->font_text_size;
335 o_font_set->font_prim_objs = o_read(toplevel, o_font_set->font_prim_objs,
336 temp_string, &err);
337 if (o_font_set->font_prim_objs == NULL) {
338 g_assert (err != NULL);
339 g_warning ("o_text_basic.c: Failed to read font file: %s\n",
340 err->message);
341 g_error_free (err);
344 o_font_set->font_prim_objs = return_head(o_font_set->font_prim_objs);
346 g_free(temp_string);
348 return(o_font_set->font_prim_objs);
351 /*! \brief count the lines of a text string
352 * \par Function Description
353 * This function just counts the number of lines that are
354 * in the \a string.
356 * \param [in] string text string to count the lines
357 * \return the number of lines
359 int o_text_num_lines(const char *string)
361 int line_count = 0;
362 const gchar *aux;
363 gunichar current_char;
365 if (string == NULL) {
366 return 0;
369 /* if it's not null, then we have at least one line */
370 line_count++;
371 /* Count how many \n are in the string */
372 aux = string;
373 while (aux && ((gunichar) (*aux) != 0) ) {
374 current_char = g_utf8_get_char_validated(aux, -1);
375 if (current_char == '\n')
376 line_count++;
377 aux = g_utf8_find_next_char(aux, NULL);
380 return (line_count);
383 /*! \brief calculates the height of a text string
384 * \par Function Description
385 * This function calculates the height of a \a string depending
386 * on it's text \a size. The number of lines and the spacing
387 * between the lines are taken into account.
389 * \param [in] obj The text object that will contain the string.
390 * \param [in] string the text string
391 * \return the total height of the text string
393 int o_text_height(OBJECT *obj, const char *string)
395 int line_count = 0;
397 if (string == NULL || font_loaded == NULL) {
398 return 0;
401 /* Get the number of lines in the string */
402 line_count = o_text_num_lines(string);
404 /* 26 is the height of a single char (in mils) */
405 /* which represents a character which is 2 pts high */
406 /* So size has to be divided in half */
407 /* and it's added the LINE_SPACING*character_height of each line */
408 return(26*obj->text->size/2*(1+LINE_SPACING*(line_count-1)));
411 /*! \brief calculate the width of a text
412 * \par Function Description
413 * This function calculates the width of a text \a string
414 * depending on the text size and the width of the individual
415 * characters that are in the text string.
417 * \param [in] toplevel The TOPLEVEL object
418 * \param [in] obj The text object that will contain the string.
419 * \param [in] string The text string
420 * \return the total width of the text.
422 int o_text_width(TOPLEVEL *toplevel, OBJECT *obj, char const *string)
424 int size = obj->text->size/2;
425 int width=0, max_width=0;
426 int size_of_tab_in_coord;
427 OBJECT *o_font_set;
428 gchar const *ptr;
429 gunichar previous_char;
430 gunichar c = 0;
431 if (string == NULL || font_loaded == NULL) {
432 return 0;
435 /* Make sure TAB_CHAR_MODEL is loaded before trying to use its text */
436 /* size */
437 o_font_set = g_hash_table_lookup (
438 font_loaded, GUINT_TO_POINTER ((gunichar)TAB_CHAR_MODEL[0]));
439 if (o_font_set == NULL) {
440 o_text_load_font(toplevel, (gunichar) TAB_CHAR_MODEL[0]);
441 o_font_set = g_hash_table_lookup (
442 font_loaded, GUINT_TO_POINTER ((gunichar)TAB_CHAR_MODEL[0]));
445 /* Get the maximum tab width's in coordinates */
446 size_of_tab_in_coord = tab_in_chars * size * o_font_set->font_text_size;
448 for (ptr = string;
449 ptr != NULL && *ptr != 0;
450 ptr = g_utf8_find_next_char (ptr, NULL))
452 previous_char = c;
453 c = g_utf8_get_char_validated (ptr, -1);
455 if ( (c == (gunichar) '\\') &&
456 (previous_char != (gunichar) '\\') ) {
457 continue;
459 if ( (c == (gunichar) '_') &&
460 (previous_char == (gunichar) '\\') ) {
461 continue;
463 switch (c) {
464 case ((gunichar)'\n'):
465 width = 0;
466 break;
467 case ((gunichar)'\t'):
468 width += (size_of_tab_in_coord - (width % size_of_tab_in_coord));
469 break;
470 default:
471 /* find current_char */
472 o_font_set = g_hash_table_lookup (font_loaded,
473 GUINT_TO_POINTER (c));
474 if (o_font_set == NULL) {
475 o_text_load_font (toplevel, (gunichar)c);
476 /* let do a new search for character c */
477 o_font_set = g_hash_table_lookup (font_loaded,
478 GUINT_TO_POINTER (c));
481 if (o_font_set != NULL) {
482 width = width + size*o_font_set->font_text_size;
485 if (width > max_width) {
486 max_width = width;
491 /* the size is a fudge factor */
492 /* Changed the return value according to a suggestion of Ales. */
493 /* Yes, the -size*10 fudge factor should be removed. */
494 /* return(max_width - size*10); */
495 return max_width;
498 /*! \brief create a complex text object from a string
499 * \par Function Description
500 * This function converts the \a string into a list of basic objects.
501 * All basic objects are appendend to the \a object.
502 * The basic objects are collected from the basic font definition
503 * of each character of they are created as lines for the overbar feature.
505 * \param [in] toplevel The TOPLEVEL object
506 * \param [in] object The symbol for which to create the basic objects
507 * \param [in] string The string to create the object list from
508 * \param [in] size The size of the text object
509 * \param [in] color The color of the text object
510 * \param [in] x The x coord of the text object
511 * \param [in] y The y coord of the text object
512 * \param [in] alignment The alignment of the text object
513 * \param [in] angle The angle of the text object (in 90 degree steps)
515 static void o_text_create_string(TOPLEVEL *toplevel, OBJECT *object,
516 char *string, int size, int color,
517 int x, int y, int alignment, int angle)
519 OBJECT *temp_tail=NULL;
520 OBJECT *temp_list;
521 OBJECT *temp_obj;
522 OBJECT *start_of_char;
523 int x_offset;
524 int y_offset;
525 int text_width;
526 int text_height;
527 int char_height;
528 int line_start_x, line_start_y;
529 int sign=1;
530 int overbar_startx=0, overbar_starty=0;
531 int overbar_endx=0, overbar_endy=0;
532 int overbar_height_offset = 0;
533 gchar *ptr;
534 OBJECT *o_font_set;
535 gunichar current_char;
536 gboolean escape = FALSE, overbar_started = FALSE;
537 gboolean finish_overbar, start_overbar, leave_parser = FALSE;
538 gboolean draw_character, draw_tabulator, draw_newline;
540 g_return_if_fail(object);
541 g_return_if_fail(string);
542 g_return_if_fail(font_loaded);
544 temp_list = object->text->prim_objs;
546 /* now read in the chars */
547 temp_tail = toplevel->page_current->object_tail;
549 text_height = o_text_height(object, string);
550 char_height = o_text_height(object, "a");
551 text_width = o_text_width(toplevel, object, string);
553 switch(angle) {
554 case(0):
555 sign = -1;
556 break;
557 case(90):
558 sign = 1;
559 break;
560 case(180):
561 sign = 1;
562 break;
563 case(270):
564 sign = -1;
565 break;
568 if (angle == 0 || angle == 180) {
569 y = y - o_text_height(object, "a") + text_height;
571 switch(alignment) {
573 case(LOWER_LEFT):
574 x_offset = x;
575 y_offset = y;
576 break;
578 case(MIDDLE_LEFT):
579 x_offset = x;
580 y_offset = y + sign*0.5*text_height;
581 break;
583 case(UPPER_LEFT):
584 x_offset = x;
585 y_offset = y + sign*text_height;
586 break;
588 case(LOWER_MIDDLE):
589 x_offset = x + sign*0.5*text_width;
590 y_offset = y;
591 break;
593 case(MIDDLE_MIDDLE):
594 x_offset = x + sign*0.5*text_width;
595 y_offset = y + sign*0.5*text_height;
596 break;
598 case(UPPER_MIDDLE):
599 x_offset = x + sign*0.5*text_width;
600 y_offset = y + sign*text_height;
602 break;
604 case(LOWER_RIGHT):
605 x_offset = x + sign*text_width;
606 y_offset = y;
607 break;
609 case(MIDDLE_RIGHT):
610 x_offset = x + sign*text_width;
611 y_offset = y + sign*0.5*text_height;
612 break;
614 case(UPPER_RIGHT):
615 x_offset = x + sign*text_width;
616 y_offset = y + sign*text_height;
617 break;
619 default:
620 fprintf(stderr, "Got an invalid text alignment [%d]\n",
621 alignment);
622 fprintf(stderr, "Defaulting to Lower Left");
623 alignment = LOWER_LEFT;
624 x_offset = x;
625 y_offset = y;
626 break;
628 } else { /* angle is 90 or 270 */
629 x = x + sign*(o_text_height(object, "a") - text_height);
631 switch(alignment) {
633 case(LOWER_LEFT):
634 x_offset = x;
635 y_offset = y;
636 break;
638 case(MIDDLE_LEFT):
639 x_offset = x + sign*0.5*text_height;
640 y_offset = y;
641 break;
643 case(UPPER_LEFT):
644 x_offset = x + sign*text_height;
645 y_offset = y;
646 break;
648 case(LOWER_MIDDLE):
649 x_offset = x;
650 y_offset = y - sign*0.5*text_width;
651 break;
653 case(MIDDLE_MIDDLE):
654 x_offset = x + sign*0.5*text_height;
655 y_offset = y - sign*0.5*text_width;
656 break;
658 case(UPPER_MIDDLE):
659 x_offset = x + sign*text_height;
660 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;
689 switch(angle) {
690 case(180):
691 x_offset = x_offset - text_width;
692 y_offset = y_offset - text_height;
693 angle = 0;
694 break;
697 if (GEDA_DEBUG) {
698 printf("width: %d\n", o_text_width(toplevel, object, string));
699 printf("1 %d %d\n", x_offset, y_offset);
702 line_start_x = x_offset;
703 line_start_y = y_offset;
705 /* the overbar is 1/4 above the char height. */
706 overbar_height_offset = char_height + char_height/4;
708 for (ptr = string;
709 ptr != NULL && !leave_parser;
710 ptr = g_utf8_find_next_char (ptr, NULL)) {
711 current_char = g_utf8_get_char_validated (ptr, -1);
713 /* reset all actions */
714 finish_overbar = FALSE;
715 start_overbar = FALSE;
716 leave_parser = FALSE;
717 draw_character = FALSE;
718 draw_tabulator = FALSE;
719 draw_newline = FALSE;
721 /* state machine to interpret the string:
722 * there are two independent state variables overbar_started and escape.
723 * The actions are set according to the current character and
724 * the two state variables.
726 switch (current_char) {
727 case '\0':
728 /* end of the string */
729 if (overbar_started)
730 finish_overbar = TRUE;
731 leave_parser = TRUE;
732 break;
733 case '\\':
734 if (escape == TRUE) {
735 draw_character = TRUE;
736 escape = FALSE;
737 } else {
738 escape = TRUE;
740 break;
741 case '_':
742 if (escape == TRUE) {
743 escape = FALSE;
744 if (overbar_started == TRUE) {
745 finish_overbar = TRUE;
746 overbar_started = FALSE;
747 } else {
748 start_overbar = TRUE;
749 overbar_started = TRUE;
751 } else {
752 draw_character = TRUE;
754 break;
755 case '\n':
756 draw_newline = TRUE;
757 if (overbar_started == TRUE) {
758 finish_overbar = TRUE;
759 start_overbar = TRUE;
761 escape = FALSE;
762 break;
763 case '\t':
764 draw_tabulator = TRUE;
765 escape = FALSE;
766 break;
767 default:
768 draw_character = TRUE;
769 escape = FALSE;
772 /* execute all actions set by the state machine
773 * Note: It's important that the three actions
774 * finish_overbar, draw_newline, start_overbar are executed
775 * in exacly that order. It's required to continue overbars
776 * over newlines.
778 if (draw_character) {
779 /* get the character from the hash table */
780 o_font_set = g_hash_table_lookup (font_loaded,
781 GUINT_TO_POINTER (current_char));
782 if (o_font_set == NULL) {
783 o_text_load_font(toplevel, (gunichar) current_char);
784 o_font_set = g_hash_table_lookup (font_loaded,
785 GUINT_TO_POINTER (current_char));
788 /* Only add the character if there are primary object.
789 e.g. the space character doesn't have those */
790 if (o_font_set->font_prim_objs->next != NULL) {
791 start_of_char = temp_list;
792 temp_list = o_list_copy_all(toplevel,
793 o_font_set->font_prim_objs,
794 temp_list, NORMAL_FLAG);
796 if (start_of_char != NULL)
797 start_of_char = start_of_char->next;
799 o_scale(start_of_char, size/2, size/2);
801 /* Rotate and translate the character to its world position */
802 o_list_rotate_world(0, 0, angle, start_of_char);
803 o_list_translate_world(x_offset, y_offset, start_of_char);
806 /* Calculate the position of the next character */
807 switch(angle) {
808 case(0):
809 x_offset = (x_offset) + size/2*o_font_set->font_text_size;
810 break;
811 case(90):
812 y_offset = (y_offset) + size/2*o_font_set->font_text_size;
813 break;
814 case(180):
815 x_offset = (x_offset) - size/2*o_font_set->font_text_size;
816 break;
817 case(270):
818 y_offset = (y_offset) - size/2*o_font_set->font_text_size;
819 break;
823 if (draw_tabulator) {
824 gint size_of_tab_in_coord;
825 gint rel_char_coord;
826 /* Get the maximum tab width's in coordinates */
827 size_of_tab_in_coord = (tab_in_chars *
828 o_text_width(toplevel, object, TAB_CHAR_MODEL));
830 switch (angle) {
831 case 0:
832 case 180:
833 rel_char_coord = x_offset - line_start_x;
834 x_offset += (size_of_tab_in_coord -
835 (rel_char_coord % size_of_tab_in_coord));
836 break;
837 case 90:
838 rel_char_coord = y_offset - line_start_y;
839 y_offset += (size_of_tab_in_coord -
840 (rel_char_coord % size_of_tab_in_coord));
841 break;
842 case 270:
843 rel_char_coord = line_start_y - y_offset;
844 y_offset -= (size_of_tab_in_coord -
845 (rel_char_coord % size_of_tab_in_coord));
846 break;
847 default:
848 fprintf(stderr, "o_text_create_string: Angle not supported\n");
849 break;
853 if (finish_overbar) {
854 switch (angle) {
855 case 0:
856 overbar_endx = x_offset;
857 overbar_endy = y_offset + overbar_height_offset;
858 break;
859 case 90:
860 overbar_endx = x_offset - overbar_height_offset;
861 overbar_endy = y_offset;
862 break;
863 case 180:
864 overbar_endx = x_offset;
865 overbar_endy = y_offset - overbar_height_offset;
866 break;
867 case 270:
868 overbar_endx = x_offset + overbar_height_offset;
869 overbar_endy = y_offset;
870 break;
871 default:
872 fprintf(stderr, "o_text_create_string: Angle not supported\n");
873 break;
875 /* Now add the overbar (if it is not a zero length overbar) */
876 if ((overbar_startx != overbar_endx)
877 || (overbar_starty != overbar_endy)) {
878 temp_obj = o_line_new(toplevel, OBJ_LINE, color,
879 overbar_startx, overbar_starty,
880 overbar_endx, overbar_endy);
881 temp_list = s_basic_link_object(temp_obj, temp_list);
885 if (draw_newline) {
886 switch (angle) {
887 case 0:
888 x_offset = line_start_x;
889 y_offset = line_start_y - char_height * LINE_SPACING;
890 break;
891 case 90:
892 x_offset = line_start_x + char_height * LINE_SPACING;
893 y_offset = line_start_y;
894 break;
895 case 180:
896 x_offset = line_start_x;
897 y_offset = line_start_y + char_height * LINE_SPACING;
898 break;
899 case 270:
900 x_offset = line_start_x - char_height * LINE_SPACING;
901 y_offset = line_start_y;
902 break;
903 default:
904 fprintf(stderr, "o_text_create_string: Angle not supported\n");
905 break;
907 line_start_x = x_offset;
908 line_start_y = y_offset;
911 if (start_overbar) {
912 switch (angle) {
913 case 0:
914 overbar_startx = x_offset;
915 overbar_starty = y_offset + overbar_height_offset;
916 break;
917 case 90:
918 overbar_startx = x_offset - overbar_height_offset;
919 overbar_starty = y_offset;
920 break;
921 case 180:
922 overbar_startx = x_offset;
923 overbar_starty = y_offset - overbar_height_offset;
924 break;
925 case 270:
926 overbar_startx = x_offset + overbar_height_offset;
927 overbar_starty = y_offset;
928 break;
929 default:
930 fprintf(stderr, "o_text_create_string: Angle not supported\n");
931 break;
936 o_complex_set_color(object, color);
938 toplevel->page_current->object_tail = temp_tail;
941 static OBJECT *o_text_new_at_xy(TOPLEVEL *toplevel,
942 char type, int color, int x, int y)
944 OBJECT *new_node=NULL;
945 TEXT *text;
947 new_node = s_toplevel_new_object(toplevel, type, "text");
948 new_node->color = color;
950 text = g_malloc(sizeof (TEXT));
951 new_node->text = text;
953 text->x = x;
954 text->y = y;
955 text->toplevel = g_object_ref(toplevel);
956 text->string = NULL;
957 text->prim_objs = o_text_new_head(toplevel);
959 new_node->copy_func = &o_text_copy;
960 new_node->bounds_recalc_func = o_text_recalc;
961 new_node->draw_func = text_draw_func;
962 new_node->psprint_func = &o_text_print;
963 new_node->visit_func = &o_text_visit;
964 new_node->destroy_func = &o_text_destroy;
966 return new_node;
969 /*! \brief Creates a text OBJECT and the graphical objects representing it
970 * \par Function Description
971 * Create an OBJECT of type OBJ_TEXT.
972 * Also add the OBJECTs forming the graphical representation of the visible
973 * string, to the text OBJECT's prim_objs list.
975 * \param [in] toplevel The TOPLEVEL object.
976 * \param [in] type OBJ_TEXT (TODO: why bother)
977 * \param [in] color The color of the text.
978 * \param [in] x World x coord of text.
979 * \param [in] y World y coord of text.
980 * \param [in] alignment How text bounding box aligns on (x, y).
981 * \param [in] angle Angle at which text will appear.
982 * \param [in] string The text.
983 * \param [in] size Text size.
984 * \param [in] visibility VISIBLE or INVISIBLE.
985 * \param [in] show_name_value SHOW_NAME_VALUE or friends.
986 * \return Pointer to text OBJECT.
988 * \note
989 * Caller is responsible for string; this function allocates its own copy.
991 OBJECT *o_text_new(TOPLEVEL *toplevel,
992 char type, int color, int x, int y, int alignment,
993 int angle, const char *string, int size,
994 int visibility, int show_name_value)
996 OBJECT *new_node=NULL;
997 OBJECT *temp_list=NULL;
998 TEXT *text;
999 char *name = NULL;
1000 char *value = NULL;
1002 if (string == NULL) {
1003 return(NULL);
1006 new_node = o_text_new_at_xy(toplevel, type, color, x, y);
1007 text = new_node->text;
1009 text->string = g_strdup (string);
1010 text->disp_string = NULL; /* We'll fix this up later */
1011 text->length = strlen(string);
1012 text->size = size;
1013 text->alignment = alignment;
1014 text->angle = angle;
1016 new_node->visibility = visibility;
1017 new_node->show_hidden = toplevel->show_hidden_default;
1018 new_node->show_name_value = show_name_value;
1020 update_disp_string (new_node);
1022 if (visibility == VISIBLE ||
1023 (visibility == INVISIBLE && toplevel->show_hidden_default)) {
1024 o_text_create_string(toplevel, new_node,
1025 text->disp_string, size, color,
1026 x, y, alignment, angle);
1027 new_node->text->displayed_width = o_text_width(toplevel, new_node,
1028 text->disp_string);
1029 new_node->text->displayed_height = o_text_height(new_node, text->disp_string);
1030 } else {
1031 new_node->text->displayed_width = 0;
1032 new_node->text->displayed_height = 0;
1035 /* Update bounding box */
1036 o_text_recalc(new_node);
1038 g_free(name);
1039 g_free(value);
1040 return new_node;
1043 /*! \brief update the visual boundaries of the text object
1044 * \par Function Description
1045 * This function updates the boundaries of the object \a o_current.
1047 * \param [in] toplevel The TOPLEVEL object
1048 * \param [in] o_current The OBJECT to update
1050 static void o_text_recalc(OBJECT *o_current)
1052 int left, right, top, bottom;
1054 if (o_current->visibility == INVISIBLE && !o_current->show_hidden) {
1055 return;
1058 if (o_current->text->x == -1 && o_current->text->y == -1) {
1059 /* Off-schematic text has no bounding box. */
1060 return;
1063 if (!world_get_text_bounds(o_current, &left, &top, &right, &bottom))
1064 return;
1066 o_current->w_left = left;
1067 o_current->w_top = top;
1068 o_current->w_right = right;
1069 o_current->w_bottom = bottom;
1070 o_current->w_bounds_valid = TRUE;
1073 static enum visit_result
1074 o_text_visit(OBJECT *o_current,
1075 enum visit_result (*fn)(OBJECT *, void *), void *context,
1076 enum visit_order order, int depth)
1078 g_return_if_fail(o_current->type == OBJ_TEXT);
1079 g_return_if_fail(o_current->text->prim_objs != NULL);
1081 return (s_visit_list(o_current->text->prim_objs, LIST_KIND_HEAD, fn, context,
1082 order, depth));
1085 static void o_text_destroy(OBJECT *o_current)
1087 g_free(o_current->text->string);
1088 g_free(o_current->text->disp_string);
1090 if (o_current->text->prim_objs) {
1091 s_delete_list_fromstart(o_current->text->toplevel,
1092 o_current->text->prim_objs);
1095 g_object_unref(o_current->text->toplevel);
1097 g_free(o_current->text);
1100 /*! \brief read a text object from a char buffer
1101 * \par Function Description
1102 * This function reads a text object from the textbuffer \a tb and
1103 * the text starting with the line \a firstline.
1105 * \param [in] toplevel The TOPLEVEL object
1106 * \param [in] first_line the first line of the text
1107 * \param [in] tb a text buffer (usually a line of a schematic file)
1108 * \param [in] release_ver The release number gEDA
1109 * \param [in] fileformat_ver a integer value of the file format
1110 * \return The object list
1112 OBJECT *o_text_read(TOPLEVEL *toplevel,
1113 const char *first_line,
1114 TextBuffer *tb,
1115 unsigned int release_ver,
1116 unsigned int fileformat_ver)
1118 OBJECT *new_obj;
1119 char type;
1120 int x, y;
1121 int color;
1122 int size;
1123 int visibility;
1124 int show_name_value;
1125 int angle;
1126 int alignment;
1127 int num_lines = 0;
1128 int i;
1129 char* string = NULL;
1130 GString *textstr;
1132 if (fileformat_ver >= 1) {
1133 sscanf(first_line, "%c %d %d %d %d %d %d %d %d %d\n", &type, &x, &y,
1134 &color, &size,
1135 &visibility, &show_name_value,
1136 &angle, &alignment, &num_lines);
1137 } else if (release_ver < VERSION_20000220) {
1138 /* yes, above less than (not less than and equal) is correct. The format */
1139 /* change occurred in 20000220 */
1140 sscanf(first_line, "%c %d %d %d %d %d %d %d\n", &type, &x, &y,
1141 &color, &size,
1142 &visibility, &show_name_value,
1143 &angle);
1144 alignment = LOWER_LEFT; /* older versions didn't have this */
1145 num_lines = 1; /* only support a single line */
1146 } else {
1147 sscanf(first_line, "%c %d %d %d %d %d %d %d %d\n", &type, &x, &y,
1148 &color, &size,
1149 &visibility, &show_name_value,
1150 &angle, &alignment);
1151 num_lines = 1; /* only support a single line */
1154 if (size == 0) {
1155 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);
1158 switch(angle) {
1159 case(0):
1160 case(90):
1161 case(180):
1162 case(270):
1163 break;
1165 default:
1166 s_log_message(_("Found an unsupported text angle [ %c %d %d %d %d %d %d %d %d ]\n"),
1167 type, x, y, color, size, visibility, show_name_value, angle, alignment);
1168 s_log_message(_("Setting angle to 0\n"));
1169 angle=0;
1170 break;
1174 switch(alignment) {
1175 case(LOWER_LEFT):
1176 case(MIDDLE_LEFT):
1177 case(UPPER_LEFT):
1178 case(LOWER_MIDDLE):
1179 case(MIDDLE_MIDDLE):
1180 case(UPPER_MIDDLE):
1181 case(LOWER_RIGHT):
1182 case(MIDDLE_RIGHT):
1183 case(UPPER_RIGHT):
1185 break;
1187 default:
1188 s_log_message(_("Found an unsupported text alignment [ %c %d %d %d %d %d %d %d %d ]\n"),
1189 type, x, y, color, size, visibility, show_name_value, angle, alignment);
1190 s_log_message(_("Setting alignment to LOWER_LEFT\n"));
1191 alignment = LOWER_LEFT;
1192 break;
1195 if (color < 0 || color > MAX_COLORS) {
1196 s_log_message(_("Found an invalid color [ %s ]\n"), first_line);
1197 s_log_message(_("Setting color to WHITE\n"));
1198 color = WHITE;
1201 g_assert(num_lines && num_lines > 0);
1203 textstr = g_string_new ("");
1204 for (i = 0; i < num_lines; i++) {
1205 gchar *line;
1207 line = s_textbuffer_next_line (tb);
1209 if (line != NULL)
1211 textstr = g_string_append (textstr, line);
1214 /* retrieve the character string from the GString */
1215 string = g_string_free (textstr, FALSE);
1217 string = remove_last_nl(string);
1219 /* convert the character string to UTF-8 if necessary */
1220 if (!g_utf8_validate (string, -1, NULL)) {
1221 /* if it is not utf-8, it is ISO_8859-15 */
1222 gchar *tmp = g_convert (string, strlen (string),
1223 "UTF-8", "ISO_8859-15",
1224 NULL, NULL, NULL);
1225 if (tmp == NULL) {
1226 fprintf (stderr, "Failed to convert text string to UTF-8: %s.\n",
1227 string);
1228 } else {
1229 /* successfully converted string, now use tmp as string */
1230 g_free (string);
1231 string = tmp;
1235 new_obj = o_text_new(toplevel, type, color, x, y,
1236 alignment, angle, string,
1237 size, visibility, show_name_value);
1238 g_free(string);
1240 return new_obj;
1243 /*! \brief read and set infos of a font object
1244 * \par Function Description
1245 * This function reads the font definition buffer \a buf and sets
1246 * the width of a character. This function also deals with the special,
1247 * invisible character space and newline.
1249 * \param [in] buf the font definition according to the geda file format
1250 * \todo Investigate why the TAB character is not defined here.
1252 void o_text_set_info_font(char buf[])
1254 char type;
1255 int width;
1256 gunichar character=0;
1257 gchar *buf_ptr;
1258 int special=0;
1259 char *string;
1260 OBJECT *o_font_set;
1262 string = remove_nl (buf);
1264 /* parse the font info: */
1265 buf_ptr = (gchar*)string;
1266 /* - type */
1267 type = *buf_ptr++;
1268 if (type != INFO_FONT) {
1269 g_critical ("o_text_set_info_font: Bad font type '%c', expected '%c'\n",
1270 type, INFO_FONT);
1271 return;
1274 while (buf_ptr != NULL && *buf_ptr == ' ') buf_ptr++;
1275 /* - character */
1276 if (buf_ptr != NULL && *buf_ptr != '\0') {
1277 character = g_utf8_get_char_validated (buf_ptr, -1);
1278 if (character == (gunichar)-1) {
1279 s_log_message (_("Failed to validate utf-8 character in font definition: \"%s\".\n"),
1280 string);
1281 return;
1283 /* move buf_ptr just after character */
1284 buf_ptr = g_utf8_find_next_char (buf_ptr, NULL);
1286 while (buf_ptr != NULL && *buf_ptr == ' ') buf_ptr++;
1287 /* - width and special */
1288 if (buf_ptr != NULL) {
1289 sscanf (buf_ptr, "%d %d\n", &width, &special);
1292 /* deal with special characters */
1293 if (special == 1) {
1294 switch (character) {
1295 case ((gunichar)'_'):
1296 /* space */
1297 character = (gunichar)' ';
1298 break;
1299 case ((gunichar)'n'):
1300 /* newline */
1301 character = (gunichar)'\n';
1302 break;
1306 o_font_set = g_hash_table_lookup (font_loaded,
1307 GUINT_TO_POINTER ((gunichar)character));
1308 if (o_font_set != NULL) {
1309 o_font_set->font_text_size = width;
1310 } else {
1311 gchar outbuf[7];
1312 gint l = g_unichar_to_utf8 (character, outbuf);
1313 outbuf[l] = '\0';
1314 fprintf(stderr,
1315 "o_text_set_info_font: character %s not found!!!\n", outbuf);
1320 /*! \brief Create a string representation of the text object
1321 * \par Function Description
1322 * This function takes a text \a object and return a string
1323 * according to the file format definition.
1325 * \param [in] object a text OBJECT
1326 * \return the string representation of the text OBJECT
1328 char *o_text_save(OBJECT *object)
1330 int x, y;
1331 int color;
1332 int size;
1333 char *string;
1334 char *buf;
1335 int num_lines;
1337 x = object->text->x;
1338 y = object->text->y;
1340 string = object->text->string;
1341 size = object->text->size;
1343 /* Use the right color */
1344 if (object->saved_color == -1) {
1345 color = object->color;
1346 } else {
1347 color = object->saved_color;
1350 /* string can have multiple lines (separated by \n's) */
1351 num_lines = o_text_num_lines(string);
1353 buf = g_strdup_printf("%c %d %d %d %d %d %d %d %d %d\n%s", object->type,
1354 x, y, color, size, object->visibility,
1355 object->show_name_value, object->text->angle,
1356 object->text->alignment, num_lines, string);
1358 return(buf);
1361 /*! \brief recreate the graphics of a text object
1362 * \par Function Description
1363 * This function updates the underlying primary of the text object
1364 * \a o_current.
1366 * \param o_current The text object to update
1368 void o_text_recreate(OBJECT *o_current)
1370 char *name = NULL;
1371 char *value = NULL;
1372 TEXT *text = o_current->text;
1374 update_disp_string (o_current);
1376 o_list_delete_rest(text->toplevel, text->prim_objs);
1378 if (o_current->visibility == VISIBLE ||
1379 (o_current->visibility == INVISIBLE && o_current->show_hidden)) {
1380 o_text_create_string(text->toplevel, o_current,
1381 text->disp_string,
1382 text->size,
1383 o_current->color,
1384 text->x, text->y,
1385 text->alignment,
1386 text->angle);
1388 o_complex_set_saved_color_only(o_current, o_current->saved_color);
1389 text->displayed_width = o_text_width(text->toplevel, o_current,
1390 text->disp_string);
1391 text->displayed_height = o_text_height(o_current, text->disp_string);
1392 } else {
1393 /* make sure list is truly free */
1394 text->displayed_width = 0;
1395 text->displayed_height = 0;
1398 o_text_recalc(o_current);
1400 g_free(name);
1401 g_free(value);
1404 /*! \brief move a text object
1405 * \par Function Description
1406 * This function changes the position of a text object \a o_current.
1408 * \param [in] dx The x-distance to move the object
1409 * \param [in] dy The y-distance to move the object
1410 * \param [in] o_current The text OBJECT to be moved
1412 void o_text_translate_world(int dx, int dy, OBJECT *o_current)
1414 o_current->text->x = o_current->text->x + dx;
1415 o_current->text->y = o_current->text->y + dy;
1417 o_list_translate_world(dx, dy, o_current->text->prim_objs);
1419 /* Update bounding box */
1420 o_text_recalc(o_current);
1423 /*! \brief create a copy of a text object
1424 * \par Function Description
1425 * This function creates a copy of the text object \a o_current.
1427 * \param [in] toplevel The TOPLEVEL object
1428 * \param [in] o_current The object that is copied
1429 * \return a new text object
1431 static OBJECT *o_text_copy(TOPLEVEL *toplevel, OBJECT *o_current)
1433 OBJECT *new_obj;
1434 int color;
1436 if (o_current->saved_color == -1) {
1437 color = o_current->color;
1438 } else {
1439 color = o_current->saved_color;
1442 new_obj = o_text_new (toplevel, OBJ_TEXT, color,
1443 o_current->text->x, o_current->text->y,
1444 o_current->text->alignment,
1445 o_current->text->angle,
1446 o_current->text->string,
1447 o_current->text->size,
1448 o_current->visibility,
1449 o_current->show_name_value);
1451 return new_obj;
1454 /*! \brief delete a font set
1455 * \par Function Description
1456 * This is a GHRFunc function that deletes a single font set.
1458 * \param [in] key The hash key (the font character)
1459 * \param [in] value The value of the hash table (the font object)
1460 * \param [in] user_data Data supplied by the user (the TOPLEVEL object)
1462 static gboolean delete_font_set (gpointer key G_GNUC_UNUSED, gpointer value,
1463 gpointer user_data)
1465 OBJECT *tmp = (OBJECT*)value;
1466 TOPLEVEL *toplevel = (TOPLEVEL*)user_data;
1468 if (tmp != NULL) {
1469 if (tmp->font_prim_objs != NULL) {
1470 s_delete_list_fromstart (toplevel, tmp->font_prim_objs);
1471 tmp->font_prim_objs = NULL;
1473 /* do not use s_delete() as tmp is not fully initialized */
1474 g_free (tmp->name);
1476 /* Do not free tmp here since it will be freed with the function */
1477 /* that was specified when the hash table was created. */
1480 return TRUE;
1483 /*! \brief free the font hash tables
1484 * \par Function Description
1485 * This function destroys the two global font hash tables
1486 * <b>font_loaded</b> and <b>font_char_to_file</b>
1488 void o_text_freeallfonts(TOPLEVEL *toplevel)
1490 /* destroy the char-to-objects hastable */
1491 g_hash_table_foreach_remove (font_loaded,
1492 delete_font_set,
1493 toplevel);
1494 g_hash_table_destroy (font_loaded);
1495 font_loaded = NULL;
1497 /* destroy the font-to-filename hashtable */
1498 g_hash_table_destroy (font_char_to_file);
1499 font_char_to_file = NULL;
1503 /*! \brief write a text string to a postscript file
1504 * \par Function Description
1505 * This function writes the single \a string into the postscript file \a fp.
1507 * \param [in] fp pointer to a FILE structure
1508 * \param [in] string The string to print
1509 * \param [in] unicode_table Table of unicode items
1511 * \todo investigate whether the TAB character is handled correctly
1513 void o_text_print_text_string(FILE *fp, char *string, GArray *unicode_table)
1515 int j;
1516 gchar *aux;
1517 gunichar current_char, c;
1519 if (!string)
1521 return;
1524 aux = string;
1526 fprintf(fp, "(");
1528 while (aux && ((gunichar) (*aux) != 0)) {
1529 current_char = g_utf8_get_char_validated(aux, -1);
1530 if (current_char == '(' || current_char == ')' || current_char == '\\') {
1531 fprintf(fp, "\\");
1534 c = current_char;
1535 if (c >= 128) {
1536 current_char = '?';
1537 for (j = 0; j < unicode_table->len; j++) {
1538 if (c == g_array_index(unicode_table, gunichar, j)) {
1539 current_char = j + 128;
1540 break;
1546 if (current_char == '\t') {
1547 /* Output eight spaces instead of the tab character */
1548 fprintf(fp, " ");
1549 } else {
1550 fprintf(fp, "%c", current_char);
1553 aux = g_utf8_find_next_char(aux, NULL);
1556 fprintf(fp,") ");
1559 /*! \brief print a text object into a postscript file
1560 * \par Function Description
1561 * This function writes the postscript representation of the text object
1562 * \a o_current into the the file \a fp.
1563 * \param [in] toplevel The TOPLEVEL object
1564 * \param [in] fp pointer to a FILE structure
1565 * \param [in] o_current The OBJECT to print
1566 * \param [in] unicode_table Table of unicode items
1568 void o_text_print(TOPLEVEL *toplevel, FILE *fp, OBJECT *o_current,
1569 double scale, GArray *unicode_table)
1571 int alignment;
1572 char *centering_control = NULL;
1573 char *p,*s;
1574 char *output_string = NULL;
1575 char *name = NULL;
1576 char *value = NULL;
1577 int x, y, angle, len, char_height;
1578 float font_size;
1580 if (o_current->visibility != VISIBLE) {
1581 return;
1584 if (!o_current->text->string) {
1585 return;
1588 fprintf(fp, "gsave\n");
1590 if (toplevel->text_output == VECTOR_FONTS
1591 || (toplevel->print_vector_threshold > 0
1592 && (o_text_num_lines(o_current->text->string)
1593 > toplevel->print_vector_threshold))) {
1594 f_print_objects(toplevel,
1596 o_current->text->prim_objs,
1597 0, 0,
1598 scale, unicode_table);
1599 fprintf(fp, "grestore\n");
1600 return;
1603 if (toplevel->print_color) {
1604 f_print_set_color(fp, o_current->color);
1608 if (o_attrib_get_name_value(o_current->text->string, &name, &value)) {
1609 switch(o_current->show_name_value) {
1610 case(SHOW_NAME_VALUE):
1611 output_string = g_strdup(o_current->text->string);
1612 break;
1614 case(SHOW_NAME):
1615 output_string = g_strdup(name);
1616 break;
1618 case(SHOW_VALUE):
1619 output_string = g_strdup(value);
1620 break;
1622 } else {
1623 output_string = g_strdup(o_current->text->string);
1626 /* Apply alignment map to apply when text is 180 degrees rotated.
1627 * We want the text on the printer to appear upside right, even
1628 * though mathematically it aught to be upside down. To make this
1629 * work, we will reset the angle to 0, when it's equal to 180
1630 * degrees, then apply a transformation to the origin location as if
1631 * the text was rotated about that point. E.g. if the text origin
1632 * was at the lower left, and the text was rotated by 180 degrees,
1633 * it would be as if the origin was at the upper right. The same
1634 * reasoning has been applied to all 8 other text origins.
1635 * MIDDLE_MIDDLE maps to itself.
1637 alignment = o_current->text->alignment;
1638 angle = o_current->text->angle;
1639 if(angle == 180) {
1640 angle = 0; /* reset angle to 0 to make text upright */
1641 switch(alignment) {
1642 case(LOWER_LEFT): alignment = UPPER_RIGHT;
1643 break;
1644 case(MIDDLE_LEFT): alignment = MIDDLE_RIGHT;
1645 break;
1646 case(UPPER_LEFT): alignment = LOWER_RIGHT;
1647 break;
1648 case(LOWER_MIDDLE): alignment = UPPER_MIDDLE;
1649 break;
1650 case(MIDDLE_MIDDLE): alignment = MIDDLE_MIDDLE;
1651 break;
1652 case(UPPER_MIDDLE): alignment = LOWER_MIDDLE;
1653 break;
1654 case(LOWER_RIGHT): alignment = UPPER_LEFT;
1655 break;
1656 case(MIDDLE_RIGHT): alignment = MIDDLE_LEFT;
1657 break;
1658 case(UPPER_RIGHT): alignment = LOWER_LEFT;
1659 break;
1663 /* Create an appropriate control string for the centering. */
1664 switch(alignment) {
1665 /* hcenter rjustify vcenter vjustify */
1666 case(LOWER_LEFT): centering_control = "false false false false";
1667 break;
1668 case(MIDDLE_LEFT): centering_control = "false false true false";
1669 break;
1670 case(UPPER_LEFT): centering_control = "false false false true";
1671 break;
1672 case(LOWER_MIDDLE): centering_control = "true false false false";
1673 break;
1674 case(MIDDLE_MIDDLE): centering_control = "true false true false";
1675 break;
1676 case(UPPER_MIDDLE): centering_control = "true false false true";
1677 break;
1678 case(LOWER_RIGHT): centering_control = "false true false false";
1679 break;
1680 case(MIDDLE_RIGHT): centering_control = "false true true false";
1681 break;
1682 case(UPPER_RIGHT): centering_control = "false true false true";
1683 break;
1686 char_height = o_text_height(o_current, "a");
1687 fprintf(fp,"%s %f [",centering_control,(float)(char_height*LINE_SPACING));
1689 /* split the line at each newline and print them */
1690 p = output_string; /* Current point */
1691 s = output_string; /* Start of the current string */
1692 len = strlen(output_string)+1;
1693 while(len != 0) {
1694 /* Have we reached the end of a line? */
1695 if((*p == '\n') || (*p == '\0')) {
1696 /* Yes, replace the newline with a NULL and output the string */
1697 *p = '\0';
1698 o_text_print_text_string(fp, s, unicode_table);
1699 /* Update output string start for next string */
1700 s = p+1; /* One past the current character. */
1702 p++; /* Advance to next character */
1703 len--; /* Keep track of how many characters left to process */
1706 /* Finish up with the rest of the text print command */
1707 /* Collect pertinent info about the text location */
1708 x = o_current->text->x;
1709 y = o_current->text->y;
1710 font_size = (((float)(o_current->text->size))
1711 * toplevel->postscript_font_scale / 72.0 * 1000.0);
1712 fprintf(fp,"] %d %d %d %f text\n",angle,x,y,font_size);
1715 g_free(output_string);
1716 g_free(name);
1717 g_free(value);
1719 fprintf(fp, "grestore\n");
1723 /*! \brief rotate a text object around a centerpoint
1724 * \par Function Description
1725 * This function rotates a text \a object around the point
1726 * (\a world_centerx, \a world_centery).
1728 * \param [in] world_centerx x-coord of the rotation center
1729 * \param [in] world_centery y-coord of the rotation center
1730 * \param [in] angle The angle to rotate the text object
1731 * \param [in] object The text object
1732 * \note only steps of 90 degrees are allowed for the \a angle
1734 void o_text_rotate_world(int world_centerx, int world_centery,
1735 int angle, OBJECT *object)
1737 int x, y;
1738 int newx, newy;
1740 g_return_if_fail(object != NULL);
1741 g_return_if_fail(object->type == OBJ_TEXT);
1743 object->text->angle = ( object->text->angle + angle ) % 360;
1745 x = object->text->x + (-world_centerx);
1746 y = object->text->y + (-world_centery);
1748 rotate_point_90(x, y, angle, &newx, &newy);
1750 x = newx + (world_centerx);
1751 y = newy + (world_centery);
1753 o_text_translate_world(x-object->text->x, y-object->text->y, object);
1755 o_text_recreate(object);
1759 /*! \brief mirror a text object horizontaly at a centerpoint
1760 * \par Function Description
1761 * This function mirrors a text \a object horizontaly at the point
1762 * (\a world_centerx, \a world_centery).
1764 * \param [in] world_centerx x-coord of the mirror position
1765 * \param [in] world_centery y-coord of the mirror position
1766 * \param [in] object The text object
1768 void o_text_mirror_world(int world_centerx, int world_centery,
1769 OBJECT *object)
1771 int origx, origy;
1772 int x, y;
1774 origx = object->text->x;
1775 origy = object->text->y;
1777 x = origx + (-world_centerx);
1778 y = origy + (-world_centery);
1780 if ((object->text->angle%180)==0) {
1781 switch(object->text->alignment) {
1782 case(LOWER_LEFT):
1783 object->text->alignment=LOWER_RIGHT;
1784 break;
1786 case(MIDDLE_LEFT):
1787 object->text->alignment=MIDDLE_RIGHT;
1788 break;
1790 case(UPPER_LEFT):
1791 object->text->alignment=UPPER_RIGHT;
1792 break;
1794 case(LOWER_RIGHT):
1795 object->text->alignment=LOWER_LEFT;
1796 break;
1798 case(MIDDLE_RIGHT):
1799 object->text->alignment=MIDDLE_LEFT;
1800 break;
1802 case(UPPER_RIGHT):
1803 object->text->alignment=UPPER_LEFT;
1804 break;
1806 default:
1807 break;
1809 } else {
1810 switch(object->text->alignment) {
1811 case(LOWER_LEFT):
1812 object->text->alignment=UPPER_LEFT;
1813 break;
1815 case(UPPER_LEFT):
1816 object->text->alignment=LOWER_LEFT;
1817 break;
1819 case(LOWER_RIGHT):
1820 object->text->alignment=UPPER_RIGHT;
1821 break;
1823 case(UPPER_RIGHT):
1824 object->text->alignment=LOWER_RIGHT;
1825 break;
1827 case(LOWER_MIDDLE):
1828 object->text->alignment=UPPER_MIDDLE;
1829 break;
1831 case(UPPER_MIDDLE):
1832 object->text->alignment=LOWER_MIDDLE;
1833 break;
1835 default:
1836 break;
1840 object->text->x = -x + (world_centerx);
1841 object->text->y = y + (world_centery);
1843 o_text_recreate(object);
1846 /*! \brief Calculates the distance between the given point and the closest
1847 * point on the text.
1849 * This function will calculate the distance to the text regardless
1850 * if the text is visible or not.
1852 * \param [in] text the text of the OBJECT
1853 * \param [in] x The x coordinate of the given point.
1854 * \param [in] y The y coordinate of the given point.
1855 * \return The shortest distance from the object to the point. If the
1856 * distance cannot be calculated, this function returns a really large
1857 * number (G_MAXDOUBLE). With an invalid parameter, this function
1858 * returns G_MAXDOUBLE.
1860 gdouble o_text_shortest_distance(TEXT const *text, gint x, gint y)
1862 gdouble distance;
1863 gdouble shortest_distance = G_MAXDOUBLE;
1864 OBJECT const *temp;
1866 if (text == NULL) {
1867 g_critical("o_text_shortest_distance(): text == NULL\n");
1868 return G_MAXDOUBLE;
1871 temp = text->prim_objs;
1873 if (temp != NULL) {
1874 temp = temp->next;
1877 while (temp != NULL) {
1878 distance = o_shortest_distance(temp, x, y);
1880 shortest_distance = min(shortest_distance, distance);
1882 temp = temp->next;
1885 return shortest_distance;
1888 /*! \brief Set the string displayed by a text object.
1889 * \par Function Description
1890 * Updates the text object with a new text string.
1892 * \param [in] obj The text object.
1893 * \param [in] new_string The new value.
1895 void o_text_set_string(OBJECT *obj, const gchar *new_string)
1897 g_return_if_fail(new_string != NULL);
1898 o_text_take_string(obj, g_strdup(new_string));
1901 void o_text_take_string(OBJECT *obj, gchar *new_string)
1903 g_return_if_fail (obj != NULL);
1904 g_return_if_fail (obj->type == OBJ_TEXT);
1905 g_return_if_fail (obj->text != NULL);
1906 g_return_if_fail (new_string != NULL);
1908 g_free (obj->text->string);
1909 obj->text->string = new_string;
1911 g_signal_emit_by_name(obj, "changed");
1912 if (obj->attached_to) {
1913 g_signal_emit_by_name(obj->attached_to, "attribs-changed");
1917 /*! \brief Get the string displayed by a text object.
1918 * \par Function Description
1919 * Retrieve the text string from a text object. The returned string
1920 * should be treated as constant.
1922 * \param [in] obj The text object.
1923 * \return The text object's string, or NULL on failure.
1925 const gchar *o_text_get_string(OBJECT const *obj)
1927 g_return_val_if_fail (obj != NULL, NULL);
1928 g_return_val_if_fail (obj->type == OBJ_TEXT, NULL);
1929 g_return_val_if_fail (obj->text != NULL, NULL);
1931 return obj->text->string;
1934 /*! \todo Finish function documentation!!!
1935 * \brief
1936 * \par Function Description
1938 * \note
1939 * The object passed in should be the REAL object, NOT any copy in any
1940 * selection list
1942 void o_text_change(OBJECT *object, char const *string, int visibility, int show)
1944 if (object == NULL) {
1945 return;
1948 if (object->type != OBJ_TEXT) {
1949 return;
1952 /* FIXME: Block changed:: until all changes are done. */
1954 /* second change the real object */
1955 o_text_set_string(object, string);
1957 object->visibility = visibility;
1958 object->show_name_value = show;
1959 g_signal_emit_by_name(object, "changed");