1 /*
2  * Fig2dev: Translate Fig code to various Devices
3  * Copyright (c) 1991 by Micah Beck
4  * Parts Copyright (c) 1985-1988 by Supoj Sutanthavibul
5  * Parts Copyright (c) 1989-2015 by Brian V. Smith
6  * Parts Copyright (c) 2015-2020 by Thomas Loimer
7  *
8  * Any party obtaining a copy of these files is granted, free of charge, a
9  * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
10  * nonexclusive right and license to deal in this software and documentation
11  * files (the "Software"), including without limitation the rights to use,
12  * copy, modify, merge, publish, distribute, sublicense and/or sell copies
13  * of the Software, and to permit persons who receive copies from any such
14  * party to do so, with the only requirement being that the above copyright
15  * and this permission notice remain intact.
16  *
17  */
18 
19 /*
20  * genmap.c: convert fig to HTML imagemap
21  *
22  * Copyright (c) 1999 by T. Sato <VEF00200@nifty.ne.jp>
23  *
24  *
25  * Description:
26  *	Generate HTML imagemap from Fig file.
27  *	If an object has comment like ``HREF="url" ALT="string"'',
28  *	the object will become a link to the URL.
29  *	Object with smaller depth will be listed before deeper ones.
30  *	Figure comment will be used as the default link.
31  */
32 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #ifdef	HAVE_STRINGS_H
41 #include <strings.h>
42 #endif
43 #include <math.h>
44 #include <ctype.h>
45 
46 #include "fig2dev.h"	/* includes bool.h and object.h */
47 //#include "object.h"
48 #include "messages.h"
49 #include "pi.h"
50 
51 #define TEXT_LENGTH  300
52 
53 static int	border_margin = 0;
54 static char	url[TEXT_LENGTH], alt[TEXT_LENGTH];
55 static char	buf[2000];
56 
57 struct hlink_item {
58   char *url;
59   char *alt;
60   char *area;
61   struct hlink_item* prev;
62 };
63 typedef struct hlink_item hlink;
64 
65 /* The last link that was added. */
66 static hlink* last_link = 0;
67 
68 
69 #define fscale 15
70 
71 #define XZOOM(x)   round(mag * ((x) - llx) / fscale + border_margin)
72 #define YZOOM(y)   round(mag * ((y) - lly) / fscale + border_margin)
73 
74 #define MIN_DIST    5  /* ignore closer points when processing polyline */
75 
76 #define ARC_STEP    (M_PI / 9.0)
77 #define ARC_EXPAND  1.02   /* make polygon slightly larger */
78 
79 
80 void
genmap_option(char opt,char * optarg)81 genmap_option(char opt, char *optarg)
82 {
83   switch (opt) {
84   case 'b':	/* border margin */
85     sscanf(optarg,"%d",&border_margin);
86     break;
87   case 'G':
88   case 'L':
89     break;
90   default:
91     put_msg(Err_badarg, opt, "map");
92     exit(1);
93   }
94 }
95 
96 static char *
is_link(F_comment * comment)97 is_link(F_comment *comment)
98 {
99   char *cp1, *cp2;
100   bool have_alt = false;
101   int i;
102 
103   url[0] = '\0';
104   alt[0] = '\0';
105 
106   while (comment != NULL) {
107     cp1 = comment->comment;
108     while (isspace(*cp1)) cp1++;  /* ignore leading spaces */
109 
110     if (strncasecmp(cp1, "HREF=", 5) == 0) {
111       /* comment like "HREF=url ALT=string" */
112 
113       cp2 = cp1 + 5;
114       while (isspace(*cp2)) cp2++;  /* spaces */
115       i = 0;
116       while (isgraph(*cp2)) url[i++] = *(cp2++);  /* URL */
117       url[i] = '\0';
118       while (isspace(*cp2)) cp2++;  /* spaces */
119       if (*cp2 != '\0') {
120 	if (strncasecmp(cp2, "ALT=", 4) == 0) {
121 	  have_alt = true;
122 	  cp2 = cp2 + 4;
123 	  while (isspace(*cp2)) cp2++;	/* spaces */
124 	  i = 0;
125 	  if (*cp2 == '"') {
126 	    cp2++;
127 	    while (*cp2 != '\0' && *cp2 != '"') alt[i++] = *(cp2++);  /* text */
128 	  } else {
129 	    while (isgraph(*cp2)) alt[i++] = *(cp2++);	/* text */
130 	  }
131 	  alt[i] = '\0';
132 	} else {
133 	  fprintf(stderr, "fig2dev(map): unknown attribute: %s\n", cp2);
134 	}
135       }
136       if (!have_alt)
137 	fprintf(stderr, "fig2dev(map): ALT is required in HTML 3.2: %s\n", cp1);
138       return cp1;
139     }
140     comment = comment->next;
141   }
142   return NULL;
143 }
144 
145 static void
add_link(char * area)146 add_link(char *area)
147 {
148     hlink* hl = (hlink*)malloc(sizeof(hlink));
149     hl->url = (char *)malloc(strlen(url) + 1);
150     strcpy(hl->url, url);
151     hl->alt = (char *)malloc(strlen(alt) + 1);
152     strcpy(hl->alt, alt);
153     hl->area = (char *)malloc(strlen(area) + 1);
154     strcpy(hl->area, area);
155     hl->prev = last_link;
156     last_link=hl;
157 }
158 
159 void
genmap_start(F_compound * objects)160 genmap_start(F_compound *objects)
161 {
162   char *basename_buf = NULL;
163   char *basename;
164   char *ref;
165   int i;
166 
167   if (to != NULL) {
168     basename_buf = strdup(to);
169     basename = basename_buf;
170     for (i = strlen(basename) - 1; 0 < i; --i) {
171       if (basename[i] == '.') {
172 	basename[i] = '\0';
173 	break;
174       }
175     }
176   } else {
177     basename = "imagemap";
178   }
179 
180   fprintf(tfp, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Frameset//EN\">\n");
181   fprintf(tfp, "<HTML>\n");
182   fprintf(tfp, "<HEAD><TITLE>HTML imagemap generated by fig2dev</TITLE></HEAD>\n");
183   fprintf(tfp, "<BODY>\n\n");
184   fprintf(tfp, "<H1>HTML imagemap generated by fig2dev</H1>\n");
185   fprintf(tfp, "<P>This file is generated by fig2dev.</P>\n");
186   fprintf(tfp, "<P>You can copy the following lines into your HTML document.\n");
187   fprintf(tfp, "You may need to edit the name of the image file in the first line.</P>\n");
188   fprintf(tfp, "\n");
189   fprintf(tfp, "<IMG SRC=\"%s.png\" USEMAP=\"#%s\">\n", basename, basename);
190   fprintf(tfp, "<MAP NAME=\"%s\">\n", basename);
191 
192   ref = is_link(objects->comments);
193   if (ref != NULL) {
194     sprintf(buf, "<AREA COORDS=\"%d,%d,%d,%d\" %s>\n",
195 	    XZOOM(llx), YZOOM(lly), XZOOM(urx), YZOOM(ury), ref);
196     add_link(buf);
197   }
198   if (basename_buf)
199     free(basename_buf);
200 }
201 
202 int
genmap_end(void)203 genmap_end(void)
204 {
205   hlink* l;
206   int len;
207   char label[TEXT_LENGTH];
208 
209   for (l = last_link; l!= 0; l=l->prev) {
210     fprintf(tfp, "%s", l->area);
211   }
212   fprintf(tfp, "</MAP>\n");
213 
214   fprintf(tfp, "\n");
215   fprintf(tfp, "<h2>Alternative Text Links</h2>\n");
216   fprintf(tfp, "<P>Because not all people can use imagemaps,\n");
217   fprintf(tfp, "it is recommended to prepare an alternative way for the people\n");
218   fprintf(tfp, "who can't use or don't want to use them.</P>\n");
219   fprintf(tfp, "<P>Here are text the links extracted from the above imagemap.\n");
220   fprintf(tfp, "If the ALT attribute (it is required in HTML 3.2) was not specified\n");
221   fprintf(tfp, "in the imagemap, those URLs will be displayed\n");
222   fprintf(tfp, "instead of labels specified by the ALT attributes.</P>\n");
223   fprintf(tfp, "\n");
224   fprintf(tfp, "<P ALIGN=\"CENTER\">\n");
225   len = 0;
226   for (l = last_link; l!= 0; l=l->prev) {
227     if (l->alt[0] != '\0') strcpy(label, l->alt);
228     else sprintf(label, "<TT>%s</TT>", l->url);
229     fprintf(tfp, "%s<A HREF=%s>%s</A>\n",
230 	    (len == 0) ? "" : " | ", l->url, label);
231 
232     len = len + strlen(label) + 3;
233     if (50 < len) {
234       fprintf(tfp, "<BR>\n");
235       len = 0;
236     }
237   }
238   fprintf(tfp, "</P>\n");
239 
240   fprintf(tfp, "\n");
241   fprintf(tfp, "</BODY>\n");
242   fprintf(tfp, "</HTML>\n");
243 
244   /* all ok */
245   return 0;
246 }
247 
248 void
genmap_arc(F_arc * a)249 genmap_arc(F_arc *a)
250 {
251   char *ref;
252   int cx, cy, sx, sy, ex, ey;
253   double r;
254   double sa, ea;
255   double alpha;
256 
257   ref = is_link(a->comments);
258   if (ref != NULL) {
259     cx = XZOOM(a->center.x);
260     cy = YZOOM(a->center.y);
261     sx = XZOOM(a->point[0].x);
262     sy = YZOOM(a->point[0].y);
263     ex = XZOOM(a->point[2].x);
264     ey = YZOOM(a->point[2].y);
265 
266     if (a->direction == 0) {
267       sa = atan2((double)(sy - cy), (double)(sx - cx));
268       ea = atan2((double)(ey - cy), (double)(ex - cx));
269     } else {
270       ea = atan2((double)(sy - cy), (double)(sx - cx));
271       sa = atan2((double)(ey - cy), (double)(ex - cx));
272     }
273     if (ea < sa) ea = ea + 2 * M_PI;
274 
275     r = sqrt((double)((sx - cx) * (sx - cx)
276 		      + (sy - cy) * (sy - cy))) * ARC_EXPAND + 1.0;
277 
278     sprintf(buf, "<AREA SHAPE=\"poly\" COORDS=\"");
279     if (a->type == T_PIE_WEDGE_ARC)
280       sprintf(&buf[strlen(buf)], "%d,%d,", cx, cy);
281     for (alpha = sa; alpha < (ea - ARC_STEP / 4); alpha += ARC_STEP) {
282       if (alpha != sa) sprintf(&buf[strlen(buf)], ",");
283       sprintf(&buf[strlen(buf)], "%d,%d",
284 	      round(cx + r * cos(alpha)), round(cy + r * sin(alpha)));
285     }
286     sprintf(&buf[strlen(buf)], ",%d,%d",
287 	    round(cx + r * cos(ea)), round(cy + r * sin(ea)));
288     sprintf(&buf[strlen(buf)], "\" %s>\n", ref);
289     add_link(buf);
290   }
291 }
292 
293 void
genmap_ellipse(F_ellipse * e)294 genmap_ellipse(F_ellipse *e)
295 {
296   char *ref;
297   int x0, y0;
298   double rx, ry;
299   double angle, theta;
300 
301   ref = is_link(e->comments);
302   if (ref != NULL) {
303     x0 = XZOOM(e->center.x);
304     y0 = YZOOM(e->center.y);
305     rx = mag * e->radiuses.x / fscale;
306     ry = mag * e->radiuses.y / fscale;
307     angle = - e->angle;
308     if (e->radiuses.x == e->radiuses.y) {
309       sprintf(buf, "<AREA SHAPE=\"circle\" COORDS=\"%d,%d,%d\" %s>\n",
310 	      x0, y0, round(rx) + 1, ref);
311       add_link(buf);
312     } else {
313       rx = rx * ARC_EXPAND + 1.0;
314       ry = ry * ARC_EXPAND + 1.0;
315       sprintf(buf, "<AREA SHAPE=\"poly\" COORDS=\"");
316       for (theta = 0.0; theta < 2.0 * M_PI; theta += ARC_STEP) {
317 	if (theta != 0.0) sprintf(&buf[strlen(buf)], ",");
318 	sprintf(&buf[strlen(buf)], "%d,%d",
319 		round(x0 + cos(angle) * rx * cos(theta)
320 		      - sin(angle) * ry * sin(theta)),
321 		round(y0 + sin(angle) * rx * cos(theta)
322 		      + cos(angle) * ry * sin(theta)));
323       }
324       sprintf(&buf[strlen(buf)], "\" %s>\n", ref);
325       add_link(buf);
326     }
327   }
328 }
329 
330 void
genmap_line(F_line * l)331 genmap_line(F_line *l)
332 {
333   char *ref;
334   int xmin, xmax, ymin, ymax;
335   int x, y, last_x, last_y;
336   F_point *p;
337 
338   ref = is_link(l->comments);
339   if (ref != NULL) {
340     switch (l->type) {
341     case T_BOX:
342     case T_ARC_BOX:
343     case T_PIC_BOX:
344       xmin = xmax = l->points->x;
345       ymin = ymax = l->points->y;
346       for (p = l->points->next; p != NULL; p = p->next) {
347 	if (p->x < xmin) xmin = p->x;
348 	if (xmax < p->x) xmax = p->x;
349 	if (p->y < ymin) ymin = p->y;
350 	if (ymax < p->y) ymax = p->y;
351       }
352       sprintf(buf, "<AREA COORDS=\"%d,%d,%d,%d\" %s>\n",
353 	      XZOOM(xmin), YZOOM(ymin), XZOOM(xmax), YZOOM(ymax), ref);
354       add_link(buf);
355       break;
356     case T_POLYLINE:
357     case T_POLYGON:
358       sprintf(buf, "<AREA SHAPE=\"poly\" COORDS=\"");
359       for (p = l->points; (l->type==T_POLYLINE? p: p->next) != NULL; p = p->next) {
360 	x = XZOOM(p->x);
361 	y = YZOOM(p->y);
362 	if (p == l->points
363 	    || MIN_DIST < abs(last_x - x) || MIN_DIST < abs(last_y - y)) {
364 	  if (sizeof(buf) < strlen(buf) + 100) {
365 	    fprintf(stderr, "fig2dev(map): too complex POLYLINE... ignored\n");
366 	    return;
367 	  } else {
368 	    if (p != l->points) sprintf(&buf[strlen(buf)], ",");
369 	    sprintf(&buf[strlen(buf)], "%d,%d", x, y);
370 	    last_x = x;
371 	    last_y = y;
372 	  }
373 	}
374       }
375       sprintf(&buf[strlen(buf)], "\" %s>\n", ref);
376       add_link(buf);
377       break;
378     }
379   }
380 }
381 
382 void
genmap_text(F_text * t)383 genmap_text(F_text *t)
384 {
385   char *ref;
386 
387   ref = is_link(t->comments);
388   if (ref != NULL) {
389     fprintf(stderr, "fig2dev(map): TEXT can't be used as link region\n");
390   }
391 }
392 
393 struct driver dev_map = {
394 	genmap_option,
395 	genmap_start,
396 	gendev_nogrid,
397 	genmap_arc,
398 	genmap_ellipse,
399 	genmap_line,
400 	(void (*)(F_spline *))gendev_null,
401 	genmap_text,
402 	genmap_end,
403 	INCLUDE_TEXT
404 };
405