[project @ 2004-03-26 22:16:31 by jmb]

Imagemap support

svn path=/import/netsurf/; revision=671
This commit is contained in:
John Mark Bell 2004-03-26 22:16:31 +00:00
parent 83b0835341
commit 4b38a2d61a
8 changed files with 617 additions and 3 deletions

View File

@ -24,6 +24,7 @@
#include "netsurf/desktop/401login.h"
#endif
#include "netsurf/desktop/browser.h"
#include "netsurf/desktop/imagemap.h"
#include "netsurf/render/box.h"
#include "netsurf/render/font.h"
#include "netsurf/render/form.h"
@ -1362,6 +1363,34 @@ void browser_window_follow_link(struct browser_window *bw,
free(url);
break;
}
if (click_boxes[i].box->usemap != NULL) {
char *href, *url;
href = imagemap_get(bw->current_content,
click_boxes[i].box->usemap,
click_boxes[i].actual_x,
click_boxes[i].actual_y,
click_x, click_y);
if (!href)
continue;
url = url_join(href,
bw->current_content->data.html.
base_url);
if (!url)
continue;
if (click_type == 1) {
browser_window_go(bw, url);
} else if (click_type == 2) {
browser_window_create(url);
} else if (click_type == 0) {
browser_window_set_status(bw, url);
done = 1;
}
free(url);
break;
}
if (click_type == 0 && click_boxes[i].box->title != NULL) {
browser_window_set_status(bw,
click_boxes[i].box->

527
desktop/imagemap.c Normal file
View File

@ -0,0 +1,527 @@
/*
* This file is part of NetSurf, http://netsurf.sourceforge.net/
* Licensed under the GNU General Public License,
* http://www.opensource.org/licenses/gpl-license
* Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
*
* Much of this shamelessly copied from utils/messages.c
*/
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include "netsurf/content/content.h"
#include "netsurf/desktop/imagemap.h"
#include "netsurf/utils/log.h"
#include "netsurf/utils/utils.h"
#define HASH_SIZE 31 /* fixed size hash table */
typedef enum {IMAGEMAP_DEFAULT, IMAGEMAP_RECT, IMAGEMAP_CIRCLE, IMAGEMAP_POLY } imagemap_entry_type;
struct mapentry {
imagemap_entry_type type; /**< type of shape */
char *url; /**< url to go to */
union {
struct {
int x; /**< x coordinate of centre */
int y; /**< y coordinate of center */
int r; /**< radius of circle */
} circle;
struct {
int x0; /**< left hand edge */
int y0; /**< top edge */
int x1; /**< right hand edge */
int y1; /**< bottom edge */
} rect;
struct {
int num; /**< number of points */
float *xcoords; /**< x coordinates */
float *ycoords; /**< y coordinates */
} poly;
} bounds;
struct mapentry *next; /**< next entry in list */
};
struct imagemap {
char *key; /**< key for this entry */
struct mapentry *list; /**< pointer to linked list of entries */
struct imagemap *next; /**< next entry in this hash chain */
};
static void imagemap_add(struct content *c, const char *key,
struct mapentry *list);
static void imagemap_create(struct content *c);
static struct mapentry *imagemap_extract_map(xmlNode *node, struct content *c,
struct mapentry *entry);
static struct mapentry *imagemap_addtolist(xmlNode *n, struct mapentry *entry);
static void imagemap_freelist(struct mapentry *list);
static unsigned int imagemap_hash(const char *key);
static int imagemap_point_in_poly(int num, float *xpt, float *ypt, unsigned long x, unsigned long y, unsigned long click_x, unsigned long click_y);
/**
* Add an imagemap to the hashtable, creating it if it doesn't exist
*
* @param c The containing content
* @param key The name of the imagemap
* @param list List of map regions
*/
void imagemap_add(struct content *c, const char *key, struct mapentry *list) {
struct imagemap *map;
unsigned int slot;
assert(c->type == CONTENT_HTML);
imagemap_create(c);
map = xcalloc(1, sizeof(*map));
map->key = xstrdup(key);
map->list = list;
slot = imagemap_hash(key);
map->next = c->data.html.imagemaps[slot];
c->data.html.imagemaps[slot] = map;
}
/**
* Create hashtable of imagemaps
*
* @param c The containing content
*/
void imagemap_create(struct content *c) {
assert(c->type == CONTENT_HTML);
if (c->data.html.imagemaps == 0) {
c->data.html.imagemaps = xcalloc(HASH_SIZE,
sizeof(struct imagemap));
}
}
/**
* Destroy hashtable of imagemaps
*
* @param c The containing content
*/
void imagemap_destroy(struct content *c) {
unsigned int i;
assert(c->type == CONTENT_HTML);
/* no imagemaps -> return */
if (c->data.html.imagemaps == 0) return;
for (i = 0; i != HASH_SIZE; i++) {
struct imagemap *map, *next;
map = c->data.html.imagemaps[i];
while (map != 0) {
next = map->next;
imagemap_freelist(map->list);
xfree(map->key);
xfree(map);
map = next;
}
}
xfree(c->data.html.imagemaps);
}
/**
* Dump imagemap data to the log
*
* @param c The containing content
*/
void imagemap_dump(struct content *c) {
unsigned int i;
int j;
assert(c->type == CONTENT_HTML);
if (c->data.html.imagemaps == 0) return;
for (i = 0; i != HASH_SIZE; i++) {
struct imagemap *map;
struct mapentry *entry;
map = c->data.html.imagemaps[i];
while (map != 0) {
LOG(("Imagemap: %s", map->key));
for (entry = map->list; entry; entry = entry->next) {
switch (entry->type) {
case IMAGEMAP_DEFAULT:
LOG(("\tDefault: %s", entry->url));
break;
case IMAGEMAP_RECT:
LOG(("\tRectangle: %s: [(%d,%d),(%d,%d)]",
entry->url,
entry->bounds.rect.x0,
entry->bounds.rect.y0,
entry->bounds.rect.x1,
entry->bounds.rect.y1));
break;
case IMAGEMAP_CIRCLE:
LOG(("\tCircle: %s: [(%d,%d),%d]",
entry->url,
entry->bounds.circle.x,
entry->bounds.circle.y,
entry->bounds.circle.r));
break;
case IMAGEMAP_POLY:
LOG(("\tPolygon: %s:",
entry->url));
for (j=0;
j!=entry->bounds.poly.num;
j++) {
fprintf(stderr, "(%d,%d) ",
(int)entry->bounds.poly.xcoords[j],
(int)entry->bounds.poly.ycoords[j]);
}
fprintf(stderr,"\n");
break;
}
}
map = map->next;
}
}
}
/**
* Extract all imagemaps from a document tree
*
* @param node Root node of tree
* @param c The containing content
*/
void imagemap_extract(xmlNode *node, struct content *c) {
xmlNode *this;
struct mapentry *entry = 0;
char *name;
if (node->type == XML_ELEMENT_NODE) {
if (strcmp(node->name, "map") == 0) {
if (!(name = (char*)xmlGetProp(node,
(const xmlChar*)"name")))
return;
entry = imagemap_extract_map(node, c, entry);
imagemap_add(c, name, entry);
xmlFree(name);
return;
}
}
else return;
/* now recurse */
for (this = node->children; this != 0; this = this->next) {
imagemap_extract(this, c);
}
}
struct mapentry *imagemap_extract_map(xmlNode *node, struct content *c, struct mapentry *entry) {
xmlNode *this;
if (node->type == XML_ELEMENT_NODE) {
/** \todo ignore <area> elements if there are other
* block-level elements present in map
*/
if (strcmp(node->name, "area") == 0 ||
strcmp(node->name, "a") == 0) {
entry = imagemap_addtolist(node, entry);
return entry;
}
}
else return entry;
for (this = node->children; this != 0; this = this->next) {
entry = imagemap_extract_map(this, c, entry);
}
return entry;
}
struct mapentry *imagemap_addtolist(xmlNode *n, struct mapentry *entry) {
char *shape, *coords = 0, *href, *val;
int num;
struct mapentry *new, *temp;
if (strcmp(n->name, "area") == 0) {
/* nohref attribute present - ignore this entry */
if (xmlGetProp(n, (const xmlChar*)"nohref") != 0) {
return entry;
}
}
/* no href -> ignore */
if (!(href = (char*)xmlGetProp(n, (const xmlChar*)"href"))) {
return entry;
}
/* no shape -> ignore */
if (!(shape = (char*)xmlGetProp(n, (const xmlChar*)"shape"))) {
xmlFree(href);
return entry;
}
if (strcasecmp(shape, "default") != 0) {
/* no coords -> ignore */
if (!(coords = (char*)xmlGetProp(n, (const xmlChar*)"coords"))) {
xmlFree(href);
xmlFree(shape);
return entry;
}
}
new = xcalloc(1, sizeof(*new));
/* extract area shape */
if (strcasecmp(shape, "rect") == 0) {
new->type = IMAGEMAP_RECT;
}
else if (strcasecmp(shape, "circle") == 0) {
new->type = IMAGEMAP_CIRCLE;
}
else if (strcasecmp(shape, "poly") == 0) {
new->type = IMAGEMAP_POLY;
}
else if (strcasecmp(shape, "default") == 0) {
new->type = IMAGEMAP_DEFAULT;
}
else { /* unknown shape -> bail */
xfree(new);
xmlFree(href);
xmlFree(shape);
xmlFree(coords);
return entry;
}
new->url = xstrdup(href);
if (new->type != IMAGEMAP_DEFAULT) {
/* coordinates are a comma-separated list of values */
val = strtok(coords, ",");
num = 1;
switch (new->type) {
case IMAGEMAP_RECT:
/* (left, top, right, bottom) */
while (val && num <= 4) {
switch (num) {
case 1:
new->bounds.rect.x0 = atoi(val);
break;
case 2:
new->bounds.rect.y0 = atoi(val);
break;
case 3:
new->bounds.rect.x1 = atoi(val);
break;
case 4:
new->bounds.rect.y1 = atoi(val);
break;
}
num++;
val = strtok('\0', ",");
}
break;
case IMAGEMAP_CIRCLE:
/* (x, y, radius ) */
while (val && num <= 3) {
switch (num) {
case 1:
new->bounds.circle.x = atoi(val);
break;
case 2: new->bounds.circle.y = atoi(val);
break;
case 3:
new->bounds.circle.r = atoi(val);
break;
}
num++;
val = strtok('\0', ",");
}
break;
case IMAGEMAP_POLY:
new->bounds.poly.xcoords =
xcalloc(0, sizeof(*new->bounds.poly.xcoords));
new->bounds.poly.ycoords =
xcalloc(0, sizeof(*new->bounds.poly.ycoords));
int x, y;
while (val) {
x = atoi(val);
val = strtok('\0', ",");
if (!val) break;
y = atoi(val);
new->bounds.poly.xcoords =
xrealloc(new->bounds.poly.xcoords,
num*sizeof(*new->bounds.poly.xcoords));
new->bounds.poly.ycoords =
xrealloc(new->bounds.poly.ycoords,
num*sizeof(*new->bounds.poly.ycoords));
new->bounds.poly.xcoords[num-1] = x;
new->bounds.poly.ycoords[num-1] = y;
num++;
val = strtok('\0', ",");
}
new->bounds.poly.num = num-1;
break;
default:
break;
}
}
new->next = 0;
if (entry) {
/* add to END of list */
for (temp = entry; temp->next != 0; temp = temp->next)
;
temp->next = new;
}
else {
entry = new;
}
xmlFree(href);
xmlFree(shape);
xmlFree(coords);
return entry;
}
/**
* Free list of imagemap entries
*
* @param list Pointer to head of list
*/
void imagemap_freelist(struct mapentry *list) {
struct mapentry *entry, *prev;
entry = list;
while (entry != 0) {
prev = entry;
xfree(entry->url);
if (entry->type == IMAGEMAP_POLY) {
xfree(entry->bounds.poly.xcoords);
xfree(entry->bounds.poly.ycoords);
}
entry = entry->next;
xfree(prev);
}
}
/**
* Retrieve url associated with imagemap entry
*
* @param c The containing content
* @param key The map name to search for
* @param x The left edge of the containing box
* @param y The top edge of the containing box
* @param click_x The horizontal location of the click
* @param click_y The vertical location of the click
* @return The url associated with this area, or NULL if not found
*/
char *imagemap_get(struct content *c, const char *key, unsigned long x,
unsigned long y, unsigned long click_x, unsigned long click_y) {
unsigned int slot = 0;
struct imagemap *map;
struct mapentry *entry;
unsigned long cx, cy;
assert(c->type == CONTENT_HTML);
slot = imagemap_hash(key);
for (map = c->data.html.imagemaps[slot];
map != 0 && strcasecmp(map->key, key) != 0;
map = map->next)
;
if (map == 0) return NULL;
for (entry = map->list; entry; entry = entry->next) {
switch (entry->type) {
case IMAGEMAP_DEFAULT:
/* just return the URL. no checks required */
return entry->url;
break;
case IMAGEMAP_RECT:
if (click_x >= x + entry->bounds.rect.x0 &&
click_x <= x + entry->bounds.rect.x1 &&
click_y >= y + entry->bounds.rect.y0 &&
click_y <= y + entry->bounds.rect.y1) {
return entry->url;
}
break;
case IMAGEMAP_CIRCLE:
cx = x + entry->bounds.circle.x - click_x;
cy = y + entry->bounds.circle.y - click_y;
if ((cx*cx + cy*cy) <=
(unsigned long)(entry->bounds.circle.r*
entry->bounds.circle.r)) {
return entry->url;
}
break;
case IMAGEMAP_POLY:
if (imagemap_point_in_poly(entry->bounds.poly.num, entry->bounds.poly.xcoords, entry->bounds.poly.ycoords, x, y, click_x, click_y)) {
return entry->url;
}
break;
}
}
return NULL;
}
/**
* Hash function
*
* @param key The key to hash
* @return The hashed value
*/
unsigned int imagemap_hash(const char *key) {
unsigned int z = 0;
if (key == 0) return 0;
for (; *key != 0; key++) {
z += *key & 0x1f;
}
return (z % (HASH_SIZE - 1)) + 1;
}
/**
* Test if a point lies within an arbitrary polygon
* Modified from comp.graphics.algorithms FAQ 2.03
*
* @param num Number of vertices
* @param xpt Array of x coordinates
* @param ypt Array of y coordinates
* @param x Left hand edge of containing box
* @param y Top edge of containing box
* @param click_x X coordinate of click
* @param click_y Y coordinate of click
* @return 1 if point is in polygon, 0 if outside. 0 or 1 if on boundary
*/
int imagemap_point_in_poly(int num, float *xpt, float *ypt, unsigned long x, unsigned long y, unsigned long click_x, unsigned long click_y) {
int i, j, c=0;
for (i = 0, j = num-1; i < num; j = i++) {
if ((((ypt[i]+y <= click_y) && (click_y < ypt[j]+y)) ||
((ypt[j]+y <= click_y) && (click_y < ypt[i]+y))) &&
(click_x < (xpt[j] - xpt[i]) *
(click_y - (ypt[i]+y)) / (ypt[j] - ypt[i]) + xpt[i]+x))
c = !c;
}
return c;
}

21
desktop/imagemap.h Normal file
View File

@ -0,0 +1,21 @@
/*
* This file is part of NetSurf, http://netsurf.sourceforge.net/
* Licensed under the GNU General Public License,
* http://www.opensource.org/licenses/gpl-license
* Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
*/
#ifndef _NETSURF_DESKTOP_IMAGEMAP_H_
#define _NETSURF_DESKTOP_IMAGEMAP_H_
#include "libxml/HTMLtree.h"
struct content;
void imagemap_destroy(struct content *c);
void imagemap_dump(struct content *c);
void imagemap_extract(xmlNode *node, struct content *c);
char *imagemap_get(struct content *c, const char *key, unsigned long x,
unsigned long y, unsigned long click_x, unsigned long click_y);
#endif

View File

@ -12,7 +12,7 @@ OBJECTS_COMMON = cache.o content.o fetch.o fetchcache.o \
messages.o utils.o translit.o pool.o url.o
OBJECTS = $(OBJECTS_COMMON) \
browser.o loginlist.o netsurf.o options.o \
htmlinstance.o htmlredraw.o \
htmlinstance.o htmlredraw.o imagemap.o \
401login.o constdata.o dialog.o download.o frames.o gui.o \
menus.o mouseactions.o \
textselection.o theme.o window.o \

View File

@ -190,6 +190,7 @@ struct box * box_create(struct css_style * style,
box->col = 0;
box->font = 0;
box->gadget = 0;
box->usemap = 0;
box->object = 0;
#ifdef WITH_PLUGIN
box->object_params = 0;
@ -771,7 +772,7 @@ struct result box_image(xmlNode *n, struct status *status,
struct css_style *style)
{
struct box *box;
char *s, *url, *s1;
char *s, *url, *s1, *map;
xmlChar *s2;
box = box_create(style, status->href, status->title,
@ -789,6 +790,17 @@ struct result box_image(xmlNode *n, struct status *status,
if (!(s = (char *) xmlGetProp(n, (const xmlChar *) "src")))
return (struct result) {box, 0};
/* imagemap associated with this image */
if ((map = xmlGetProp(n, (const xmlChar *) "usemap"))) {
if (map[0] == '#') {
box->usemap = xstrdup(map+1);
}
else {
box->usemap = xstrdup(map);
}
xmlFree(map);
}
/* remove leading and trailing whitespace */
s1 = strip(s);
@ -1764,6 +1776,8 @@ void box_free_box(struct box *box)
free(box->style);
}
if (box->usemap)
free(box->usemap);
free(box->text);
/* TODO: free object_params */
}
@ -1779,7 +1793,7 @@ struct result box_object(xmlNode *n, struct status *status,
struct box *box;
struct object_params *po;
struct plugin_params* pp;
char *s, *url = NULL;
char *s, *url = NULL, *map;
xmlNode *c;
box = box_create(style, status->href, 0,
@ -1808,6 +1822,17 @@ struct result box_object(xmlNode *n, struct status *status,
xmlFree(s);
}
/* imagemap associated with this object */
if ((map = xmlGetProp(n, (const xmlChar *) "usemap"))) {
if (map[0] == '#') {
box->usemap = xstrdup(map+1);
}
else {
box->usemap = xstrdup(map);
}
xmlFree(map);
}
/* object type */
if ((s = (char *) xmlGetProp(n, (const xmlChar *) "type"))) {

View File

@ -195,6 +195,8 @@ struct box {
/** Form control data, or 0 if not a form control. */
struct form_control* gadget;
char *usemap; /** (Image)map to use with this object, or 0 if none */
/** Object in this box (usually an image), or 0 if none. */
struct content* object;
/** Parameters for the object, or 0. */

View File

@ -18,6 +18,7 @@
#include "netsurf/content/content.h"
#include "netsurf/content/fetch.h"
#include "netsurf/content/fetchcache.h"
#include "netsurf/desktop/imagemap.h"
#ifdef riscos
#include "netsurf/desktop/gui.h"
#endif
@ -180,6 +181,10 @@ int html_convert(struct content *c, unsigned int width, unsigned int height)
xml_to_box(html, c);
/*box_dump(c->data.html.layout->children, 0);*/
/* extract image maps - can't do this sensibly in xml_to_box */
imagemap_extract(html, c);
/*imagemap_dump(c);*/
/* XML tree not required past this point */
xmlFreeDoc(document);
@ -752,6 +757,8 @@ void html_destroy(struct content *c)
free(c->title);
imagemap_destroy(c);
if (c->data.html.parser)
htmlFreeParserCtxt(c->data.html.parser);

View File

@ -23,6 +23,7 @@ struct box;
struct browser_window;
struct content;
struct object_params;
struct imagemap;
struct box_position {
struct box *box;
@ -73,6 +74,8 @@ struct content_html_data {
const content_type *permitted_types;
} *object;
struct imagemap **imagemaps; /**< Hashtable of imagemaps */
pool box_pool; /**< Memory pool for box tree. */
pool string_pool; /**< Memory pool for strings. */
};