netscape-revival
lib/layout/layimage.c
/* -*- Mode: C; tab-width: 4 -*-
* layimage.c - Image layout and fetching/prefetching
*
* $Id: layimage.c,v 1.111 1996/07/31 21:11:09 brendan Exp $
*/
#include "xp.h"
#include "net.h"
#include "xp_rgb.h"
#include "pa_parse.h"
#include "layout.h"
#ifdef PROFILE
#pragma profile on
#endif
#define IMAGE_DEF_DIM 50
#define IMAGE_DEF_BORDER 0
#define IMAGE_DEF_ANCHOR_BORDER 2
#define IMAGE_DEF_VERTICAL_SPACE 0
#define IMAGE_DEF_HORIZONTAL_SPACE 0
#define IMAGE_DEF_FLOAT_HORIZONTAL_SPACE 3
/* Primitive image allocator also sets defaults for image structure */
static LO_ImageStruct *
lo_new_image_element(MWContext *context, lo_DocState *state,
void *edit_element, LO_TextAttr *tptr)
{
LO_ImageStruct *image;
image = (LO_ImageStruct *)lo_NewElement(context, state, LO_IMAGE, edit_element, 0);
if (image == NULL)
return NULL;
/*
* preserve the edit element and offset across the bzero.
* The edit_element and edit_offset were set in lo_NewElement.
*/
{
void* save_edit_element = image->edit_element;
int32 save_edit_offset = image->edit_offset;
XP_BZERO(image, sizeof(*image));
image->edit_element = save_edit_element;
image->edit_offset = save_edit_offset;
}
image->type = LO_IMAGE;
/*
* Fill in default font information.
*/
if (tptr == NULL)
{
LO_TextAttr tmp_attr;
lo_SetDefaultFontAttr(state, &tmp_attr, context);
tptr = lo_FetchTextAttr(state, &tmp_attr);
}
image->text_attr = tptr;
image->FE_Data = NULL;
image->image_data = NULL;
image->image_attr = XP_NEW(LO_ImageAttr);
if (image->image_attr == NULL)
{ /* clean up all previously allocated objects */
lo_FreeElement(context, (LO_Element *)image, FALSE);
state->top_state->out_of_memory = TRUE;
return 0;
}
image->image_attr->attrmask = 0;
image->image_attr->form_id = -1;
image->image_attr->alignment = LO_ALIGN_BASELINE;
image->image_attr->usemap_name = NULL;
image->image_attr->usemap_ptr = NULL;
image->ele_attrmask = 0;
image->sel_start = -1;
image->sel_end = -1;
return image;
}
Bool
LO_ParseRGB(char *rgb, uint8 *red_ptr, uint8 *green_ptr, uint8 *blue_ptr)
{
char *ptr;
int32 i, j, len;
int32 val, bval;
int32 red_val, green_val, blue_val;
intn bytes_per_val;
*red_ptr = 0;
*green_ptr = 0;
*blue_ptr = 0;
red_val = 0;
green_val = 0;
blue_val = 0;
if (rgb == NULL)
{
return FALSE;
}
len = XP_STRLEN(rgb);
if (len == 0)
{
return FALSE;
}
/*
* Strings not starting with a '#' are probably named colors.
* look them up in the xp lookup table.
*/
ptr = rgb;
if (*ptr == '#')
{
ptr++;
len--;
}
else
{
/*
* If we successfully look up a color name, return its RGB.
*/
if (XP_ColorNameToRGB(ptr, red_ptr, green_ptr, blue_ptr) == 0)
{
return TRUE;
}
}
if (len == 0)
{
return FALSE;
}
bytes_per_val = (intn)((len + 2) / 3);
if (bytes_per_val > 4)
{
bytes_per_val = 4;
}
for (j=0; j<3; j++)
{
val = 0;
for (i=0; i<bytes_per_val; i++)
{
if (*ptr == '\0')
{
bval = 0;
}
else
{
bval = TOLOWER((unsigned char)*ptr);
if ((bval >= '0')&&(bval <= '9'))
{
bval = bval - '0';
}
else if ((bval >= 'a')&&(bval <= 'f'))
{
bval = bval - 'a' + 10;
}
else
{
bval = 0;
}
ptr++;
}
val = (val << 4) + bval;
}
if (j == 0)
{
red_val = val;
}
else if (j == 1)
{
green_val = val;
}
else
{
blue_val = val;
}
}
while ((red_val > 255)||(green_val > 255)||(blue_val > 255))
{
red_val = (red_val >> 4);
green_val = (green_val >> 4);
blue_val = (blue_val >> 4);
}
*red_ptr = (uint8)red_val;
*green_ptr = (uint8)green_val;
*blue_ptr = (uint8)blue_val;
return TRUE;
}
void
lo_BodyForeground(MWContext *context, lo_DocState *state, LO_ImageStruct *image)
{
PA_Tag tag_rec;
PA_Tag *tag;
PA_Block buff;
char *str;
uint8 red, green, blue;
tag = &tag_rec;
tag->type = P_BODY;
tag->is_end = FALSE;
tag->newline_count = 0;
tag->data = image->alt;
tag->data_len = image->alt_len;
tag->true_len = 0;
tag->lo_data = NULL;
tag->next = NULL;
/*
* Get the document text TEXT parameter.
*/
buff = lo_FetchParamValue(context, tag, PARAM_TEXT);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
LO_ParseRGB(str, &red, &green, &blue);
PA_UNLOCK(buff);
PA_FREE(buff);
state->text_fg.red = red;
state->text_fg.green = green;
state->text_fg.blue = blue;
lo_RecolorText(state);
}
/*
* Get the anchor text LINK parameter.
*/
buff = lo_FetchParamValue(context, tag, PARAM_LINK);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
LO_ParseRGB(str, &red, &green, &blue);
PA_UNLOCK(buff);
PA_FREE(buff);
state->anchor_color.red = red;
state->anchor_color.green = green;
state->anchor_color.blue = blue;
}
/*
* Get the visited anchor text VLINK parameter.
*/
buff = lo_FetchParamValue(context, tag, PARAM_VLINK);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
LO_ParseRGB(str, &red, &green, &blue);
PA_UNLOCK(buff);
PA_FREE(buff);
state->visited_anchor_color.red = red;
state->visited_anchor_color.green = green;
state->visited_anchor_color.blue = blue;
}
/*
* Get the active anchor text ALINK parameter.
*/
buff = lo_FetchParamValue(context, tag, PARAM_ALINK);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
LO_ParseRGB(str, &red, &green, &blue);
PA_UNLOCK(buff);
PA_FREE(buff);
state->active_anchor_color.red = red;
state->active_anchor_color.green = green;
state->active_anchor_color.blue = blue;
}
if (image->alt != NULL)
{
PA_FREE(image->alt);
image->alt = NULL;
image->alt_len = 0;
}
/*
* Free up stuff we stuck in anchor_href
*/
if (image->anchor_href != NULL)
{
PA_FREE((PA_Block)(image->anchor_href));
image->anchor_href = NULL;
}
}
void
lo_BodyBackground(MWContext *context, lo_DocState *state, PA_Tag *tag,
Bool from_user)
{
LO_ImageStruct *image;
PA_Block buff;
char *str;
uint8 red, green, blue;
/*
* Fill in the backdrop image structure with default data
*/
image = lo_new_image_element(context, state, NULL, NULL);
if (!image)
return;
image->ele_id = -1;
/*
* Get the required src parameter, and make the resulting
* url and absolute url.
*/
buff = lo_FetchParamValue(context, tag, PARAM_BACKGROUND);
if (buff != NULL)
{
PA_Block new_buff;
char *new_str;
PA_LOCK(str, char *, buff);
if (str != NULL)
{
int32 len;
len = lo_StripTextWhitespace(str, XP_STRLEN(str));
}
/*
* Do not allow BACKGROUND=""
*/
if ((str != NULL)&&(*str != '\0'))
{
new_str = NET_MakeAbsoluteURL(
state->top_state->base_url, str);
}
else
{
new_str = NULL;
}
if (new_str == NULL)
{
new_buff = NULL;
}
else
{
char *url;
new_buff = PA_ALLOC(XP_STRLEN(new_str) + 1);
if (new_buff != NULL)
{
PA_LOCK(url, char *, new_buff);
XP_STRCPY(url, new_str);
PA_UNLOCK(new_buff);
}
else
{
state->top_state->out_of_memory = TRUE;
}
XP_FREE(new_str);
}
PA_UNLOCK(buff);
PA_FREE(buff);
buff = new_buff;
}
image->image_url = buff;
/*
* Get the BGCOLOR parameter. This is for a color background
* instead of an image backdrop. BACKGROUND if it exists overrides
* the BGCOLOR specification.
*/
buff = lo_FetchParamValue(context, tag, PARAM_BGCOLOR);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
LO_ParseRGB(str, &red, &green, &blue);
PA_UNLOCK(buff);
}
image->anchor_href = (LO_AnchorData *)buff;
/*
* Copy this tag data into alt, so we can yank it out later
* to parse possible foreground colors if our backdrop image
* succeeds.
*/
image->alt = PA_ALLOC(tag->data_len + 1);
if (image->alt != NULL)
{
char *tag_data;
PA_LOCK(tag_data, char *, tag->data);
PA_LOCK(str, char *, image->alt);
XP_STRCPY(str, tag_data);
PA_UNLOCK(image->alt);
PA_UNLOCK(tag->data);
image->alt_len = (int16)tag->data_len;
}
/*
* Make sure we don't load insecure images inside
* a secure document.
*/
if (state->top_state->security_level > 0)
{
PA_LOCK(str, char *, image->image_url);
if (((from_user == FALSE)&&(str != NULL))||
((from_user != FALSE)&&(str != NULL)&&
(XP_STRNCMP(str, "file:/", 6) != 0)))
{
if (NET_IsURLSecure(str) == FALSE)
{
PA_UNLOCK(image->image_url);
PA_FREE(image->image_url);
image->image_url = PA_ALLOC(
XP_STRLEN("internal-icon-insecure") + 1);
PA_LOCK(str, char *, image->image_url);
if (image->image_url != NULL)
{
XP_STRCPY(str, "internal-icon-insecure");
}
else
{
state->top_state->out_of_memory = TRUE;
}
if (state->top_state->insecure_images == FALSE)
{
state->top_state->insecure_images = TRUE;
FE_SecurityDialog(context, SD_INSECURE_DOCS_WITHIN_SECURE_DOCS_NOT_SHOWN);
}
}
}
PA_UNLOCK(image->image_url);
}
image->image_attr->attrmask |= LO_ATTR_BACKDROP;
if (from_user != FALSE)
{
image->image_attr->attrmask |= LO_ATTR_STICKY;
}
/*
* Have the front end fetch this backdrop image. It should
* know not to return or call me back until the whole image
* is loaded.
*/
if (image->anchor_href != NULL)
{
state->top_state->doc_specified_bg = TRUE;
state->text_bg.red = red;
state->text_bg.green = green;
state->text_bg.blue = blue;
lo_RecolorText(state);
FE_SetBackgroundColor(context, red, green, blue);
}
if (image->image_url != NULL)
{
state->top_state->doc_specified_bg = TRUE;
state->top_state->have_a_bg_url = TRUE;
FE_GetImageInfo(context, image, state->top_state->force_reload);
/*
* If the FE gave us the backdrop immediately,
* then top_state->backdrop_image will already be
* set, otherwise we block. Don't block if we are
* already blocked on a real image.
*/
if ((state->top_state->backdrop_image != (LO_Element *)image)&&
(state->top_state->layout_blocking_element == NULL))
{
#ifdef BLOCK_ON_BG_IMAGE
state->top_state->layout_blocking_element =
(LO_Element *)image;
#else
lo_BodyForeground(context, state, image);
#endif /* BLOCK_ON_BG_IMAGE */
}
}
else
{
lo_BodyForeground(context, state, image);
/*
* Free up the fake image element
*/
if (image->alt != NULL)
{
PA_FREE(image->alt);
}
image->alt = NULL;
if (image->image_url != NULL)
{
PA_FREE(image->image_url);
}
image->image_url = NULL;
if (image->lowres_image_url != NULL)
{
PA_FREE(image->lowres_image_url);
}
image->lowres_image_url = NULL;
lo_FreeImageAttributes(image->image_attr);
image->image_attr = NULL;
/*
* Free up stuff we stuck in anchor_href
*/
if (image->anchor_href != NULL)
{
PA_FREE((PA_Block)(image->anchor_href));
}
}
}
void
lo_BlockedImageLayout(MWContext *context, lo_DocState *state, PA_Tag *tag)
{
LO_ImageStruct *image;
PA_Block buff;
char *str;
int32 val;
int32 doc_width;
/*
* Fill in the image structure with default data
*/
image = lo_new_image_element(context, state, tag->edit_element, NULL);
if (!image)
return;
image->ele_id = NEXT_ELEMENT;
image->border_width = -1;
image->border_vert_space = IMAGE_DEF_VERTICAL_SPACE;
image->border_horiz_space = IMAGE_DEF_HORIZONTAL_SPACE;
/*
* Check for the ISMAP parameter
*/
buff = lo_FetchParamValue(context, tag, PARAM_ISMAP);
if (buff != NULL)
{
image->image_attr->attrmask |= LO_ATTR_ISMAP;
PA_FREE(buff);
}
/*
* Check for the USEMAP parameter. The name is stored in the
* image attributes immediately, the map pointer will be
* stored in later.
*/
buff = lo_FetchParamValue(context, tag, PARAM_USEMAP);
if (buff != NULL)
{
char *new_str;
char *map_name;
char *tptr;
map_name = NULL;
PA_LOCK(str, char *, buff);
/*
* Make this an absolute URL
*/
if (str != NULL)
{
int32 len;
len = lo_StripTextWhitespace(str, XP_STRLEN(str));
}
/*
* The IETF draft says evaluate #name relative to real,
* and not the base url.
*/
if ((str != NULL)&&(*str == '#'))
{
new_str = NET_MakeAbsoluteURL(
state->top_state->url, str);
}
else
{
new_str = NET_MakeAbsoluteURL(
state->top_state->base_url, str);
}
/*
* If we have a local URL, we can use the USEMAP.
*/
if ((new_str != NULL)&&
(lo_IsAnchorInDoc(state, new_str) != FALSE))
{
tptr = strrchr(new_str, '#');
if (tptr == NULL)
{
tptr = new_str;
}
else
{
tptr++;
}
map_name = XP_STRDUP(tptr);
XP_FREE(new_str);
}
/*
* Else a non-local URL, right now we don't support this.
*/
else if (new_str != NULL)
{
XP_FREE(new_str);
}
PA_UNLOCK(buff);
PA_FREE(buff);
image->image_attr->usemap_name = map_name;
}
/*
* Check for an align parameter
*/
buff = lo_FetchParamValue(context, tag, PARAM_ALIGN);
if (buff != NULL)
{
Bool floating;
floating = FALSE;
PA_LOCK(str, char *, buff);
image->image_attr->alignment = lo_EvalAlignParam(str,
&floating);
if (floating != FALSE)
{
image->ele_attrmask |= LO_ELE_FLOATING;
}
PA_UNLOCK(buff);
PA_FREE(buff);
}
/*
* Get the required src parameter, and make the resulting
* url and absolute url.
*/
buff = lo_FetchParamValue(context, tag, PARAM_SRC);
if (buff != NULL)
{
PA_Block new_buff;
char *new_str;
new_buff = NULL;
PA_LOCK(str, char *, buff);
if (str != NULL)
{
int32 len;
len = lo_StripTextWhitespace(str, XP_STRLEN(str));
}
new_str = NET_MakeAbsoluteURL(state->top_state->base_url, str);
if (new_str != NULL)
{
char *url;
new_buff = PA_ALLOC(XP_STRLEN(new_str) + 1);
if (new_buff != NULL)
{
PA_LOCK(url, char *, new_buff);
XP_STRCPY(url, new_str);
PA_UNLOCK(new_buff);
}
XP_FREE(new_str);
}
PA_UNLOCK(buff);
PA_FREE(buff);
buff = new_buff;
if (new_str == NULL || new_buff == NULL)
{ /* clean up all previously allocated objects */
lo_FreeElement(context, (LO_Element *)image, TRUE);
state->top_state->out_of_memory = TRUE;
return;
}
}
image->image_url = buff;
/*
* Get the optional lowsrc parameter, and make the resulting
* url and absolute url.
*/
buff = lo_FetchParamValue(context, tag, PARAM_LOWSRC);
if (buff != NULL)
{
PA_Block new_buff;
char *new_str;
new_buff = NULL;
PA_LOCK(str, char *, buff);
if (str != NULL)
{
int32 len;
len = lo_StripTextWhitespace(str, XP_STRLEN(str));
}
new_str = NET_MakeAbsoluteURL(state->top_state->base_url, str);
if (new_str != NULL)
{
char *url;
new_buff = PA_ALLOC(XP_STRLEN(new_str) + 1);
if (new_buff != NULL)
{
PA_LOCK(url, char *, new_buff);
XP_STRCPY(url, new_str);
PA_UNLOCK(new_buff);
}
XP_FREE(new_str);
}
PA_UNLOCK(buff);
PA_FREE(buff);
buff = new_buff;
if (new_str == NULL || new_buff == NULL)
{ /* clean up all previously allocated objects */
lo_FreeElement(context, (LO_Element *)image, TRUE);
state->top_state->out_of_memory = TRUE;
return;
}
}
image->lowres_image_url = buff;
/*
* Get the alt parameter, and store the resulting
* text, and its length.
*/
buff = lo_FetchParamValue(context, tag, PARAM_ALT);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
image->alt_len = XP_STRLEN(str);
image->alt_len = (int16)lo_StripTextNewlines(str,
(int32)image->alt_len);
PA_UNLOCK(buff);
}
image->alt = buff;
doc_width = state->right_margin - state->left_margin;
/*
* Get the width parameter, in absolute or percentage.
* If percentage, make it absolute.
*/
buff = lo_FetchParamValue(context, tag, PARAM_WIDTH);
if (buff != NULL)
{
Bool is_percent;
PA_LOCK(str, char *, buff);
val = lo_ValueOrPercent(str, &is_percent);
if (is_percent != FALSE)
{
if (state->allow_percent_width == FALSE)
{
val = 0;
}
else
{
val = doc_width * val / 100;
if (val < 1)
{
val = 1;
}
}
}
else
{
val = FEUNITS_X(val, context);
if (val < 1)
{
val = 1;
}
}
image->width = val;
PA_UNLOCK(buff);
PA_FREE(buff);
}
/*
* Get the height parameter, in absolute or percentage.
* If percentage, make it absolute.
*/
buff = lo_FetchParamValue(context, tag, PARAM_HEIGHT);
if (buff != NULL)
{
Bool is_percent;
PA_LOCK(str, char *, buff);
val = lo_ValueOrPercent(str, &is_percent);
if (is_percent != FALSE)
{
if (state->allow_percent_height == FALSE)
{
val = 0;
}
else
{
val = state->win_height * val / 100;
if (val < 1)
{
val = 1;
}
}
}
else
{
val = FEUNITS_Y(val, context);
if (val < 1)
{
val = 1;
}
}
image->height = val;
PA_UNLOCK(buff);
PA_FREE(buff);
}
/*
* Get the border parameter.
*/
buff = lo_FetchParamValue(context, tag, PARAM_BORDER);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
val = XP_ATOI(str);
if (val < 0)
{
val = 0;
}
image->border_width = val;
PA_UNLOCK(buff);
PA_FREE(buff);
}
image->border_width = FEUNITS_X(image->border_width, context);
/*
* Get the extra vertical space parameter.
*/
buff = lo_FetchParamValue(context, tag, PARAM_VSPACE);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
val = XP_ATOI(str);
if (val < 0)
{
val = 0;
}
image->border_vert_space = val;
PA_UNLOCK(buff);
PA_FREE(buff);
}
image->border_vert_space = FEUNITS_Y(image->border_vert_space, context);
/*
* Get the extra horizontal space parameter.
*/
if (image->ele_attrmask & LO_ELE_FLOATING)
{
image->border_horiz_space = IMAGE_DEF_FLOAT_HORIZONTAL_SPACE;
}
else
{
image->border_horiz_space = IMAGE_DEF_HORIZONTAL_SPACE;
}
buff = lo_FetchParamValue(context, tag, PARAM_HSPACE);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
val = XP_ATOI(str);
if (val < 0)
{
val = 0;
}
image->border_horiz_space = val;
PA_UNLOCK(buff);
PA_FREE(buff);
}
image->border_horiz_space = FEUNITS_X(image->border_horiz_space,
context);
/*
* Make sure we don't load insecure images inside
* a secure document.
*/
if (state->top_state->security_level > 0)
{
if (image->image_url != NULL)
{
PA_LOCK(str, char *, image->image_url);
if (NET_IsURLSecure(str) == FALSE)
{
PA_UNLOCK(image->image_url);
PA_FREE(image->image_url);
image->image_url = PA_ALLOC(
XP_STRLEN("internal-icon-insecure") + 1);
if (image->image_url != NULL)
{
PA_LOCK(str, char *, image->image_url);
XP_STRCPY(str, "internal-icon-insecure");
PA_UNLOCK(image->image_url);
}
else
{
state->top_state->out_of_memory = TRUE;
}
if (state->top_state->insecure_images == FALSE)
{
state->top_state->insecure_images = TRUE;
FE_SecurityDialog(context, SD_INSECURE_DOCS_WITHIN_SECURE_DOCS_NOT_SHOWN);
}
}
else
{
PA_UNLOCK(image->image_url);
}
}
}
#ifdef MOCHA
/* Reflect the image into Javascript */
lo_ReflectImage(context, state, tag, image, TRUE);
#endif
/*
* Have the front end fetch this image, and tells us
* its dimentions if it knows them.
*/
FE_GetImageInfo(context, image, state->top_state->force_reload);
tag->lo_data = (void *)image;
}
void
lo_PartialFormatImage(MWContext *context, lo_DocState *state, PA_Tag *tag)
{
LO_ImageStruct *image;
image = (LO_ImageStruct *)tag->lo_data;
if (image == NULL)
{
return;
}
#ifdef MOCHA
/* Reflect the image into Javascript */
lo_ReflectImage(context, state, tag, image, FALSE);
#endif
tag->lo_data = NULL;
/*
* Assign it a properly sequencial element id.
*/
image->ele_id = NEXT_ELEMENT;
image->x = state->x;
image->y = state->y;
image->anchor_href = state->current_anchor;
if (image->border_width < 0)
{
if ((image->anchor_href != NULL)||
(image->image_attr->usemap_name != NULL))
{
image->border_width = IMAGE_DEF_ANCHOR_BORDER;
}
else
{
image->border_width = IMAGE_DEF_BORDER;
}
image->border_width = FEUNITS_X(image->border_width, context);
}
if (state->font_stack == NULL)
{
LO_TextAttr tmp_attr;
LO_TextAttr *tptr;
/*
* Fill in default font information.
*/
lo_SetDefaultFontAttr(state, &tmp_attr, context);
tptr = lo_FetchTextAttr(state, &tmp_attr);
image->text_attr = tptr;
}
else
{
image->text_attr = state->font_stack->text_attr;
}
if ((image->text_attr != NULL)&&
(image->text_attr->attrmask & LO_ATTR_ANCHOR))
{
image->image_attr->attrmask |= LO_ATTR_ANCHOR;
}
if (image->image_attr->usemap_name != NULL)
{
LO_TextAttr tmp_attr;
lo_CopyTextAttr(image->text_attr, &tmp_attr);
tmp_attr.fg.red = STATE_UNVISITED_ANCHOR_RED(state);
tmp_attr.fg.green = STATE_UNVISITED_ANCHOR_GREEN(state);
tmp_attr.fg.blue = STATE_UNVISITED_ANCHOR_BLUE(state);
image->text_attr = lo_FetchTextAttr(state, &tmp_attr);
}
/*
* We may have to block on this image until later
* when the front end can give us the width and height.
*/
if ((image->width == 0)||(image->height == 0))
{
state->top_state->layout_blocking_element = (LO_Element *)image;
}
else
{
lo_FinishImage(context, state, image);
}
}
/*************************************
* Function: lo_FormatImage
*
* Description: This function does all the work to format
* and lay out an image on a line of elements.
* Creates the new image structure. Fills it
* in based on the parameters in the image tag.
* Calls the front end to start fetching the image
* and get image dimentions if necessary.
* Places image on line, breaking line if necessary.
*
* Params: Window context, document state, and the image tag data.
*
* Returns: Nothing.
*************************************/
void
lo_FormatImage(MWContext *context, lo_DocState *state, PA_Tag *tag)
{
LO_ImageStruct *image;
LO_TextAttr *tptr = NULL;
PA_Block buff;
char *str;
int32 val;
int32 doc_width;
Bool is_a_form;
if (state->font_stack)
tptr = state->font_stack->text_attr;
/*
* Fill in the image structure with default data
*/
image = lo_new_image_element(context, state, tag->edit_element, tptr);
if (!image)
return;
image->ele_id = NEXT_ELEMENT;
image->x = state->x;
image->y = state->y;
image->anchor_href = state->current_anchor;
if (image->anchor_href != NULL)
{
image->border_width = IMAGE_DEF_ANCHOR_BORDER;
}
else
{
image->border_width = IMAGE_DEF_BORDER;
}
image->border_vert_space = IMAGE_DEF_VERTICAL_SPACE;
image->border_horiz_space = IMAGE_DEF_HORIZONTAL_SPACE;
if ((image->text_attr != NULL)&&
(image->text_attr->attrmask & LO_ATTR_ANCHOR))
{
image->image_attr->attrmask |= LO_ATTR_ANCHOR;
}
if (tag->type == P_INPUT)
{
is_a_form = TRUE;
}
else
{
is_a_form = FALSE;
}
if (is_a_form != FALSE)
{
LO_TextAttr tmp_attr;
image->image_attr->form_id = state->top_state->current_form_num;
image->image_attr->attrmask |= LO_ATTR_ISFORM;
image->border_width = IMAGE_DEF_ANCHOR_BORDER;
lo_CopyTextAttr(image->text_attr, &tmp_attr);
tmp_attr.fg.red = STATE_UNVISITED_ANCHOR_RED(state);
tmp_attr.fg.green = STATE_UNVISITED_ANCHOR_GREEN(state);
tmp_attr.fg.blue = STATE_UNVISITED_ANCHOR_BLUE(state);
image->text_attr = lo_FetchTextAttr(state, &tmp_attr);
}
image->image_attr->usemap_name = NULL;
image->image_attr->usemap_ptr = NULL;
image->ele_attrmask = 0;
image->sel_start = -1;
image->sel_end = -1;
/*
* Check for the ISMAP parameter
*/
buff = lo_FetchParamValue(context, tag, PARAM_ISMAP);
if (buff != NULL)
{
image->image_attr->attrmask |= LO_ATTR_ISMAP;
PA_FREE(buff);
}
/*
* Check for the USEMAP parameter. The name is stored in the
* image attributes immediately, the map pointer will be
* stored in later.
*/
buff = lo_FetchParamValue(context, tag, PARAM_USEMAP);
if (buff != NULL)
{
char *new_str;
char *map_name;
char *tptr;
map_name = NULL;
PA_LOCK(str, char *, buff);
/*
* Make this an absolute URL
*/
if (str != NULL)
{
int32 len;
len = lo_StripTextWhitespace(str, XP_STRLEN(str));
}
/*
* The IETF draft says evaluate #name relative to real,
* and not the base url.
*/
if ((str != NULL)&&(*str == '#'))
{
new_str = NET_MakeAbsoluteURL(
state->top_state->url, str);
}
else
{
new_str = NET_MakeAbsoluteURL(
state->top_state->base_url, str);
}
/*
* If we have a local URL, we can use the USEMAP.
*/
if ((new_str != NULL)&&
(lo_IsAnchorInDoc(state, new_str) != FALSE))
{
tptr = strrchr(new_str, '#');
if (tptr == NULL)
{
tptr = new_str;
}
else
{
tptr++;
}
map_name = XP_STRDUP(tptr);
XP_FREE(new_str);
}
/*
* Else a non-local URL, right now we don't support this.
*/
else if (new_str != NULL)
{
XP_FREE(new_str);
}
PA_UNLOCK(buff);
PA_FREE(buff);
image->image_attr->usemap_name = map_name;
if (image->image_attr->usemap_name != NULL)
{
LO_TextAttr tmp_attr;
image->border_width = IMAGE_DEF_ANCHOR_BORDER;
lo_CopyTextAttr(image->text_attr, &tmp_attr);
tmp_attr.fg.red = STATE_UNVISITED_ANCHOR_RED(state);
tmp_attr.fg.green = STATE_UNVISITED_ANCHOR_GREEN(state);
tmp_attr.fg.blue = STATE_UNVISITED_ANCHOR_BLUE(state);
image->text_attr = lo_FetchTextAttr(state, &tmp_attr);
}
}
/*
* Check for an align parameter
*/
buff = lo_FetchParamValue(context, tag, PARAM_ALIGN);
if (buff != NULL)
{
Bool floating;
floating = FALSE;
PA_LOCK(str, char *, buff);
image->image_attr->alignment = lo_EvalAlignParam(str,
&floating);
if (floating != FALSE)
{
image->ele_attrmask |= LO_ELE_FLOATING;
}
PA_UNLOCK(buff);
PA_FREE(buff);
}
/*
* Get the required src parameter, and make the resulting
* url and absolute url.
*/
buff = lo_FetchParamValue(context, tag, PARAM_SRC);
if (buff != NULL)
{
PA_Block new_buff;
char *new_str;
PA_LOCK(str, char *, buff);
if (str != NULL)
{
int32 len;
len = lo_StripTextWhitespace(str, XP_STRLEN(str));
}
new_str = NET_MakeAbsoluteURL(state->top_state->base_url, str);
if (new_str == NULL)
{
new_buff = NULL;
}
else
{
char *url;
new_buff = PA_ALLOC(XP_STRLEN(new_str) + 1);
if (new_buff != NULL)
{
PA_LOCK(url, char *, new_buff);
XP_STRCPY(url, new_str);
PA_UNLOCK(new_buff);
}
else
{
state->top_state->out_of_memory = TRUE;
}
/* Complete kludge: if this image has the magic SRC,
* then also parse an HREF from it (the IMG tag doesn't
* normally have an HREF parameter.)
*
* The front ends know that when the user clicks on an
* image with the magic SRC, that they may find the
* "real" URL in the HREF... Gag me.
*/
if (!XP_STRCMP(new_str, "internal-external-reconnect"))
{
PA_Block hbuf;
hbuf = lo_FetchParamValue(context, tag, PARAM_HREF);
if (hbuf != NULL)
{
image->anchor_href =
lo_NewAnchor(state, hbuf, NULL);
}
}
XP_FREE(new_str);
}
PA_UNLOCK(buff);
PA_FREE(buff);
buff = new_buff;
}
image->image_url = buff;
/*
* Get the optional lowsrc parameter, and make the resulting
* url and absolute url.
*/
buff = lo_FetchParamValue(context, tag, PARAM_LOWSRC);
if (buff != NULL)
{
PA_Block new_buff;
char *new_str;
PA_LOCK(str, char *, buff);
if (str != NULL)
{
int32 len;
len = lo_StripTextWhitespace(str, XP_STRLEN(str));
}
new_str = NET_MakeAbsoluteURL(state->top_state->base_url, str);
if (new_str == NULL)
{
new_buff = NULL;
}
else
{
char *url;
new_buff = PA_ALLOC(XP_STRLEN(new_str) + 1);
if (new_buff != NULL)
{
PA_LOCK(url, char *, new_buff);
XP_STRCPY(url, new_str);
PA_UNLOCK(new_buff);
}
else
{
state->top_state->out_of_memory = TRUE;
}
XP_FREE(new_str);
}
PA_UNLOCK(buff);
PA_FREE(buff);
buff = new_buff;
}
image->lowres_image_url = buff;
/*
* Get the alt parameter, and store the resulting
* text, and its length. Form the special image form
* element, store the name in alt.
*/
if (is_a_form != FALSE)
{
buff = lo_FetchParamValue(context, tag, PARAM_NAME);
}
else
{
buff = lo_FetchParamValue(context, tag, PARAM_ALT);
}
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
image->alt_len = XP_STRLEN(str);
image->alt_len = (int16)lo_StripTextNewlines(str,
(int32)image->alt_len);
PA_UNLOCK(buff);
}
image->alt = buff;
doc_width = state->right_margin - state->left_margin;
/*
* Get the width parameter, in absolute or percentage.
* If percentage, make it absolute.
*/
buff = lo_FetchParamValue(context, tag, PARAM_WIDTH);
if (buff != NULL)
{
Bool is_percent;
PA_LOCK(str, char *, buff);
val = lo_ValueOrPercent(str, &is_percent);
if (is_percent != FALSE)
{
if (state->allow_percent_width == FALSE)
{
val = 0;
}
else
{
val = doc_width * val / 100;
if (val < 1)
{
val = 1;
}
}
}
else
{
val = FEUNITS_X(val, context);
if (val < 1)
{
val = 1;
}
}
image->width = val;
PA_UNLOCK(buff);
PA_FREE(buff);
}
/*
* Get the height parameter, in absolute or percentage.
* If percentage, make it absolute.
*/
buff = lo_FetchParamValue(context, tag, PARAM_HEIGHT);
if (buff != NULL)
{
Bool is_percent;
PA_LOCK(str, char *, buff);
val = lo_ValueOrPercent(str, &is_percent);
if (is_percent != FALSE)
{
if (state->allow_percent_height == FALSE)
{
val = 0;
}
else
{
val = state->win_height * val / 100;
if (val < 1)
{
val = 1;
}
}
}
else
{
val = FEUNITS_Y(val, context);
if (val < 1)
{
val = 1;
}
}
image->height = val;
PA_UNLOCK(buff);
PA_FREE(buff);
}
/*
* Get the border parameter.
*/
buff = lo_FetchParamValue(context, tag, PARAM_BORDER);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
val = XP_ATOI(str);
if (val < 0)
{
val = 0;
}
image->border_width = val;
PA_UNLOCK(buff);
PA_FREE(buff);
}
image->border_width = FEUNITS_X(image->border_width, context);
/*
* Get the extra vertical space parameter.
*/
buff = lo_FetchParamValue(context, tag, PARAM_VSPACE);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
val = XP_ATOI(str);
if (val < 0)
{
val = 0;
}
image->border_vert_space = val;
PA_UNLOCK(buff);
PA_FREE(buff);
}
image->border_vert_space = FEUNITS_Y(image->border_vert_space, context);
/*
* Get the extra horizontal space parameter.
*/
if (image->ele_attrmask & LO_ELE_FLOATING)
{
image->border_horiz_space = IMAGE_DEF_FLOAT_HORIZONTAL_SPACE;
}
else
{
image->border_horiz_space = IMAGE_DEF_HORIZONTAL_SPACE;
}
buff = lo_FetchParamValue(context, tag, PARAM_HSPACE);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
val = XP_ATOI(str);
if (val < 0)
{
val = 0;
}
image->border_horiz_space = val;
PA_UNLOCK(buff);
PA_FREE(buff);
}
image->border_horiz_space = FEUNITS_X(image->border_horiz_space,
context);
/*
* Make sure we don't load insecure images inside
* a secure document.
*/
if (state->top_state->security_level > 0)
{
if (image->image_url != NULL)
{
PA_LOCK(str, char *, image->image_url);
if (NET_IsURLSecure(str) == FALSE)
{
PA_UNLOCK(image->image_url);
PA_FREE(image->image_url);
image->image_url = PA_ALLOC(
XP_STRLEN("internal-icon-insecure") + 1);
if (image->image_url != NULL)
{
PA_LOCK(str, char *, image->image_url);
XP_STRCPY(str, "internal-icon-insecure");
PA_UNLOCK(image->image_url);
}
else
{
state->top_state->out_of_memory = TRUE;
}
if (state->top_state->insecure_images == FALSE)
{
state->top_state->insecure_images = TRUE;
FE_SecurityDialog(context, SD_INSECURE_DOCS_WITHIN_SECURE_DOCS_NOT_SHOWN);
}
}
else
{
PA_UNLOCK(image->image_url);
}
}
}
#ifdef MOCHA
/* Reflect the image into Javascript */
lo_ReflectImage(context, state, tag, image, FALSE);
#endif
/*
* Have the front end fetch this image, and tells us
* its dimentions if it knows them.
*/
FE_GetImageInfo(context, image, state->top_state->force_reload);
/*
* We may have to block on this image until later
* when the front end can give us the width and height.
*/
if ((image->width == 0)||(image->height == 0))
{
state->top_state->layout_blocking_element = (LO_Element *)image;
}
else
{
lo_FinishImage(context, state, image);
}
}
void
lo_FinishImage(MWContext *context, lo_DocState *state, LO_ImageStruct *image)
{
PA_Block buff;
char *str;
Bool line_break;
int32 baseline_inc;
int32 line_inc;
int32 image_height, image_width;
LO_TextStruct tmp_text;
LO_TextInfo text_info;
/*
* All this work is to get the text_info filled in for the current
* font in the font stack. Yuck, there must be a better way.
*/
memset (&tmp_text, 0, sizeof (tmp_text));
buff = PA_ALLOC(1);
if (buff == NULL)
{
state->top_state->out_of_memory = TRUE;
return;
}
PA_LOCK(str, char *, buff);
str[0] = ' ';
PA_UNLOCK(buff);
tmp_text.text = buff;
tmp_text.text_len = 1;
tmp_text.text_attr =
state->font_stack->text_attr;
FE_GetTextInfo(context, &tmp_text, &text_info);
PA_FREE(buff);
/*
* Eventually this will never happen since we always
* have dims here from either the image tag itself or the
* front end.
*/
if (image->width == 0)
{
if (image->image_data != NULL)
{
image->width = image->image_data->width;
}
else
{
image->width = IMAGE_DEF_DIM;
}
}
if (image->height == 0)
{
if (image->image_data != NULL)
{
image->height = image->image_data->height;
}
else
{
image->height = IMAGE_DEF_DIM;
}
}
image_width = image->width + (2 * image->border_width) +
(2 * image->border_horiz_space);
image_height = image->height + (2 * image->border_width) +
(2 * image->border_vert_space);
/*
* If this image has a named usemap, try to locate it
* in the map_list and stuff a pointer into the image
* structure.
*
* We no longer look this up now, because due to
* table cell relayouts, the usemap_ptr may change.
* This lookup will be triggered later, after everything
* is laid out by user events in the USEMAPed image.
*/
#if 0
if (image->image_attr->usemap_name != NULL)
{
lo_MapRec *map;
map = lo_FindNamedMap(context, state,
image->image_attr->usemap_name);
image->image_attr->usemap_ptr = (void *)map;
}
#endif
/*
* SEVERE FLOW BREAK! This may be a floating image,
* which means at this point we go do something completely
* different.
*/
if (image->ele_attrmask & LO_ELE_FLOATING)
{
if (image->image_attr->alignment == LO_ALIGN_RIGHT)
{
if (state->right_margin_stack == NULL)
{
image->x = state->right_margin - image_width;
}
else
{
image->x = state->right_margin_stack->margin -
image_width;
}
if (image->x < 0)
{
image->x = 0;
}
}
else
{
image->x = state->left_margin;
}
image->y = -1;
image->x_offset += (int16)image->border_horiz_space;
image->y_offset += (int32)image->border_vert_space;
lo_AddMarginStack(state, image->x, image->y,
image->width, image->height,
image->border_width,
image->border_vert_space, image->border_horiz_space,
image->image_attr->alignment);
/*
* Insert this element into the float list.
*/
image->next = state->float_list;
state->float_list = (LO_Element *)image;
if (state->at_begin_line != FALSE)
{
lo_FindLineMargins(context, state);
state->x = state->left_margin;
}
return;
}
/*
* Will this image make the line too wide.
*/
if ((state->x + image_width) > state->right_margin)
{
line_break = TRUE;
}
else
{
line_break = FALSE;
}
/*
* We cannot break a line if we have no break positions.
* Usually happens with a single line of unbreakable text.
*/
/*
* This doesn't work right now, I don't know why, seems we
* have lost the last old_break_pos somehow.
*/
#ifdef FIX_THIS
if ((line_break != FALSE)&&(state->break_pos == -1))
{
/*
* It may be possible to break a previous
* text element on the same line.
*/
if (state->old_break_pos != -1)
{
lo_BreakOldElement(context, state);
line_break = FALSE;
}
else
{
line_break = FALSE;
}
}
#endif /* FIX_THIS */
/*
* if we are at the beginning of the line. There is
* no point in breaking, we are just too wide.
* Also don't break in unwrapped preformatted text.
* Also can't break inside a NOBR section.
*/
if ((state->at_begin_line != FALSE)||
(state->preformatted == PRE_TEXT_YES)||
(state->breakable == FALSE))
{
line_break = FALSE;
}
/*
* break on the image if we have
* a break.
*/
if (line_break != FALSE)
{
/*
* We need to make the elements sequential, linefeed
* before image.
*/
state->top_state->element_id = image->ele_id;
lo_HardLineBreak(context, state, TRUE);
image->x = state->x;
image->y = state->y;
image->ele_id = NEXT_ELEMENT;
}
/*
* Figure out how to align this image.
* baseline_inc is how much to increase the baseline
* of previous element of this line. line_inc is how
* much to increase the line height below the baseline.
*/
baseline_inc = 0;
line_inc = 0;
/*
* If we are at the beginning of a line, with no baseline,
* we first set baseline and line_height based on the current
* font, then place the image.
*/
if (state->baseline == 0)
{
#ifdef ALIGN_IMAGE_WITH_FAKE_TEXT
state->baseline = text_info.ascent;
state->line_height = text_info.ascent + text_info.descent;
#else
state->baseline = 0;
/* Why do we need this?
if (state->line_height < 1)
{
state->line_height = 1;
}
*/
#endif /* ALIGN_IMAGE_WITH_FAKE_TEXT */
}
lo_CalcAlignOffsets(state, &text_info, image->image_attr->alignment,
image_width, image_height,
&image->x_offset, &image->y_offset, &line_inc, &baseline_inc);
image->x_offset += (int16)image->border_horiz_space;
image->y_offset += (int32)image->border_vert_space;
lo_AppendToLineList(context, state,
(LO_Element *)image, baseline_inc);
state->baseline += (intn) baseline_inc;
state->line_height += (intn) (baseline_inc + line_inc);
/*
* Clean up state
*/
state->x = state->x + image->x_offset +
image_width - image->border_horiz_space;
state->linefeed_state = 0;
state->at_begin_line = FALSE;
state->trailing_space = FALSE;
state->cur_ele_type = LO_IMAGE;
}
Bool
LO_BlockedOnImage(MWContext *context, LO_ImageStruct *image)
{
int32 doc_id;
lo_TopState *top_state;
/*
* Get the unique document ID, and retreive this
* documents layout state.
*/
doc_id = XP_DOCID(context);
top_state = lo_FetchTopState(doc_id);
if ((top_state == NULL)||(top_state->doc_state == NULL))
{
return(FALSE);
}
if ((top_state->layout_blocking_element != NULL)&&
(top_state->layout_blocking_element == (LO_Element *)image))
{
return(TRUE);
}
return(FALSE);
}
/*
* Make sure the image library has been informed of all possible
* image URLs that might be loaded by this document.
*/
void
lo_NoMoreImages(MWContext *context)
{
PA_Tag *tag_ptr;
int32 doc_id;
lo_TopState *top_state;
lo_DocState *state;
/*
* All blocked tags should be at the top level of state
*/
doc_id = XP_DOCID(context);
top_state = lo_FetchTopState(doc_id);
if ((top_state == NULL)||(top_state->doc_state == NULL))
{
/*
* No matter what, we must be done with all images now
*/
IL_NoMoreImages(context);
return;
}
state = top_state->doc_state;
tag_ptr = top_state->tags;
/*
* Go through all the remaining blocked tags looking
* for <INPUT TYPE=IMAGE> tags.
*/
while (tag_ptr != NULL)
{
if (tag_ptr->type == P_INPUT)
{
PA_Block buff;
char *str;
int32 type;
type = FORM_TYPE_TEXT;
buff = lo_FetchParamValue(context, tag_ptr, PARAM_TYPE);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
type = lo_ResolveInputType(str);
PA_UNLOCK(buff);
PA_FREE(buff);
}
/*
* Prefetch this image.
* The ImageStruct created will get stuck into
* tag_ptr->lo_data, and freed later in
* lo_ProcessInputTag().
*/
if (type == FORM_TYPE_IMAGE)
{
lo_BlockedImageLayout(context, state, tag_ptr);
}
}
tag_ptr = tag_ptr->next;
}
/*
* No matter what, we must be done with all images now
*/
IL_NoMoreImages(context);
}
#ifdef EDITOR
LO_ImageStruct* LO_NewImageElement( MWContext* context ){
int32 doc_id;
lo_TopState *top_state;
lo_DocState *state;
LO_ImageStruct *image;
/*
* Get the unique document ID, and retreive this
* documents layout state.
*/
doc_id = XP_DOCID(context);
top_state = lo_FetchTopState(doc_id);
if ((top_state == NULL)||(top_state->doc_state == NULL))
{
return 0;
}
state = top_state->doc_state;
state->edit_current_element = 0;
state->edit_current_offset = 0;
/*
* Fill in the image structure with default data
*/
image = lo_new_image_element(context, state, NULL, NULL);
if (!image)
return NULL;
image->border_width = -1;
image->border_vert_space = IMAGE_DEF_VERTICAL_SPACE;
image->border_horiz_space = IMAGE_DEF_HORIZONTAL_SPACE;
return image;
}
#endif
#ifdef PROFILE
#pragma profile off
#endif