%{ /* * This is a plug-in for GIMP. * * Generates clickable image maps. * * Copyright (C) 1998-2005 Maurits Rijk lpeek.mrijk@consunet.nl * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include "imap_circle.h" #include "imap_file.h" #include "imap_main.h" #include "imap_polygon.h" #include "imap_rectangle.h" #include "imap_string.h" extern int csim_lex(void); extern int csim_restart(FILE *csim_in); static void csim_error(char* s); static gchar * unescape_text(gchar *input); static enum {UNDEFINED, RECTANGLE, CIRCLE, POLYGON} current_type; static Object_t *current_object; static MapInfo_t *_map_info; %} %union { int val; double value; char *id; } %token IMG SRC WIDTH HEIGHT BORDER USEMAP %token START_MAP END_MAP NAME AREA SHAPE COORDS ALT HREF NOHREF %token TARGET ONMOUSEOVER ONMOUSEOUT ONFOCUS ONBLUR %token AUTHOR DESCRIPTION BEGIN_COMMENT END_COMMENT %token FLOAT %token STRING %type integer_value %% csim_file : image start_map comment_lines area_list end_map ; image : '<' IMG SRC '=' STRING image_tags xhtml_close { g_strreplace(&_map_info->image_name, $5); g_free ($5); } ; image_tags : /* Empty */ | image_tags image_tag ; image_tag : image_width | image_height | BORDER '=' integer_value {} | USEMAP '=' STRING { g_free ($3); } | ALT '=' STRING { g_free ($3); } ; image_width : WIDTH '=' integer_value { _map_info->old_image_width = $3; } ; image_height : HEIGHT '=' integer_value { _map_info->old_image_height = $3; } ; integer_value : FLOAT { $$ = (gint) $1; } | STRING { $$ = (gint) g_ascii_strtod ($1, NULL); g_free ($1); } ; start_map : '<' START_MAP NAME '=' STRING '>' { g_strreplace(&_map_info->title, $5); g_free ($5); } ; comment_lines : /* empty */ | comment_lines comment_line ; comment_line : author_line | description_line | real_comment ; real_comment : BEGIN_COMMENT STRING END_COMMENT { g_free ($2); } ; author_line : AUTHOR STRING END_COMMENT { g_strreplace(&_map_info->author, $2); g_free ($2); } ; description_line: DESCRIPTION STRING END_COMMENT { gchar *description; description = g_strconcat(_map_info->description, $2, "\n", NULL); g_strreplace(&_map_info->description, description); g_free ($2); } ; area_list : /* empty */ | area_list area ; area : '<' AREA tag_list xhtml_close { if (current_type != UNDEFINED) add_shape(current_object); } ; xhtml_close : '>' | '/' '>' ; tag_list : /* Empty */ | tag_list tag ; tag : shape_tag | coords_tag | href_tag | nohref_tag | alt_tag | target_tag | onmouseover_tag | onmouseout_tag | onfocus_tag | onblur_tag ; shape_tag : SHAPE '=' STRING { if (!g_ascii_strcasecmp($3, "RECT")) { current_object = create_rectangle(0, 0, 0, 0); current_type = RECTANGLE; } else if (!g_ascii_strcasecmp($3, "CIRCLE")) { current_object = create_circle(0, 0, 0); current_type = CIRCLE; } else if (!g_ascii_strcasecmp($3, "POLY")) { current_object = create_polygon(NULL); current_type = POLYGON; } else if (!g_ascii_strcasecmp($3, "DEFAULT")) { current_type = UNDEFINED; } g_free ($3); } ; coords_tag : COORDS '=' STRING { char *p; if (current_type == RECTANGLE) { Rectangle_t *rectangle; rectangle = ObjectToRectangle(current_object); p = strtok($3, ","); rectangle->x = atoi(p); p = strtok(NULL, ","); rectangle->y = atoi(p); p = strtok(NULL, ","); rectangle->width = atoi(p) - rectangle->x; p = strtok(NULL, ","); rectangle->height = atoi(p) - rectangle->y; } else if (current_type == CIRCLE) { Circle_t *circle; circle = ObjectToCircle(current_object); p = strtok($3, ","); circle->x = atoi(p); p = strtok(NULL, ","); circle->y = atoi(p); p = strtok(NULL, ","); circle->r = atoi(p); } else if (current_type == POLYGON) { Polygon_t *polygon = ObjectToPolygon(current_object); GList *points; GdkPoint *point, *first; gint x, y; p = strtok($3, ","); x = atoi(p); p = strtok(NULL, ","); y = atoi(p); point = new_point(x, y); points = g_list_append(NULL, (gpointer) point); while(1) { p = strtok(NULL, ","); if (!p) break; x = atoi(p); p = strtok(NULL, ","); y = atoi(p); point = new_point(x, y); points = g_list_append(points, (gpointer) point); } /* Remove last point if duplicate */ first = (GdkPoint*) points->data; polygon->points = points; if (first->x == point->x && first->y == point->y) polygon_remove_last_point(polygon); polygon->points = points; } g_free ($3); } ; href_tag : HREF '=' STRING { if (current_type == UNDEFINED) { g_strreplace(&_map_info->default_url, $3); } else { object_set_url(current_object, unescape_text($3)); } g_free ($3); } ; nohref_tag : NOHREF optional_value { } ; optional_value : /* Empty */ | '=' STRING { g_free ($2); } ; alt_tag : ALT '=' STRING { object_set_comment(current_object, unescape_text($3)); g_free ($3); } ; target_tag : TARGET '=' STRING { object_set_target(current_object, unescape_text($3)); g_free ($3); } ; onmouseover_tag : ONMOUSEOVER '=' STRING { object_set_mouse_over(current_object, unescape_text($3)); g_free ($3); } ; onmouseout_tag : ONMOUSEOUT '=' STRING { object_set_mouse_out(current_object, unescape_text($3)); g_free ($3); } ; onfocus_tag : ONFOCUS '=' STRING { object_set_focus(current_object, unescape_text($3)); g_free ($3); } ; onblur_tag : ONBLUR '=' STRING { object_set_blur(current_object, unescape_text($3)); g_free ($3); } ; end_map : '<' END_MAP '>' ; %% static void csim_error(char* s) { extern FILE *csim_in; csim_restart(csim_in); } gboolean load_csim (const char* filename) { gboolean status; extern FILE *csim_in; csim_in = g_fopen(filename, "r"); if (csim_in) { _map_info = get_map_info(); status = !csim_parse(); fclose(csim_in); } else { status = FALSE; } return status; } static gchar* unescape_text (gchar *input) { /* * We "unescape" simple things "in place", knowing that unescaped * strings always are shorter than the original input. * * It is a shame there is no g_markup_unescape_text() function, but * instead you have to create a full GMarkupParser/Context. */ struct token { const char *escaped; const char unescaped; }; const struct token tab[] = { { """, '"' }, { "'", '\'' }, { "&", '&' }, { "<", '<' }, { ">", '>' } }; size_t i; for (i = 0; i < (sizeof tab / sizeof tab[0]); i++) { const size_t escaped_len = strlen (tab[i].escaped); char *p; /* FIXME: The following code does not perform a UTF-8 substring search. */ for (p = strstr (input, tab[i].escaped); p != NULL; p = strstr (p, tab[i].escaped)) { size_t copy_len; *p++ = tab[i].unescaped; copy_len = strlen (p) - escaped_len + 2; memmove (p, p + escaped_len - 1, copy_len); if (*p == 0) break; } } return input; }