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