1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
5 *
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.
10 *
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.
15 *
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /*! \file o_text_basic.c
22 * \brief functions for the text and fonts
23 *
24 * \par The font definitions
25 *
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.
29 *
30 * All remaining lines are basic graphical lines. They build the
31 * appearance of the character.
32 *
33 * \image html o_text_font_overview.png
34 * \image latex o_text_font_overview.pdf "font overview" width=14cm
35 *
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.
41 *
42 * \par The text definitions
43 *
44 * The text is stored and printed in several different representations.
45 *
46 * In the gEDA files the text is just a string. It is stored unmodified
47 * in <b>OBJECT->text->string</b>.
48 *
49 * If the string is an attribute with an equal sign as delimiter between
50 * an attribute name and an attribute value, then it is possible to
51 * hide some parts of the text. The still visible part of an attribute
52 * is stored in <b>OBJECT->text->disp_string</b>.
53 *
54 * \image html o_text_text_overview.png
55 * \image latex o_text_text_overview.pdf "text overview" width=14cm
56 *
57 * To draw the text in gschem, the string is interpreted and converted
58 * to a list of basic graphical objects. The basic line objects are
59 * collected from the font character objects.
60 */
61
62 #include <config.h>
63 #include <missing.h>
64
65 #include <stdio.h>
66 #include <math.h>
67 #include <sys/stat.h>
68 #ifdef HAVE_STRING_H
69 #include <string.h>
70 #endif
71 #ifdef HAVE_UNISTD_H
72 #include <unistd.h>
73 #endif
74
75 #include "libgeda_priv.h"
76
77 #ifdef HAVE_LIBDMALLOC
78 #include <dmalloc.h>
79 #endif
80
81 /*! \brief Scale factor between legacy gschem font units and postscript points.
82 *
83 * \par Description
84 * gschem fonts are nominally specified in points, however there is a
85 * difference in how the specified font size corresponds to the metrics of
86 * the font when compared to typical typographic usage.
87 *
88 * The following factor was impirically determined to approximately match the
89 * cap-height between the legacy gschem font, and fonts rendered using pango.
90 */
91 #define GEDA_FONT_FACTOR 1.3
92
93 /*! \brief Scale factor font height and line-spacing (for print only)
94 *
95 * \par Description
96 * Specifies the scale factor between the nominal font size and the inter-
97 * line spacing used to render it when printing.
98 */
99 #define PRINT_LINE_SPACING 1.12
100
101 /*! Size of a tab in characters */
102 int tab_in_chars = 8;
103
104 /*! \brief update the visible part of a string
105 * \par Function Description
106 * If a string is an attribute, then it is possible to hide
107 * the name or the value part of the attribute string.
108 * This functions updates the text->disp_string according
109 * to the object->show_name_value settings
110 *
111 * \param [in] object The OBJECT to update
112 */
update_disp_string(OBJECT * object)113 static void update_disp_string (OBJECT *object)
114 {
115 char *name = NULL;
116 char *value = NULL;
117 TEXT *text = object->text;
118
119 g_free (text->disp_string);
120
121 if (o_attrib_get_name_value (object, &name, &value)) {
122 switch (object->show_name_value) {
123 case (SHOW_NAME_VALUE):
124 text->disp_string = g_strdup (text->string);
125 break;
126
127 case (SHOW_NAME):
128 if (name[0] != '\0') {
129 text->disp_string = g_strdup (name);
130 } else {
131 g_critical ("Got an improper attribute: %s\n",
132 text->string);
133 text->disp_string = g_strdup ("invalid");
134 }
135 break;
136
137 case (SHOW_VALUE):
138 if (value[0] != '\0') {
139 text->disp_string = g_strdup(value);
140 } else {
141 g_critical ("Got an improper attribute: %s\n",
142 text->string);
143 text->disp_string = g_strdup ("invalid");
144 }
145 break;
146 }
147 /* free the strings allocated by o_attrib_get_name_value */
148 g_free(name);
149 g_free(value);
150 } else {
151 text->disp_string = g_strdup (text->string);
152 }
153 }
154
155 /*! \brief calculate and return the boundaries of a text object
156 * \par Function Description
157 * This function calculates the object boudaries of a text \a object.
158 *
159 * \param [in] toplevel The TOPLEVEL object.
160 * \param [in] o_current a text object
161 * \param [out] left the left world coord
162 * \param [out] top the top world coord
163 * \param [out] right the right world coord
164 * \param [out] bottom the bottom world coord
165 */
world_get_text_bounds(TOPLEVEL * toplevel,OBJECT * o_current,int * left,int * top,int * right,int * bottom)166 int world_get_text_bounds(TOPLEVEL *toplevel, OBJECT *o_current, int *left,
167 int *top, int *right, int *bottom)
168 {
169 if (toplevel->rendered_text_bounds_func != NULL) {
170 return
171 toplevel->rendered_text_bounds_func (toplevel->rendered_text_bounds_data,
172 o_current,
173 left, top, right, bottom);
174 }
175
176 return FALSE;
177 }
178
179 /*! \brief get the position of a text object
180 * \par Function Description
181 * This function gets the position of the base point of a text object.
182 *
183 * \param [in] toplevel The toplevel environment.
184 * \param [out] x pointer to the x-position
185 * \param [out] y pointer to the y-position
186 * \param [in] object The object to get the position.
187 * \return TRUE if successfully determined the position, FALSE otherwise
188 */
o_text_get_position(TOPLEVEL * toplevel,gint * x,gint * y,OBJECT * object)189 gboolean o_text_get_position (TOPLEVEL *toplevel, gint *x, gint *y,
190 OBJECT *object)
191 {
192 *x = object->text->x;
193 *y = object->text->y;
194 return TRUE;
195 }
196
197
198 /*! \brief count the lines of a text string
199 * \par Function Description
200 * This function just counts the number of lines that are
201 * in the \a string.
202
203 * \param [in] string text string to count the lines
204 * \return the number of lines
205 */
o_text_num_lines(const char * string)206 int o_text_num_lines(const char *string)
207 {
208 int line_count = 0;
209 const gchar *aux;
210 gunichar current_char;
211
212 if (string == NULL) {
213 return 0;
214 }
215
216 /* if it's not null, then we have at least one line */
217 line_count++;
218 /* Count how many \n are in the string */
219 aux = string;
220 while (aux && ((gunichar) (*aux) != 0) ) {
221 current_char = g_utf8_get_char_validated(aux, -1);
222 if (current_char == '\n')
223 line_count++;
224 aux = g_utf8_find_next_char(aux, NULL);
225 }
226
227 return (line_count);
228 }
229
230
231 /*! \brief Creates a text OBJECT and the graphical objects representing it
232 * \par Function Description
233 * Create an OBJECT of type OBJ_TEXT.
234 *
235 * \param [in] toplevel The TOPLEVEL object.
236 * \param [in] type OBJ_TEXT (TODO: why bother)
237 * \param [in] color The color of the text.
238 * \param [in] x World x coord of text.
239 * \param [in] y World y coord of text.
240 * \param [in] alignment How text bounding box aligns on (x, y).
241 * \param [in] angle Angle at which text will appear.
242 * \param [in] string The text (TODO: can be char const *)!
243 * \param [in] size Text size.
244 * \param [in] visibility VISIBLE or INVISIBLE.
245 * \param [in] show_name_value SHOW_NAME_VALUE or friends.
246 * \return Pointer to text OBJECT.
247 *
248 * \note
249 * Caller is responsible for string; this function allocates its own copy.
250 */
o_text_new(TOPLEVEL * toplevel,char type,int color,int x,int y,int alignment,int angle,const char * string,int size,int visibility,int show_name_value)251 OBJECT *o_text_new(TOPLEVEL *toplevel,
252 char type, int color, int x, int y, int alignment,
253 int angle, const char *string, int size,
254 int visibility, int show_name_value)
255 {
256 OBJECT *new_node=NULL;
257 TEXT *text;
258
259 if (string == NULL) {
260 return(NULL);
261 }
262
263 new_node = s_basic_new_object(type, "text");
264
265 text = (TEXT *) g_malloc(sizeof(TEXT));
266
267 text->string = g_strdup (string);
268 text->disp_string = NULL; /* We'll fix this up later */
269 text->length = strlen(string);
270 text->size = size;
271 text->alignment = alignment;
272 text->x = x;
273 text->y = y;
274 text->angle = angle;
275
276 new_node->text = text;
277
278 new_node->color = color;
279 o_set_visibility (toplevel, new_node, visibility);
280 new_node->show_name_value = show_name_value;
281
282 update_disp_string (new_node);
283
284 /* Update bounding box */
285 new_node->w_bounds_valid = FALSE;
286
287 return new_node;
288 }
289
290 /*! \brief update the visual boundaries of the text object
291 * \par Function Description
292 * This function updates the boundaries of the object \a o_current.
293 *
294 * \param [in] toplevel The TOPLEVEL object
295 * \param [in] o_current The OBJECT to update
296 */
o_text_recalc(TOPLEVEL * toplevel,OBJECT * o_current)297 void o_text_recalc(TOPLEVEL *toplevel, OBJECT *o_current)
298 {
299 int left, right, top, bottom;
300
301 if ((!o_is_visible (toplevel, o_current)) &&
302 (!toplevel->show_hidden_text)) {
303 return;
304 }
305
306 if ( !world_get_text_bounds(toplevel, o_current, &left, &top, &right, &bottom) )
307 return;
308
309 o_current->w_left = left;
310 o_current->w_top = top;
311 o_current->w_right = right;
312 o_current->w_bottom = bottom;
313 o_current->w_bounds_valid = TRUE;
314 }
315
316 /*! \brief read a text object from a char buffer
317 * \par Function Description
318 * This function reads a text object from the textbuffer \a tb and
319 * the text starting with the line \a firstline.
320 * If the line object was read successfully, a new object is
321 * create and appended to the \a object_list.
322 *
323 * \param [in] toplevel The TOPLEVEL object
324 * \param [in] first_line the first line of the text
325 * \param [in] tb a text buffer (usually a line of a schematic file)
326 * \param [in] release_ver The release number gEDA
327 * \param [in] fileformat_ver a integer value of the file format
328 * \return The object list, or NULL on error.
329 */
o_text_read(TOPLEVEL * toplevel,const char * first_line,TextBuffer * tb,unsigned int release_ver,unsigned int fileformat_ver,GError ** err)330 OBJECT *o_text_read (TOPLEVEL *toplevel,
331 const char *first_line,
332 TextBuffer *tb,
333 unsigned int release_ver,
334 unsigned int fileformat_ver,
335 GError **err)
336 {
337 OBJECT *new_obj;
338 char type;
339 int x, y;
340 int color;
341 int size;
342 int visibility;
343 int show_name_value;
344 int angle;
345 int alignment;
346 int num_lines = 0;
347 int i;
348 char* string = NULL;
349 GString *textstr;
350
351 if (fileformat_ver >= 1) {
352 if (sscanf(first_line, "%c %d %d %d %d %d %d %d %d %d\n", &type, &x, &y,
353 &color, &size,
354 &visibility, &show_name_value,
355 &angle, &alignment, &num_lines) != 10) {
356 g_set_error(err, EDA_ERROR, EDA_ERROR_PARSE, _("Failed to parse text object"));
357 return NULL;
358 }
359 } else if (release_ver < VERSION_20000220) {
360 /* yes, above less than (not less than and equal) is correct. The format */
361 /* change occurred in 20000220 */
362 if (sscanf(first_line, "%c %d %d %d %d %d %d %d\n", &type, &x, &y,
363 &color, &size,
364 &visibility, &show_name_value,
365 &angle) != 8) {
366 g_set_error(err, EDA_ERROR, EDA_ERROR_PARSE, _("Failed to parse text object"));
367 return NULL;
368 }
369 alignment = LOWER_LEFT; /* older versions didn't have this */
370 num_lines = 1; /* only support a single line */
371 } else {
372 if (sscanf(first_line, "%c %d %d %d %d %d %d %d %d\n", &type, &x, &y,
373 &color, &size,
374 &visibility, &show_name_value,
375 &angle, &alignment) != 9) {
376 g_set_error (err, EDA_ERROR, EDA_ERROR_PARSE, _("Failed to parse text object"));
377 return NULL;
378 }
379 num_lines = 1; /* only support a single line */
380 }
381
382 if (size == 0) {
383 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);
384 }
385
386 switch(angle) {
387
388 case(0):
389 case(90):
390 case(180):
391 case(270):
392 break;
393
394 default:
395 s_log_message(_("Found an unsupported text angle [ %c %d %d %d %d %d %d %d %d ]\n"),
396 type, x, y, color, size, visibility, show_name_value, angle, alignment);
397 s_log_message(_("Setting angle to 0\n"));
398 angle=0;
399 break;
400
401 }
402
403 switch(alignment) {
404 case(LOWER_LEFT):
405 case(MIDDLE_LEFT):
406 case(UPPER_LEFT):
407 case(LOWER_MIDDLE):
408 case(MIDDLE_MIDDLE):
409 case(UPPER_MIDDLE):
410 case(LOWER_RIGHT):
411 case(MIDDLE_RIGHT):
412 case(UPPER_RIGHT):
413
414 break;
415
416 default:
417 s_log_message(_("Found an unsupported text alignment [ %c %d %d %d %d %d %d %d %d ]\n"),
418 type, x, y, color, size, visibility, show_name_value, angle, alignment);
419 s_log_message(_("Setting alignment to LOWER_LEFT\n"));
420 alignment = LOWER_LEFT;
421 break;
422 }
423
424 if (color < 0 || color > MAX_COLORS) {
425 s_log_message(_("Found an invalid color [ %s ]\n"), first_line);
426 s_log_message(_("Setting color to default color\n"));
427 color = DEFAULT_COLOR;
428 }
429
430 g_assert(num_lines && num_lines > 0);
431
432 textstr = g_string_new ("");
433 for (i = 0; i < num_lines; i++) {
434 const gchar *line;
435
436 line = s_textbuffer_next_line (tb);
437
438 if (line == NULL) {
439 g_string_free (textstr, TRUE);
440 g_set_error(err, EDA_ERROR, EDA_ERROR_PARSE, _("Unexpected end-of-file after %d lines"), i);
441 return NULL;
442 }
443
444 textstr = g_string_append (textstr, line);
445 }
446 /* retrieve the character string from the GString */
447 string = g_string_free (textstr, FALSE);
448
449 string = remove_last_nl(string);
450
451 /* convert the character string to UTF-8 if necessary */
452 if (!g_utf8_validate (string, -1, NULL)) {
453 /* if it is not utf-8, it is ISO_8859-15 */
454 gchar *tmp = g_convert (string, strlen (string),
455 "UTF-8", "ISO_8859-15",
456 NULL, NULL, NULL);
457 if (tmp == NULL) {
458 fprintf (stderr, "Failed to convert text string to UTF-8: %s.\n",
459 string);
460 } else {
461 /* successfully converted string, now use tmp as string */
462 g_free (string);
463 string = tmp;
464 }
465 }
466
467 new_obj = o_text_new(toplevel, type, color, x, y,
468 alignment, angle, string,
469 size, visibility, show_name_value);
470 g_free(string);
471
472 return new_obj;
473 }
474
475
476 /*! \brief Create a string representation of the text object
477 * \par Function Description
478 * This function takes a text \a object and return a string
479 * according to the file format definition.
480 *
481 * \param [in] toplevel a TOPLEVEL structure
482 * \param [in] object a text OBJECT
483 * \return the string representation of the text OBJECT
484 */
o_text_save(TOPLEVEL * toplevel,OBJECT * object)485 char *o_text_save(TOPLEVEL *toplevel, OBJECT *object)
486 {
487 int x, y;
488 int size;
489 char *string;
490 char *buf;
491 int num_lines;
492
493 x = object->text->x;
494 y = object->text->y;
495
496 string = object->text->string;
497 size = object->text->size;
498
499 /* string can have multiple lines (seperated by \n's) */
500 num_lines = o_text_num_lines(string);
501
502 buf = g_strdup_printf ("%c %d %d %d %d %d %d %d %d %d\n%s", object->type,
503 x, y, object->color, size,
504 o_is_visible (toplevel, object) ? VISIBLE : INVISIBLE,
505 object->show_name_value, object->text->angle,
506 object->text->alignment, num_lines, string);
507
508 return(buf);
509 }
510
511 /*! \brief recreate the graphics of a text object
512 * \par Function Description
513 * This function updates the underlying primary of the text object
514 * \a o_current.
515 *
516 * \param toplevel The TOPLEVEL object
517 * \param o_current The text object to update
518 */
o_text_recreate(TOPLEVEL * toplevel,OBJECT * o_current)519 void o_text_recreate(TOPLEVEL *toplevel, OBJECT *o_current)
520 {
521 o_emit_pre_change_notify (toplevel, o_current);
522 update_disp_string (o_current);
523 o_current->w_bounds_valid = FALSE;
524 o_emit_change_notify (toplevel, o_current);
525 }
526
527 /*! \brief move a text object
528 * \par Function Description
529 * This function changes the position of a text object \a o_current.
530 *
531 * \param [in] toplevel The TOPLEVEL object
532 * \param [in] dx The x-distance to move the object
533 * \param [in] dy The y-distance to move the object
534 * \param [in] o_current The text OBJECT to be moved
535 */
o_text_translate_world(TOPLEVEL * toplevel,int dx,int dy,OBJECT * o_current)536 void o_text_translate_world(TOPLEVEL *toplevel,
537 int dx, int dy, OBJECT *o_current)
538 {
539 o_current->text->x = o_current->text->x + dx;
540 o_current->text->y = o_current->text->y + dy;
541
542 /* Update bounding box */
543 o_current->w_bounds_valid = FALSE;
544 }
545
546 /*! \brief create a copy of a text object
547 * \par Function Description
548 * This function creates a copy of the text object \a o_current.
549 *
550 * \param [in] toplevel The TOPLEVEL object
551 * \param [in] o_current The object that is copied
552 * \return a new text object
553 */
o_text_copy(TOPLEVEL * toplevel,OBJECT * o_current)554 OBJECT *o_text_copy(TOPLEVEL *toplevel, OBJECT *o_current)
555 {
556 OBJECT *new_obj;
557
558 new_obj = o_text_new (toplevel, OBJ_TEXT, o_current->color,
559 o_current->text->x, o_current->text->y,
560 o_current->text->alignment,
561 o_current->text->angle,
562 o_current->text->string,
563 o_current->text->size,
564 o_is_visible (toplevel, o_current) ? VISIBLE : INVISIBLE,
565 o_current->show_name_value);
566
567 return new_obj;
568 }
569
570
571 /*! \brief write a text string to a postscript file
572 * \par Function Description
573 * This function writes the single \a string into the postscript file \a fp.
574 *
575 * \param [in] fp pointer to a FILE structure
576 * \param [in] string The string to print
577 * \param [in] unicode_count Number of items in the unicode table
578 * \param [in] unicode_table Table of unicode items
579 *
580 * \todo investigate whether the TAB character is handled correctly
581 */
o_text_print_text_string(FILE * fp,char * string,int unicode_count,gunichar * unicode_table)582 void o_text_print_text_string(FILE *fp, char *string, int unicode_count,
583 gunichar *unicode_table)
584 {
585 int j;
586 gchar *aux;
587 gunichar current_char, c;
588
589 if (!string)
590 {
591 return;
592 }
593
594 aux = string;
595
596 fprintf(fp, "(");
597
598 while (aux && ((gunichar) (*aux) != 0)) {
599 current_char = g_utf8_get_char_validated(aux, -1);
600 if (current_char == '(' || current_char == ')' || current_char == '\\') {
601 fprintf(fp, "\\");
602 }
603
604 c = current_char;
605 if (c >= 128) {
606 current_char = '?';
607 if (unicode_count) {
608 for (j = 0; j < unicode_count; j++)
609 if (c == unicode_table[j]) {
610 current_char = j + 128;
611 break;
612 }
613 }
614 }
615
616
617 if (current_char == '\t') {
618 /* Output eight spaces instead of the tab character */
619 fprintf(fp, " ");
620 } else {
621 fprintf(fp, "%c", current_char);
622 }
623
624 aux = g_utf8_find_next_char(aux, NULL);
625 }
626
627 fprintf(fp,") ");
628 }
629
630
631 /*! \brief print a text object into a postscript file
632 * \par Function Description
633 * This function writes the postscript representation of the text object
634 * \a o_current into the the file \a fp.
635 * \param [in] toplevel The TOPLEVEL object
636 * \param [in] fp pointer to a FILE structure
637 * \param [in] o_current The OBJECT to print
638 * \param [in] origin_x x-coord of the postscript origin
639 * \param [in] origin_y y-coord of the postscript origin
640 * \param [in] unicode_count Number of items in the unicode table
641 * \param [in] unicode_table Table of unicode items
642 */
o_text_print(TOPLEVEL * toplevel,FILE * fp,OBJECT * o_current,int origin_x,int origin_y,int unicode_count,gunichar * unicode_table)643 void o_text_print(TOPLEVEL *toplevel, FILE *fp, OBJECT *o_current,
644 int origin_x, int origin_y,
645 int unicode_count, gunichar *unicode_table)
646 {
647 int alignment;
648 char *centering_control = NULL;
649 char *p,*s;
650 char *output_string = NULL;
651 char *name = NULL;
652 char *value = NULL;
653 int x, y, angle, len;
654 float font_size;
655
656
657 if (!o_current->text->string) {
658 return;
659 }
660
661 f_print_set_color(toplevel, fp, o_current->color);
662
663
664 if (o_attrib_get_name_value (o_current, &name, &value)) {
665 switch(o_current->show_name_value) {
666 case(SHOW_NAME_VALUE):
667 output_string = g_strdup(o_current->text->string);
668 break;
669
670 case(SHOW_NAME):
671 if (name[0] != '\0') {
672 output_string = g_strdup(name);
673 } else {
674 fprintf(stderr,
675 "Got an improper attribute: %s\n",
676 o_current->text->string);
677 output_string = g_strdup("invalid");
678 }
679 break;
680
681 case(SHOW_VALUE):
682 if (value[0] != '\0') {
683 output_string = g_strdup(value);
684 } else {
685 /* you probably can remove this now... */
686 /* since improper attributes will never get here */
687 fprintf(stderr,
688 "Got an improper attribute: %s\n",
689 o_current->text->string);
690 output_string = g_strdup("invalid");
691 }
692 break;
693
694 default:
695 g_return_if_reached ();
696
697 }
698 } else {
699 output_string = g_strdup(o_current->text->string);
700 }
701
702 /* Apply alignment map to apply when text is 180 degrees rotated.
703 * We want the text on the printer to appear upside right, even
704 * though mathematically it aught to be upside down. To make this
705 * work, we will reset the angle to 0, when it's equal to 180
706 * degrees, then apply a transformation to the origin location as if
707 * the text was rotated about that point. E.g. if the text origin
708 * was at the lower left, and the text was rotated by 180 degrees,
709 * it would be as if the origin was at the upper right. The same
710 * reasoning has been applied to all 8 other text origins.
711 * MIDDLE_MIDDLE maps to itself.
712 */
713 alignment = o_current->text->alignment;
714 angle = o_current->text->angle;
715 if(angle == 180) {
716 angle = 0; /* reset angle to 0 to make text upright */
717 switch(alignment) {
718 case(LOWER_LEFT): alignment = UPPER_RIGHT;
719 break;
720 case(MIDDLE_LEFT): alignment = MIDDLE_RIGHT;
721 break;
722 case(UPPER_LEFT): alignment = LOWER_RIGHT;
723 break;
724 case(LOWER_MIDDLE): alignment = UPPER_MIDDLE;
725 break;
726 case(MIDDLE_MIDDLE): alignment = MIDDLE_MIDDLE;
727 break;
728 case(UPPER_MIDDLE): alignment = LOWER_MIDDLE;
729 break;
730 case(LOWER_RIGHT): alignment = UPPER_LEFT;
731 break;
732 case(MIDDLE_RIGHT): alignment = MIDDLE_LEFT;
733 break;
734 case(UPPER_RIGHT): alignment = LOWER_LEFT;
735 break;
736 }
737 }
738
739 /* Create an appropriate control string for the centering. */
740 switch(alignment) {
741 /* hcenter rjustify vcenter vjustify */
742 case(LOWER_LEFT): centering_control = "false false false false";
743 break;
744 case(MIDDLE_LEFT): centering_control = "false false true false";
745 break;
746 case(UPPER_LEFT): centering_control = "false false false true";
747 break;
748 case(LOWER_MIDDLE): centering_control = "true false false false";
749 break;
750 case(MIDDLE_MIDDLE): centering_control = "true false true false";
751 break;
752 case(UPPER_MIDDLE): centering_control = "true false false true";
753 break;
754 case(LOWER_RIGHT): centering_control = "false true false false";
755 break;
756 case(MIDDLE_RIGHT): centering_control = "false true true false";
757 break;
758 case(UPPER_RIGHT): centering_control = "false true false true";
759 break;
760 }
761
762 font_size = o_text_get_font_size_in_points (toplevel, o_current)
763 / 72.0 * 1000.0;
764 fprintf(fp,"%s %f [",centering_control, font_size * PRINT_LINE_SPACING);
765
766 /* split the line at each newline and print them */
767 p = output_string; /* Current point */
768 s = output_string; /* Start of the current string */
769 len = strlen(output_string)+1;
770 while(len != 0) {
771 /* Have we reached the end of a line? */
772 if((*p == '\n') || (*p == '\0')) {
773 /* Yes, replace the newline with a NULL and output the string */
774 *p = '\0';
775 o_text_print_text_string(fp,s,unicode_count,unicode_table);
776 /* Update output string start for next string */
777 s = p+1; /* One past the current character. */
778 }
779 p++; /* Advance to next character */
780 len--; /* Keep track of how many characters left to process */
781 }
782
783 /* Finish up with the rest of the text print command */
784 /* Collect pertinent info about the text location */
785 x = o_current->text->x;
786 y = o_current->text->y;
787 fprintf(fp,"] %d %d %d %f text\n",angle,x,y,font_size);
788
789
790 g_free(output_string);
791 g_free(name);
792 g_free(value);
793 }
794
795
796 /*! \brief rotate a text object around a centerpoint
797 * \par Function Description
798 * This function rotates a text \a object around the point
799 * (\a world_centerx, \a world_centery).
800 *
801 * \param [in] toplevel The TOPLEVEL object
802 * \param [in] world_centerx x-coord of the rotation center
803 * \param [in] world_centery y-coord of the rotation center
804 * \param [in] angle The angle to rotate the text object
805 * \param [in] object The text object
806 * \note only steps of 90 degrees are allowed for the \a angle
807 */
o_text_rotate_world(TOPLEVEL * toplevel,int world_centerx,int world_centery,int angle,OBJECT * object)808 void o_text_rotate_world(TOPLEVEL *toplevel,
809 int world_centerx, int world_centery,
810 int angle, OBJECT *object)
811 {
812 int x, y;
813 int newx, newy;
814
815 g_return_if_fail(object != NULL);
816 g_return_if_fail(object->type == OBJ_TEXT);
817
818 object->text->angle = ( object->text->angle + angle ) % 360;
819
820 x = object->text->x + (-world_centerx);
821 y = object->text->y + (-world_centery);
822
823 rotate_point_90(x, y, angle, &newx, &newy);
824
825 x = newx + (world_centerx);
826 y = newy + (world_centery);
827
828 o_text_translate_world(toplevel, x-object->text->x, y-object->text->y, object);
829
830 o_text_recreate(toplevel, object);
831 }
832
833
834 /*! \brief mirror a text object horizontaly at a centerpoint
835 * \par Function Description
836 * This function mirrors a text \a object horizontaly at the point
837 * (\a world_centerx, \a world_centery).
838 *
839 * \param [in] toplevel The TOPLEVEL object
840 * \param [in] world_centerx x-coord of the mirror position
841 * \param [in] world_centery y-coord of the mirror position
842 * \param [in] object The text object
843 */
o_text_mirror_world(TOPLEVEL * toplevel,int world_centerx,int world_centery,OBJECT * object)844 void o_text_mirror_world(TOPLEVEL *toplevel,
845 int world_centerx, int world_centery,
846 OBJECT *object)
847 {
848 int origx, origy;
849 int x, y;
850
851 origx = object->text->x;
852 origy = object->text->y;
853
854 x = origx + (-world_centerx);
855 y = origy + (-world_centery);
856
857 if ((object->text->angle%180)==0) {
858 switch(object->text->alignment) {
859 case(LOWER_LEFT):
860 object->text->alignment=LOWER_RIGHT;
861 break;
862
863 case(MIDDLE_LEFT):
864 object->text->alignment=MIDDLE_RIGHT;
865 break;
866
867 case(UPPER_LEFT):
868 object->text->alignment=UPPER_RIGHT;
869 break;
870
871 case(LOWER_RIGHT):
872 object->text->alignment=LOWER_LEFT;
873 break;
874
875 case(MIDDLE_RIGHT):
876 object->text->alignment=MIDDLE_LEFT;
877 break;
878
879 case(UPPER_RIGHT):
880 object->text->alignment=UPPER_LEFT;
881 break;
882
883 default:
884 break;
885 }
886 } else {
887 switch(object->text->alignment) {
888 case(LOWER_LEFT):
889 object->text->alignment=UPPER_LEFT;
890 break;
891
892 case(UPPER_LEFT):
893 object->text->alignment=LOWER_LEFT;
894 break;
895
896 case(LOWER_RIGHT):
897 object->text->alignment=UPPER_RIGHT;
898 break;
899
900 case(UPPER_RIGHT):
901 object->text->alignment=LOWER_RIGHT;
902 break;
903
904 case(LOWER_MIDDLE):
905 object->text->alignment=UPPER_MIDDLE;
906 break;
907
908 case(UPPER_MIDDLE):
909 object->text->alignment=LOWER_MIDDLE;
910 break;
911
912 default:
913 break;
914 }
915 }
916
917 object->text->x = -x + (world_centerx);
918 object->text->y = y + (world_centery);
919
920 o_text_recreate(toplevel, object);
921 }
922
923 /*! \brief Calculates the distance between the given point and the closest
924 * point on the text.
925 *
926 * This function will calculate the distance to the text regardless
927 * if the text is visible or not.
928 *
929 * \param [in] object The text OBJECT.
930 * \param [in] x The x coordinate of the given point.
931 * \param [in] y The y coordinate of the given point.
932 * \param [in] force_solid If true, force treating the object as solid.
933 * \return The shortest distance from the object to the point. If the
934 * distance cannot be calculated, this function returns a really large
935 * number (G_MAXDOUBLE). With an invalid parameter, this funciton
936 * returns G_MAXDOUBLE.
937 */
o_text_shortest_distance(OBJECT * object,int x,int y,int force_solid)938 double o_text_shortest_distance (OBJECT *object, int x, int y, int force_solid)
939 {
940 double dx, dy;
941
942 g_return_val_if_fail (object->text != NULL, G_MAXDOUBLE);
943
944 dx = min (x - object->w_left, object->w_right - x);
945 dy = min (y - object->w_top, object->w_bottom - y);
946
947 dx = min (dx, 0);
948 dy = min (dy, 0);
949
950 return sqrt ((dx * dx) + (dy * dy));
951 }
952
953 /*! \brief Set the string displayed by a text object.
954 * \par Function Description
955 * Updates the text object with a new text string.
956 *
957 * \param [in] toplevel The TOPLEVEL object.
958 * \param [in] obj The text object.
959 * \param [in] new_string The new value.
960 */
o_text_set_string(TOPLEVEL * toplevel,OBJECT * obj,const gchar * new_string)961 void o_text_set_string (TOPLEVEL *toplevel, OBJECT *obj,
962 const gchar *new_string)
963 {
964 g_return_if_fail (toplevel != NULL);
965 g_return_if_fail (obj != NULL);
966 g_return_if_fail (obj->type == OBJ_TEXT);
967 g_return_if_fail (obj->text != NULL);
968 g_return_if_fail (new_string != NULL);
969
970 g_free (obj->text->string);
971 obj->text->string = g_strdup (new_string);
972
973 o_text_recreate (toplevel, obj);
974
975 if (obj->attached_to != NULL)
976 o_attrib_emit_attribs_changed (toplevel, obj->attached_to);
977 }
978
979
980
981 /*! \brief Get the string displayed by a text object.
982 * \par Function Description
983 * Retrieve the text string from a text object. The returned string
984 * should be treated as constant.
985 *
986 * \param [in] toplevel The TOPLEVEL object.
987 * \param [in] obj The text object.
988 * \return The text object's string, or NULL on failure.
989 */
o_text_get_string(TOPLEVEL * toplevel,OBJECT * obj)990 const gchar *o_text_get_string (TOPLEVEL *toplevel, OBJECT *obj)
991 {
992 g_return_val_if_fail (toplevel != NULL, NULL);
993 g_return_val_if_fail (obj != NULL, NULL);
994 g_return_val_if_fail (obj->type == OBJ_TEXT, NULL);
995 g_return_val_if_fail (obj->text != NULL, NULL);
996
997 return obj->text->string;
998 }
999
1000 /*! \brief Set the font-renderer-specific bounds function.
1001 * \par Function Description
1002 * Set the function to be used to calculate text bounds for a given
1003 * #TOPLEVEL.
1004 *
1005 * \param [in] toplevel The TOPLEVEL object
1006 * \param [in] func Function to use.
1007 * \param [in] user_data User data to be passed to the function.
1008 */
o_text_set_rendered_bounds_func(TOPLEVEL * toplevel,RenderedBoundsFunc func,void * user_data)1009 void o_text_set_rendered_bounds_func (TOPLEVEL *toplevel,
1010 RenderedBoundsFunc func,
1011 void *user_data) {
1012 toplevel->rendered_text_bounds_func = func;
1013 toplevel->rendered_text_bounds_data = user_data;
1014 }
1015
1016
1017 /*! \brief Return font size of a text object in postscript points.
1018 *
1019 * \par Description
1020 * gEDA fonts are specified in a non-standard unit. This
1021 * function applies an appopriate scaling to return the
1022 * font size in postscript points.
1023 *
1024 * \param [in] toplevel The TOPLEVEL object
1025 * \param [in] object The text OBJECT whos font size to return
1026 * \return The font size converted to postscript points.
1027 */
o_text_get_font_size_in_points(TOPLEVEL * toplevel,OBJECT * object)1028 double o_text_get_font_size_in_points (TOPLEVEL *toplevel, OBJECT *object)
1029 {
1030 g_return_val_if_fail (object->type == OBJ_TEXT, 0.);
1031
1032 return object->text->size * GEDA_FONT_FACTOR;
1033 }
1034