зеркало из https://github.com/mozilla/gecko-dev.git
7706 строки
184 KiB
C
7706 строки
184 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
|
|
#include "xp.h"
|
|
#include "pa_parse.h"
|
|
#include "layout.h"
|
|
#include "laylayer.h"
|
|
#include "glhist.h"
|
|
#ifdef JAVA
|
|
#include "java.h"
|
|
#endif
|
|
#include "libi18n.h"
|
|
#include "edt.h"
|
|
#include "laystyle.h"
|
|
#include "prefapi.h"
|
|
#include "xp_ncent.h"
|
|
#include "prefetch.h"
|
|
#include "np.h"
|
|
|
|
/* style sheet tag stack and style struct */
|
|
#include "stystack.h"
|
|
#include "stystruc.h"
|
|
#include "pics.h"
|
|
|
|
#include "libmocha.h"
|
|
#include "libevent.h"
|
|
|
|
#include "shist.h"
|
|
#include "privacy.h"
|
|
|
|
#include "intl_csi.h"
|
|
|
|
#ifdef DOM
|
|
#include "lm_dom.h"
|
|
#endif
|
|
|
|
/* WEBFONTS are defined only in laytags.c and layout.c */
|
|
#define WEBFONTS
|
|
|
|
#ifdef WEBFONTS
|
|
#include "nf.h"
|
|
#include "Mnffbu.h"
|
|
#endif /* WEBFONTS */
|
|
|
|
#ifdef XP_WIN16
|
|
#define SIZE_LIMIT 32000
|
|
#endif
|
|
|
|
/* RDF is currently only in Mac & Windows builds */
|
|
#if defined(XP_MAC) || defined(XP_WIN)
|
|
#include "htrdf.h"
|
|
#endif
|
|
|
|
#define LIST_MARGIN_INC (FEUNITS_X(40, context))
|
|
#define MQUOTE_MARGIN_INC (LIST_MARGIN_INC / 3)
|
|
|
|
#if 0
|
|
/* cmanske: Moved to layout.h so Composer can use them */
|
|
#define MIN_FONT_SIZE 1
|
|
#define MAX_FONT_SIZE 7
|
|
|
|
#define DEFAULT_BASE_POINT_SIZE 12
|
|
#define MIN_POINT_SIZE 1
|
|
#define MAX_POINT_SIZE 1600
|
|
#endif
|
|
|
|
#define DEFAULT_BASE_FONT_WEIGHT 400
|
|
#define MIN_FONT_WEIGHT 100
|
|
#define MAX_FONT_WEIGHT 900
|
|
|
|
#define HYPE_ANCHOR "about:hype"
|
|
#define HYPE_TAG_BECOMES "SRC=internal-gopher-sound BORDER=0>"
|
|
|
|
/* Added to encapsulate code that was previously in six different places in LO_LayoutTag()! */
|
|
static void lo_ProcessFontTag( lo_DocState *state, PA_Tag *tag, int32 fontSpecifier, int32 attrSpecifier );
|
|
|
|
static void lo_AddParam(PA_Tag* tag, char* aName, char* aValue);
|
|
|
|
#ifdef OJI
|
|
#define JAVA_PLUGIN_MIMETYPE "application/x-java-vm"
|
|
#endif
|
|
|
|
#ifdef ANTHRAX
|
|
static void lo_RemoveParam(PA_Tag* tag, char* name);
|
|
#endif
|
|
|
|
/*************************************
|
|
* Function: LO_ChangeFontSize
|
|
*
|
|
* Description: Utility function to change a size field based on a
|
|
* string which is either a new absolute value, or a relative
|
|
* change prefaced with + or -. Also sanifies the result
|
|
* to a valid font size.
|
|
*
|
|
* Params: Original size, and change string.
|
|
*
|
|
* Returns: New size.
|
|
*************************************/
|
|
PRIVATE
|
|
intn
|
|
LO_ChangeFontOrPointSize(intn size,
|
|
char *size_str,
|
|
intn lower_bound,
|
|
intn upper_bound)
|
|
{
|
|
intn new_size;
|
|
|
|
if ((size_str == NULL)||(*size_str == '\0'))
|
|
{
|
|
return(size);
|
|
}
|
|
|
|
if (*size_str == '+')
|
|
{
|
|
new_size = size + XP_ATOI((size_str + 1));
|
|
}
|
|
else if (*size_str == '-')
|
|
{
|
|
new_size = size - XP_ATOI((size_str + 1));
|
|
}
|
|
else
|
|
{
|
|
new_size = XP_ATOI(size_str);
|
|
}
|
|
|
|
if (new_size < lower_bound)
|
|
{
|
|
new_size = lower_bound;
|
|
}
|
|
if (new_size > upper_bound)
|
|
{
|
|
new_size = upper_bound;
|
|
}
|
|
|
|
return(new_size);
|
|
}
|
|
|
|
intn
|
|
LO_ChangeFontSize(intn size, char *size_str)
|
|
{
|
|
return LO_ChangeFontOrPointSize(size, size_str, MIN_FONT_SIZE, MAX_FONT_SIZE);
|
|
}
|
|
|
|
PRIVATE
|
|
intn
|
|
LO_ChangePointSize(intn size, char *size_str)
|
|
{
|
|
return LO_ChangeFontOrPointSize(size, size_str, MIN_POINT_SIZE, MAX_POINT_SIZE);
|
|
}
|
|
|
|
/*
|
|
* Filter out tags we should ignore based on current state.
|
|
* Return FALSE for tags we should ignore, and TRUE for
|
|
* tags we should keep.
|
|
*/
|
|
Bool
|
|
lo_FilterTag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
lo_TopState *top_state;
|
|
|
|
top_state = state->top_state;
|
|
if (top_state == NULL)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
if ((tag->data != NULL) && (tag->newline_count != LO_IGNORE_TAG_MARKER) &&
|
|
(state->in_relayout == FALSE) &&
|
|
(lo_IsTagInSourcedLayer(state, tag) == FALSE))
|
|
{
|
|
top_state->layout_bytes += tag->true_len;
|
|
}
|
|
|
|
/*
|
|
* If we are in a GRID, all non-grid tags are discarded
|
|
* If we are in a NOGRIDS, all tags except its end are discarded.
|
|
*/
|
|
if (top_state->is_grid != FALSE)
|
|
{
|
|
if (tag->type == P_NOGRIDS)
|
|
{
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
top_state->in_nogrids = TRUE;
|
|
}
|
|
else
|
|
{
|
|
top_state->in_nogrids = FALSE;
|
|
}
|
|
return(FALSE);
|
|
}
|
|
else if (top_state->in_nogrids != FALSE)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
else if ((tag->type != P_GRID)&&
|
|
(tag->type != P_GRID_CELL))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
/*
|
|
* For the LAYER tag, a SUPPRESS attribute means that all content
|
|
* till the corresponding </LAYER> should be ignored. To figure
|
|
* out which is the corresponding </LAYER> tag, we maintain a
|
|
* nesting count of LAYER tags.
|
|
*/
|
|
if ((tag->type == P_LAYER) || (tag->type == P_ILAYER)) {
|
|
/*
|
|
* If we're inside a <LAYER SUPPRESS> and this is a </LAYER>
|
|
* tag, then decrement the layer nesting count. If it goes
|
|
* down to 0, then this is the ending of the suppress tag
|
|
* and we decrement the ignore_tag_nest_level count.
|
|
*/
|
|
if (top_state->ignore_layer_nest_level && tag->is_end) {
|
|
if ((--top_state->ignore_layer_nest_level == 0) &&
|
|
(top_state->ignore_tag_nest_level > 0)) {
|
|
top_state->ignore_tag_nest_level--;
|
|
/* We throw away this </LAYER> tag */
|
|
return FALSE;
|
|
}
|
|
}
|
|
/*
|
|
* If we're inside a <LAYER SUPPRESS> and this is a <LAYER>
|
|
* tag, then just increment the layer nesting count.
|
|
*/
|
|
else if (top_state->ignore_tag_nest_level > 0) {
|
|
top_state->ignore_layer_nest_level++;
|
|
}
|
|
/* Otherwise, just check for the SUPPRESS attribute */
|
|
else {
|
|
char *buff;
|
|
|
|
buff = (char*) lo_FetchParamValue(context, tag, PARAM_SUPPRESS);
|
|
if (buff) {
|
|
top_state->ignore_tag_nest_level++;
|
|
top_state->ignore_layer_nest_level++;
|
|
XP_FREE(buff);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Not all UNIXes can do embed yet, so display the NOEMBED stuff */
|
|
#if defined(XP_UNIX) && !defined(UNIX_EMBED)
|
|
#define SUPPRESS_NOEMBED_CONTENTS 0
|
|
#else
|
|
#define SUPPRESS_NOEMBED_CONTENTS 1
|
|
#endif
|
|
|
|
/*
|
|
* Handle NOSCRIPT, NOEMBED and NOLAYER.
|
|
*/
|
|
if (((tag->type == P_NOSCRIPT) && LM_CanDoJS(context)) ||
|
|
((tag->type == P_NOLAYER)) ||
|
|
((tag->type == P_NOEMBED) && SUPPRESS_NOEMBED_CONTENTS))
|
|
{
|
|
if (!tag->is_end)
|
|
top_state->ignore_tag_nest_level++;
|
|
else if (top_state->ignore_tag_nest_level > 0)
|
|
top_state->ignore_tag_nest_level--;
|
|
}
|
|
|
|
/* Inside NOEMBED, NOSCRIPT, or NOLAYER ignore everything
|
|
* except the closing tag.
|
|
*/
|
|
if (top_state->ignore_tag_nest_level > 0)
|
|
return FALSE;
|
|
|
|
/*
|
|
* If we're inside an object, check to see whether we
|
|
* should be filtering the contents or not:
|
|
*
|
|
* + If we know how to handle the object, we should filter
|
|
* (except for PARAMs and nested OBJECTs, which we let
|
|
* pass the filter so the code in lo_LayoutTag can keep
|
|
* track of which parameters go with which object).
|
|
*
|
|
* + If we don't know how to handle the object (type is
|
|
* LO_UNKNOWN), or don't yet know if we know how to
|
|
* handle the object (type is LO_NONE), we don't filter.
|
|
*
|
|
* To determine if we're in a known object, we need to
|
|
* look up the whole object stack for a known object at
|
|
* any level.
|
|
*
|
|
* Also adding P_JAVA_APPLET to the list - support for applet redirect to obj tag
|
|
*/
|
|
if (top_state->object_stack != NULL &&
|
|
tag->type != P_PARAM && tag->type != P_OBJECT
|
|
#ifdef OJI
|
|
&& tag->type != P_JAVA_APPLET
|
|
#endif
|
|
)
|
|
{
|
|
/* Check for a known object anywhere on the stack */
|
|
lo_ObjectStack* top = top_state->object_stack;
|
|
while (top != NULL)
|
|
{
|
|
if (top->object != NULL &&
|
|
top->object->lo_element.lo_plugin.type != LO_NONE &&
|
|
top->object->lo_element.lo_plugin.type != LO_UNKNOWN)
|
|
{
|
|
/* The object was known, so filter out its contents */
|
|
return FALSE;
|
|
}
|
|
top = top->next;
|
|
}
|
|
}
|
|
|
|
#ifdef JAVA
|
|
/*
|
|
* If the user has disabled Java, act like we don't understand
|
|
* the APPLET tag.
|
|
*
|
|
* Warren sez: I factored the LJ_GetJavaEnabled() test into the two
|
|
* if statements here because it was pretty inefficient the way it
|
|
* was written -- LJ_GetJavaEnabled() at one point was going all the
|
|
* way out to the prefs for this info, and this routine is called
|
|
* for each line.
|
|
*/
|
|
/*
|
|
* APPLETs can appear anywhere. Check for them and
|
|
* maintain proper in_applet state.
|
|
*/
|
|
if (tag->type == P_JAVA_APPLET && LJ_GetJavaEnabled())
|
|
{
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
top_state->in_applet = TRUE;
|
|
#ifndef M12N /* XXXM12N Fix me, this has gone away. Sets
|
|
cx->colorSpace->install_colormap_forbidden*/
|
|
IL_UseDefaultColormapThisPage(context);
|
|
#endif /* M12N */
|
|
}
|
|
else
|
|
{
|
|
top_state->in_applet = FALSE;
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
/*
|
|
* Inside applet ignore everything except param and
|
|
* closing applets.
|
|
*/
|
|
if (top_state->in_applet != FALSE && LJ_GetJavaEnabled())
|
|
{
|
|
if (tag->type != P_PARAM)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
return(TRUE);
|
|
}
|
|
#endif /* JAVA */
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/* returns the index to the first P in the stack starting
|
|
* from the index passed in
|
|
*/
|
|
static int32
|
|
lo_find_P_tag_in_style_stack(StyleAndTagStack *style_stack, int32 index)
|
|
{
|
|
TagStruct *tag;
|
|
|
|
if(!style_stack)
|
|
return -1;
|
|
|
|
while((tag = STYLESTACK_GetTagByIndex(style_stack, index)) != NULL)
|
|
{
|
|
if(tag && !strcasecomp(tag->name, "P"))
|
|
return index;
|
|
index++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
lo_pop_paragraph_from_style_stack(lo_DocState **state,
|
|
MWContext *context,
|
|
XP_Bool is_end_tag)
|
|
{
|
|
int32 index=0;
|
|
|
|
/* the paragraph can be multiple levels down into
|
|
* the stylestack.
|
|
*
|
|
* @@@ should I only pop the paragraph or should I pop every
|
|
* thing above it exept the top one?
|
|
*/
|
|
if(!(*state)->top_state || !(*state)->top_state->style_stack)
|
|
return;
|
|
|
|
index = lo_find_P_tag_in_style_stack((*state)->top_state->style_stack, 0);
|
|
|
|
if(index < 0)
|
|
{
|
|
/* we must have mistakenly popped the paragraph during this
|
|
* end tag since the paragraph was at the top of the stack
|
|
* when this end tag was encountered
|
|
*
|
|
* so we need to pop the top tag
|
|
*/
|
|
if(is_end_tag)
|
|
LO_PopStyleTagByIndex(context, state, P_PARAGRAPH, 0);
|
|
return;
|
|
}
|
|
|
|
if(index == 0)
|
|
{
|
|
/* we can only pop the top P tag if a close tag is
|
|
* causing us to pop. Pop the next previous P tag
|
|
*/
|
|
if(!is_end_tag)
|
|
{
|
|
index = lo_find_P_tag_in_style_stack((*state)->top_state->style_stack, 1);
|
|
|
|
if(index < 0)
|
|
return;
|
|
}
|
|
|
|
#ifndef M12N /* XXXM12N Fix me, this has gone away. Sets
|
|
cx->colorSpace->install_colormap_forbidden*/
|
|
IL_UseDefaultColormapThisPage(context);
|
|
#endif /* M12N */
|
|
}
|
|
|
|
LO_PopStyleTagByIndex(context, state, P_PARAGRAPH, index);
|
|
}
|
|
|
|
void
|
|
lo_ProcessSuperElement(MWContext *context,
|
|
lo_DocState *state,
|
|
LO_SuperStruct *super)
|
|
{
|
|
if (super->is_end == FALSE)
|
|
{
|
|
state->baseline -=
|
|
super->baseline_adj;
|
|
}
|
|
else
|
|
{
|
|
state->baseline +=
|
|
super->baseline_adj;
|
|
}
|
|
}
|
|
|
|
void
|
|
lo_ProcessSubElement(MWContext *context,
|
|
lo_DocState *state,
|
|
LO_SubStruct *sub)
|
|
{
|
|
if (sub->is_end == FALSE)
|
|
{
|
|
state->baseline +=
|
|
sub->baseline_adj;
|
|
}
|
|
else
|
|
{
|
|
state->baseline -=
|
|
sub->baseline_adj;
|
|
}
|
|
}
|
|
|
|
void
|
|
lo_ProcessParagraphElement(MWContext *context,
|
|
lo_DocState **state,
|
|
LO_ParagraphStruct *paragraph,
|
|
XP_Bool in_relayout)
|
|
{
|
|
if (!paragraph->is_end)
|
|
{
|
|
if (paragraph->alignment_set)
|
|
lo_PushAlignment(*state, P_PARAGRAPH, paragraph->alignment);
|
|
|
|
(*state)->in_paragraph = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (paragraph->implicit_end)
|
|
lo_pop_paragraph_from_style_stack(state, context, !paragraph->implicit_end);
|
|
|
|
(*state)->in_paragraph = FALSE;
|
|
|
|
if (((*state)->align_stack != NULL)&&
|
|
((*state)->align_stack->type == P_PARAGRAPH))
|
|
{
|
|
lo_AlignStack *aptr;
|
|
|
|
lo_SetLineBreakState(context, *state, LO_LINEFEED_BREAK_SOFT,
|
|
LO_CLEAR_NONE, 1, !in_relayout);
|
|
|
|
aptr = lo_PopAlignment(*state);
|
|
if (aptr != NULL)
|
|
{
|
|
XP_DELETE(aptr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
lo_OpenParagraph(MWContext *context, lo_DocState **state, PA_Tag *tag, intn blank_lines)
|
|
{
|
|
PA_Block buff;
|
|
char *str;
|
|
LO_ParagraphStruct *paragraph;
|
|
|
|
paragraph = (LO_ParagraphStruct*)lo_NewElement(context, *state, LO_PARAGRAPH, NULL, 0);
|
|
|
|
XP_ASSERT(paragraph);
|
|
if (!paragraph) return;
|
|
|
|
paragraph->lo_any.type = LO_PARAGRAPH;
|
|
|
|
/*
|
|
* Need to initialize these because implicit_end gets
|
|
* checked in lo_ProcessParagraphElement and pops the
|
|
* style stack for an open paragraph tag which causes
|
|
* a crash in lo_LayoutTags
|
|
*/
|
|
paragraph->is_end = FALSE;
|
|
paragraph->implicit_end = FALSE;
|
|
paragraph->alignment_set = FALSE;
|
|
paragraph->blank_lines = blank_lines;
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_ALIGN);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff);
|
|
paragraph->alignment_set = TRUE;
|
|
paragraph->alignment =
|
|
(int32)lo_EvalDivisionAlignParam(str);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
lo_SetLineBreakState(context, *state, FALSE,
|
|
LO_LINEFEED_BREAK_PARAGRAPH, paragraph->blank_lines, FALSE);
|
|
|
|
paragraph->lo_any.x = (*state)->x;
|
|
paragraph->lo_any.y = (*state)->y;
|
|
paragraph->lo_any.x_offset = 0;
|
|
paragraph->lo_any.y_offset = 0;
|
|
paragraph->lo_any.width = 0;
|
|
paragraph->lo_any.height = 0;
|
|
paragraph->lo_any.line_height = 0;
|
|
paragraph->lo_any.ele_id = (*state)->top_state->element_id++; /*NEXT_ELEMENT doesn't work with *state */
|
|
|
|
lo_AppendToLineList(context, *state, (LO_Element*)paragraph, 0);
|
|
|
|
lo_ProcessParagraphElement(context, state, paragraph, FALSE);
|
|
}
|
|
|
|
void
|
|
lo_CloseParagraph(MWContext *context, lo_DocState **state, PA_Tag *tag, intn blank_lines)
|
|
{
|
|
/*
|
|
* If we are want to close a paragraph we are out of
|
|
* the HEAD section of the HTML and into the BODY
|
|
*
|
|
* With the exception of TITLE which can be in head, but tries to
|
|
* close paragraphs in case it was erroneously placed within body.
|
|
* This is taken care of in lo_process_title_tag()
|
|
*/
|
|
(*state)->top_state->in_head = FALSE;
|
|
(*state)->top_state->in_body = TRUE;
|
|
|
|
if ((*state)->in_paragraph != FALSE)
|
|
{
|
|
LO_ParagraphStruct *paragraph;
|
|
|
|
paragraph = (LO_ParagraphStruct*)lo_NewElement(context, *state, LO_PARAGRAPH, NULL, 0);
|
|
XP_ASSERT(paragraph);
|
|
if (!paragraph) return;
|
|
|
|
paragraph->lo_any.type = LO_PARAGRAPH;
|
|
/* don't pop the style stack if this is an end paragraph tag
|
|
* since we already did it
|
|
*/
|
|
paragraph->implicit_end = (tag && !(tag->type == P_PARAGRAPH && tag->is_end));
|
|
|
|
paragraph->blank_lines = blank_lines;
|
|
paragraph->is_end = TRUE;
|
|
lo_SetLineBreakState(context, *state, FALSE,
|
|
LO_LINEFEED_BREAK_PARAGRAPH, paragraph->blank_lines, FALSE);
|
|
|
|
paragraph->lo_any.x = (*state)->x;
|
|
paragraph->lo_any.y = (*state)->y;
|
|
paragraph->lo_any.x_offset = 0;
|
|
paragraph->lo_any.y_offset = 0;
|
|
paragraph->lo_any.width = 0;
|
|
paragraph->lo_any.height = 0;
|
|
paragraph->lo_any.line_height = 0;
|
|
paragraph->lo_any.ele_id = (*state)->top_state->element_id++; /*NEXT_ELEMENT doesn't work with *state */
|
|
|
|
lo_AppendToLineList(context, *state, (LO_Element*)paragraph, 0);
|
|
|
|
lo_ProcessParagraphElement(context, state, paragraph, FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
lo_OpenHeader(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
PA_Block buff;
|
|
char *str;
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_ALIGN);
|
|
if (buff != NULL)
|
|
{
|
|
int32 alignment;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
alignment = (int32)lo_EvalDivisionAlignParam(str);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
|
|
lo_PushAlignment(state, tag->type, alignment);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
lo_CloseHeader(MWContext *context, lo_DocState *state)
|
|
{
|
|
Bool aligned_header;
|
|
|
|
/*
|
|
* Check the top of the alignment stack to see if we are closing
|
|
* a header that explicitly set an alignment.
|
|
*/
|
|
aligned_header = FALSE;
|
|
if (state->align_stack != NULL)
|
|
{
|
|
intn type;
|
|
|
|
type = state->align_stack->type;
|
|
if ((type == P_HEADER_1)||(type == P_HEADER_2)||
|
|
(type == P_HEADER_3)||(type == P_HEADER_4)||
|
|
(type == P_HEADER_5)||(type == P_HEADER_6))
|
|
{
|
|
aligned_header = TRUE;
|
|
}
|
|
}
|
|
|
|
lo_SetLineBreakState ( context, state, FALSE, LO_LINEFEED_BREAK_HARD, 1, FALSE);
|
|
|
|
if (aligned_header != FALSE)
|
|
{
|
|
lo_AlignStack *aptr;
|
|
|
|
aptr = lo_PopAlignment(state);
|
|
if (aptr != NULL)
|
|
{
|
|
XP_DELETE(aptr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now that we are on the line after the header, we
|
|
* don't want the next blank line to be the height of the probably
|
|
* large header, so reset it to the default here.
|
|
*/
|
|
state->line_height = state->default_line_height;
|
|
}
|
|
|
|
static void
|
|
lo_process_text_tag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
char *tptr;
|
|
char *tptr2;
|
|
|
|
/*
|
|
* This is normal text, formatted or preformatted.
|
|
*/
|
|
if ((state->text_divert == P_UNKNOWN)||(state->text_divert == P_TEXT))
|
|
{
|
|
/*
|
|
* If you don't have a current text element
|
|
* to add this text to, make one here.
|
|
*/
|
|
if (state->cur_ele_type != LO_TEXT)
|
|
{
|
|
lo_FreshText(state);
|
|
state->cur_ele_type = LO_TEXT;
|
|
}
|
|
|
|
PA_LOCK (tptr, char *, tag->data);
|
|
|
|
/*
|
|
* If we are processing non-head-data text, and it is
|
|
* not just white space, then we are out of
|
|
* the HEAD section of the HTML.
|
|
*
|
|
* If the text is inside an unknown element in the HEAD
|
|
* we can assume this is content we should ignore.
|
|
*/
|
|
if (state->top_state->in_head != FALSE)
|
|
{
|
|
char *cptr;
|
|
Bool real_text;
|
|
|
|
real_text = FALSE;
|
|
cptr = tptr;
|
|
while (*cptr != '\0')
|
|
{
|
|
if (!XP_IS_SPACE(*cptr))
|
|
{
|
|
real_text = TRUE;
|
|
break;
|
|
}
|
|
cptr++;
|
|
}
|
|
if (real_text != FALSE)
|
|
{
|
|
if (state->top_state->unknown_head_tag != NULL)
|
|
{
|
|
#ifdef DEBUG
|
|
XP_TRACE(("Toss out content in unknown HEAD (%s)\n", tptr));
|
|
#endif /* DEBUG */
|
|
return;
|
|
}
|
|
/*
|
|
* If the content isnot ignore, we have left
|
|
* the HEAD and are in the BODY.
|
|
*/
|
|
state->top_state->in_head = FALSE;
|
|
state->top_state->in_body = TRUE;
|
|
}
|
|
}
|
|
|
|
if (state->preformatted != PRE_TEXT_NO)
|
|
{
|
|
lo_PreformatedText(context, state, tptr);
|
|
}
|
|
else
|
|
{
|
|
lo_FormatText(context, state, tptr);
|
|
}
|
|
PA_UNLOCK(tag->data);
|
|
}
|
|
/*
|
|
* These tags just capture the text flow as is.
|
|
*/
|
|
else if ((state->text_divert == P_TITLE)||
|
|
(state->text_divert == P_TEXTAREA)||
|
|
(state->text_divert == P_OPTION)||
|
|
(state->text_divert == P_CERTIFICATE)||
|
|
(state->text_divert == P_SCRIPT) ||
|
|
(state->text_divert == P_STYLE))
|
|
{
|
|
int32 copy_len;
|
|
|
|
if ((state->line_buf_len + tag->data_len + 1) >
|
|
state->line_buf_size)
|
|
{
|
|
int32 new_size;
|
|
|
|
new_size = state->line_buf_size +
|
|
tag->data_len + LINE_BUF_INC;
|
|
#ifdef XP_WIN16
|
|
if (new_size > SIZE_LIMIT)
|
|
{
|
|
new_size = SIZE_LIMIT;
|
|
}
|
|
#endif /* XP_WIN16 */
|
|
state->line_buf = PA_REALLOC(state->line_buf, new_size);
|
|
if (state->line_buf == NULL)
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
return;
|
|
}
|
|
state->line_buf_size = new_size;
|
|
}
|
|
|
|
copy_len = tag->data_len;
|
|
#ifdef XP_WIN16
|
|
if ((state->line_buf_len + copy_len) >
|
|
(state->line_buf_size - 1))
|
|
{
|
|
copy_len = state->line_buf_size - 1 -
|
|
state->line_buf_len;
|
|
}
|
|
#endif /* XP_WIN16 */
|
|
|
|
PA_LOCK(tptr, char *, tag->data);
|
|
PA_LOCK(tptr2, char *, state->line_buf);
|
|
XP_BCOPY(tptr, (char *)(tptr2 + state->line_buf_len),
|
|
(copy_len + 1));
|
|
state->line_buf_len += copy_len;
|
|
#ifdef XP_WIN16
|
|
tptr2[state->line_buf_len] = '\0';
|
|
#endif /* XP_WIN16 */
|
|
PA_UNLOCK(state->line_buf);
|
|
PA_UNLOCK(tag->data);
|
|
}
|
|
else /* Just ignore this text, it shouldn't be here */
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
lo_process_title_tag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
Bool tmp_in_head;
|
|
Bool tmp_in_body;
|
|
|
|
/*
|
|
* Don't let closing a paragraph before a title change the
|
|
* in_head or in_body state.
|
|
*/
|
|
tmp_in_head = state->top_state->in_head;
|
|
tmp_in_body = state->top_state->in_body;
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
state->top_state->in_head = tmp_in_head;
|
|
state->top_state->in_body = tmp_in_body;
|
|
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
/*
|
|
* Flush the line buffer so we can
|
|
* start storing the title text there.
|
|
*/
|
|
lo_FlushTextBlock(context, state);
|
|
|
|
state->line_buf_len = 0;
|
|
state->text_divert = tag->type;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Only if we captured some title text
|
|
*/
|
|
if (state->line_buf_len != 0)
|
|
{
|
|
int32 len;
|
|
char *tptr2;
|
|
|
|
PA_LOCK(tptr2, char *, state->line_buf);
|
|
/*
|
|
* Title text is cleaned up before passing
|
|
* to the front-end.
|
|
*/
|
|
len = lo_CleanTextWhitespace(tptr2, state->line_buf_len);
|
|
/*
|
|
* Subdocs can't have titles.
|
|
*/
|
|
if ((state->is_a_subdoc == SUBDOC_NOT)&&
|
|
(state->top_state->have_title == FALSE))
|
|
{
|
|
int16 charset;
|
|
|
|
charset = state->font_stack->text_attr->charset;
|
|
tptr2 = FE_TranslateISOText(context, charset, tptr2);
|
|
#ifdef MAXTITLESIZE
|
|
if ((XP_STRLEN(tptr2) > MAXTITLESIZE)&&
|
|
(MAXTITLESIZE >= 0))
|
|
{
|
|
tptr2[MAXTITLESIZE] = '\0';
|
|
}
|
|
#endif
|
|
FE_SetDocTitle(context, tptr2);
|
|
state->top_state->have_title = TRUE;
|
|
}
|
|
PA_UNLOCK(state->line_buf);
|
|
}
|
|
state->line_buf_len = 0;
|
|
state->text_divert = P_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
void
|
|
lo_ProcessCenterElement(MWContext *context, lo_DocState **state, LO_CenterStruct *center,
|
|
XP_Bool in_relayout)
|
|
{
|
|
if ((*state)->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, state, NULL, 2);
|
|
}
|
|
|
|
lo_SetLineBreakState(context, *state, LO_LINEFEED_BREAK_SOFT,
|
|
LO_CLEAR_NONE, 1, in_relayout);
|
|
|
|
if (center->is_end == FALSE)
|
|
{
|
|
lo_PushAlignment(*state, P_CENTER, LO_ALIGN_CENTER);
|
|
}
|
|
else
|
|
{
|
|
lo_AlignStack *aptr;
|
|
aptr = lo_PopAlignment(*state);
|
|
if (aptr != NULL)
|
|
{
|
|
XP_DELETE(aptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
lo_process_center_tag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
LO_CenterStruct *center;
|
|
|
|
center = (LO_CenterStruct*)lo_NewElement(context, state, LO_CENTER, NULL, 0);
|
|
|
|
center->lo_any.type = LO_CENTER;
|
|
center->is_end = tag->is_end;
|
|
|
|
center->lo_any.ele_id = NEXT_ELEMENT;
|
|
|
|
center->lo_any.x = state->x;
|
|
center->lo_any.y = state->y;
|
|
center->lo_any.x_offset = 0;
|
|
center->lo_any.y_offset = 0;
|
|
center->lo_any.width = 0;
|
|
center->lo_any.height = 0;
|
|
center->lo_any.line_height = 0;
|
|
|
|
lo_AppendToLineList(context, state, (LO_Element*)center, 0);
|
|
|
|
lo_ProcessCenterElement(context, &state, center, FALSE);
|
|
}
|
|
|
|
void
|
|
lo_ProcessMulticolumnElement(MWContext *context,
|
|
lo_DocState **state,
|
|
LO_MulticolumnStruct *multicolumn)
|
|
{
|
|
/*
|
|
* The start of a multicol infers the end of
|
|
* the HEAD section of the HTML and starts the BODY
|
|
*/
|
|
(*state)->top_state->in_head = FALSE;
|
|
(*state)->top_state->in_body = TRUE;
|
|
|
|
|
|
|
|
if (multicolumn->is_end == FALSE)
|
|
{
|
|
lo_AppendMultiColToLineList( context, (*state), multicolumn);
|
|
lo_BeginMulticolumn(context, (*state), multicolumn->tag, multicolumn);
|
|
}
|
|
else
|
|
{
|
|
if ((*state)->current_multicol != NULL)
|
|
{
|
|
lo_EndMulticolumn(context, (*state), multicolumn->tag,
|
|
(*state)->current_multicol, FALSE);
|
|
}
|
|
lo_AppendMultiColToLineList( context, (*state), multicolumn);
|
|
}
|
|
}
|
|
|
|
void
|
|
lo_ProcessDescTitleElement(MWContext *context,
|
|
lo_DocState *state,
|
|
LO_DescTitleStruct *title,
|
|
XP_Bool in_relayout)
|
|
{
|
|
/*
|
|
* Undent to the left for a title if necessary.
|
|
*/
|
|
if ((state->list_stack->level != 0)&&
|
|
(state->list_stack->type == P_DESC_LIST)&&
|
|
(state->list_stack->value > 1))
|
|
{
|
|
state->list_stack->old_left_margin -=
|
|
LIST_MARGIN_INC;
|
|
state->list_stack->value = 1;
|
|
}
|
|
|
|
if (state->linefeed_state >= 1)
|
|
{
|
|
lo_FindLineMargins(context, state, TRUE);
|
|
}
|
|
else
|
|
{
|
|
lo_SetLineBreakState(context, state, LO_LINEFEED_BREAK_SOFT,
|
|
LO_CLEAR_NONE, 1, in_relayout);
|
|
}
|
|
state->x = state->left_margin;
|
|
}
|
|
|
|
void
|
|
lo_ProcessDescTextElement(MWContext *context,
|
|
lo_DocState *state,
|
|
LO_DescTextStruct *text,
|
|
XP_Bool in_relayout)
|
|
{
|
|
/*
|
|
* Indent from the title if necessary.
|
|
*/
|
|
if ((state->list_stack->type == P_DESC_LIST)&&
|
|
(state->list_stack->value == 1))
|
|
{
|
|
state->list_stack->old_left_margin +=
|
|
LIST_MARGIN_INC;
|
|
state->list_stack->value = 2;
|
|
}
|
|
|
|
if (state->list_stack->compact == FALSE)
|
|
{
|
|
if (state->linefeed_state >= 1)
|
|
{
|
|
lo_FindLineMargins(context, state, TRUE);
|
|
}
|
|
else
|
|
{
|
|
lo_SetLineBreakState(context, state, LO_LINEFEED_BREAK_SOFT,
|
|
LO_CLEAR_NONE, 1, !in_relayout);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (state->x >=
|
|
state->list_stack->old_left_margin)
|
|
{
|
|
if (state->linefeed_state >= 1)
|
|
{
|
|
lo_FindLineMargins(context,
|
|
state, TRUE);
|
|
}
|
|
else
|
|
{
|
|
lo_SetLineBreakState(context, state, LO_LINEFEED_BREAK_SOFT,
|
|
LO_CLEAR_NONE, 1, !in_relayout);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lo_FindLineMargins(context,
|
|
state, TRUE);
|
|
state->x = state->left_margin;
|
|
}
|
|
}
|
|
state->x = state->left_margin;
|
|
|
|
/*
|
|
* Bug compatibility for <dd> outside a <dl>
|
|
*/
|
|
if (state->list_stack->type != P_DESC_LIST)
|
|
{
|
|
state->x += LIST_MARGIN_INC;
|
|
}
|
|
}
|
|
|
|
void
|
|
lo_ProcessBlockQuoteElement(MWContext *context,
|
|
lo_DocState *state,
|
|
LO_BlockQuoteStruct *quote,
|
|
XP_Bool in_relayout)
|
|
{
|
|
if (quote->is_end == FALSE)
|
|
{
|
|
lo_SetLineBreakState(context, state, LO_LINEFEED_BREAK_SOFT,
|
|
LO_CLEAR_NONE, 2, !in_relayout);
|
|
|
|
/* THIS BREAKS FLOATING IMAGES IN BLOCKQUOTES
|
|
state->list_stack->old_left_margin =
|
|
state->left_margin;
|
|
state->list_stack->old_right_margin =
|
|
state->right_margin;
|
|
*/
|
|
lo_PushList(state, quote->tag, quote->quote_type);
|
|
if (quote->quote_type == QUOTE_MQUOTE)
|
|
{
|
|
state->left_margin += MQUOTE_MARGIN_INC;
|
|
state->right_margin -= MQUOTE_MARGIN_INC;
|
|
}
|
|
else if ((quote->quote_type == QUOTE_JWZ)||
|
|
(quote->quote_type == QUOTE_CITE))
|
|
{
|
|
state->left_margin += MQUOTE_MARGIN_INC;
|
|
}
|
|
else
|
|
{
|
|
state->left_margin += LIST_MARGIN_INC;
|
|
state->right_margin -= LIST_MARGIN_INC;
|
|
}
|
|
state->x = state->left_margin;
|
|
state->list_stack->old_left_margin =
|
|
state->left_margin;
|
|
state->list_stack->old_right_margin =
|
|
state->right_margin;
|
|
|
|
if ((quote->quote_type == QUOTE_JWZ)||
|
|
(quote->quote_type == QUOTE_CITE))
|
|
{
|
|
lo_PlaceQuoteMarker(context, state,
|
|
state->list_stack);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lo_ListStack *lptr;
|
|
int8 quote_type;
|
|
Bool mquote = FALSE;
|
|
int32 mquote_line_num = 0;
|
|
int32 mquote_x = 0;
|
|
|
|
quote_type = QUOTE_NONE;
|
|
lptr = lo_PopList(state, quote->tag);
|
|
if (lptr != NULL)
|
|
{
|
|
quote_type = lptr->quote_type;
|
|
if (lptr->quote_type == QUOTE_MQUOTE) {
|
|
mquote = TRUE;
|
|
}
|
|
mquote_line_num = lptr->mquote_line_num;
|
|
mquote_x = lptr->mquote_x;
|
|
XP_DELETE(lptr);
|
|
}
|
|
state->left_margin =
|
|
state->list_stack->old_left_margin;
|
|
state->right_margin =
|
|
state->list_stack->old_right_margin;
|
|
|
|
/*
|
|
* Reset the margins properly in case
|
|
* there is a right-aligned image here.
|
|
*/
|
|
lo_FindLineMargins(context, state, TRUE);
|
|
if (state->linefeed_state >= 2)
|
|
{
|
|
lo_FindLineMargins(context, state, TRUE);
|
|
/*
|
|
* If we just popped a citation
|
|
* blockquote, there should be a
|
|
* bullet at the end of the
|
|
* current line list we
|
|
* need to remove.
|
|
*/
|
|
if ((quote_type == QUOTE_JWZ)||
|
|
(quote_type == QUOTE_CITE))
|
|
{
|
|
LO_Element *line_ptr;
|
|
|
|
line_ptr = state->line_list;
|
|
while ((line_ptr != NULL)&&
|
|
(line_ptr->lo_any.next != NULL))
|
|
{
|
|
line_ptr = line_ptr->lo_any.next;
|
|
}
|
|
if (line_ptr != NULL)
|
|
{
|
|
if (line_ptr->lo_any.prev != NULL)
|
|
{
|
|
line_ptr->lo_any.prev->lo_any.next = NULL;
|
|
}
|
|
if (line_ptr == state->line_list)
|
|
{
|
|
state->line_list = NULL;
|
|
}
|
|
lo_FreeElement(context,
|
|
line_ptr,
|
|
FALSE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lo_SetLineBreakState(context, state, LO_LINEFEED_BREAK_SOFT,
|
|
LO_CLEAR_NONE, 2, !in_relayout);
|
|
}
|
|
|
|
state->x = state->left_margin;
|
|
|
|
/* Go back and add bullets to all lines between here and the
|
|
beginning of the mailing quotation. */
|
|
if (mquote)
|
|
{
|
|
lo_add_leading_bullets(context,state,
|
|
mquote_line_num - 1,
|
|
state->line_num - state->linefeed_state - 1,
|
|
mquote_x);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
lo_process_paragraph_tag(MWContext *context, lo_DocState *state, PA_Tag *tag,
|
|
StyleStruct *style_struct)
|
|
{
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
intn blank_lines=2;
|
|
char *property;
|
|
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
|
|
if(style_struct
|
|
&& (property = STYLESTRUCT_GetString(style_struct,
|
|
TOPMARGIN_STYLE)) != NULL)
|
|
{
|
|
/* there is a top margin for this paragraph,
|
|
* so don't add two CR's just one
|
|
*/
|
|
blank_lines = 1;
|
|
XP_FREE(property);
|
|
}
|
|
|
|
lo_OpenParagraph(context, &state, tag, blank_lines);
|
|
}
|
|
else
|
|
{
|
|
char *property;
|
|
intn blank_lines=2;
|
|
|
|
if(style_struct
|
|
&& (property = STYLESTRUCT_GetString(style_struct,
|
|
BOTTOMMARGIN_STYLE)) != NULL)
|
|
{
|
|
/* there is a bottom margin for this paragraph,
|
|
* so don't add two CR's just one
|
|
*/
|
|
blank_lines = 1;
|
|
XP_FREE(property);
|
|
}
|
|
lo_CloseParagraph(context, &state, tag, blank_lines);
|
|
}
|
|
}
|
|
|
|
static void
|
|
lo_process_multicolumn_tag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
LO_MulticolumnStruct *multicolumn;
|
|
|
|
multicolumn = (LO_MulticolumnStruct*)lo_NewElement(context, state, LO_MULTICOLUMN, NULL, 0);
|
|
XP_ASSERT(multicolumn);
|
|
if (!multicolumn) return;
|
|
|
|
multicolumn->lo_any.type = LO_MULTICOLUMN;
|
|
/* multicolumn->lo_any.ele_id = NEXT_ELEMENT; */
|
|
multicolumn->is_end = tag->is_end;
|
|
multicolumn->tag = PA_CloneMDLTag(tag);
|
|
multicolumn->multicol = NULL;
|
|
|
|
/*
|
|
multicolumn->lo_any.x = state->x;
|
|
multicolumn->lo_any.y = state->y;
|
|
*/
|
|
multicolumn->lo_any.x_offset = 0;
|
|
multicolumn->lo_any.y_offset = 0;
|
|
multicolumn->lo_any.width = 0;
|
|
multicolumn->lo_any.height = 0;
|
|
multicolumn->lo_any.line_height = 0;
|
|
|
|
/* lo_AppendToLineList(context, state, (LO_Element*)multicolumn, 0); */
|
|
|
|
lo_ProcessMulticolumnElement(context, &state, multicolumn);
|
|
}
|
|
|
|
static void
|
|
lo_process_header_tag(MWContext *context, lo_DocState *state, PA_Tag *tag, int text_attr_size)
|
|
{
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
LO_TextAttr *old_attr;
|
|
LO_TextAttr *attr;
|
|
LO_TextAttr tmp_attr;
|
|
|
|
/* lo_SetSoftLineBreakState(context, state, FALSE, 2); */
|
|
lo_SetLineBreakState ( context, state, FALSE, LO_LINEFEED_BREAK_HARD, 2, FALSE);
|
|
lo_OpenHeader(context, state, tag);
|
|
|
|
old_attr = state->font_stack->text_attr;
|
|
lo_CopyTextAttr(old_attr, &tmp_attr);
|
|
tmp_attr.fontmask |= LO_FONT_BOLD;
|
|
tmp_attr.fontmask &= (~(LO_FONT_FIXED|LO_FONT_ITALIC));
|
|
tmp_attr.size = text_attr_size;
|
|
attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
|
|
lo_PushFont(state, tag->type, attr);
|
|
}
|
|
else
|
|
{
|
|
LO_TextAttr *attr;
|
|
|
|
attr = lo_PopFont(state, tag->type);
|
|
|
|
lo_CloseHeader(context, state);
|
|
/* lo_SetSoftLineBreakState(context, state, FALSE, 2); */
|
|
lo_SetLineBreakState ( context, state, FALSE, LO_LINEFEED_BREAK_HARD, 2, FALSE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
lo_process_span_tag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
LO_SpanStruct *span;
|
|
PA_Block buff;
|
|
lo_DocLists *doc_lists;
|
|
|
|
doc_lists = lo_GetCurrentDocLists(state);
|
|
|
|
span = (LO_SpanStruct*)lo_NewElement(context, state, LO_SPAN, NULL, 0);
|
|
XP_ASSERT(span);
|
|
if (!span) return;
|
|
|
|
span->lo_any.type = LO_SPAN;
|
|
span->lo_any.ele_id = NEXT_ELEMENT;
|
|
span->is_end = tag->is_end;
|
|
|
|
span->lo_any.x = state->x;
|
|
span->lo_any.y = state->y;
|
|
span->lo_any.x_offset = 0;
|
|
span->lo_any.y_offset = 0;
|
|
span->lo_any.width = 0;
|
|
span->lo_any.height = 0;
|
|
span->lo_any.line_height = 0;
|
|
|
|
#ifdef DOM
|
|
span->name_rec = NULL;
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
/* get the span's ID. */
|
|
buff = lo_FetchParamValue(context, tag, PARAM_ID);
|
|
if (buff != NULL)
|
|
{
|
|
state->in_span = TRUE;
|
|
state->current_span = buff;
|
|
if (lo_SetNamedSpan(state, buff))
|
|
{
|
|
lo_BindNamedSpanToElement(state, buff, NULL);
|
|
lo_ReflectSpan(context, state, tag,
|
|
doc_lists->span_list,
|
|
lo_CurrentLayerId(state));
|
|
span->name_rec = doc_lists->span_list;
|
|
}
|
|
|
|
PA_UNLOCK(buff);
|
|
}
|
|
lo_AppendToLineList(context, state, (LO_Element*)span, 0);
|
|
state->in_span = TRUE;
|
|
}
|
|
else
|
|
{
|
|
state->in_span = FALSE;
|
|
lo_AppendToLineList(context, state, (LO_Element*)span, 0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
lo_process_div_tag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
LO_DivStruct *div;
|
|
|
|
div = (LO_DivStruct*)lo_NewElement(context, state, LO_DIV, NULL, 0);
|
|
XP_ASSERT(div);
|
|
if (!div) return;
|
|
|
|
div->lo_any.type = LO_DIV;
|
|
div->lo_any.ele_id = NEXT_ELEMENT;
|
|
div->is_end = tag->is_end;
|
|
|
|
div->lo_any.x = state->x;
|
|
div->lo_any.y = state->y;
|
|
div->lo_any.x_offset = 0;
|
|
div->lo_any.y_offset = 0;
|
|
div->lo_any.width = 0;
|
|
div->lo_any.height = 0;
|
|
div->lo_any.line_height = 0;
|
|
|
|
lo_AppendToLineList(context, state, (LO_Element*)div, 0);
|
|
}
|
|
|
|
PRIVATE Bool lo_do_underline = TRUE;
|
|
|
|
#ifdef XP_MAC
|
|
PRIVATE
|
|
#endif
|
|
int PR_CALLBACK
|
|
lo_underline_pref_callback(const char *pref_name, void *closure)
|
|
{
|
|
PREF_GetBoolPref("browser.underline_anchors", &lo_do_underline);
|
|
|
|
return PREF_NOERROR;
|
|
}
|
|
|
|
PRIVATE
|
|
Bool
|
|
lo_underline_anchors()
|
|
{
|
|
static Bool first_time = TRUE;
|
|
|
|
if(first_time)
|
|
{
|
|
first_time = FALSE;
|
|
PREF_GetBoolPref("browser.underline_anchors", &lo_do_underline);
|
|
PREF_RegisterCallback("browser.underline_anchors", lo_underline_pref_callback, NULL);
|
|
}
|
|
|
|
return (lo_do_underline);
|
|
}
|
|
|
|
/* if the color is currently equal to the default visited
|
|
* or unvisited link color, change the color very slightly
|
|
* to make it different. That way we can tell the difference
|
|
* between the default colors and the colors set by style sheets
|
|
*/
|
|
static void
|
|
lo_make_link_color_different_than_default(lo_DocState *state, LO_TextAttr *tmp_attr)
|
|
{
|
|
if( (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))
|
|
|| (tmp_attr->fg.red == STATE_VISITED_ANCHOR_RED(state)
|
|
&& tmp_attr->fg.green == STATE_VISITED_ANCHOR_GREEN(state)
|
|
&& tmp_attr->fg.blue == STATE_VISITED_ANCHOR_BLUE(state)))
|
|
{
|
|
/* change the color slightly */
|
|
if(tmp_attr->fg.red < 255)
|
|
tmp_attr->fg.red++;
|
|
else
|
|
tmp_attr->fg.red--;
|
|
}
|
|
}
|
|
|
|
static void
|
|
lo_process_anchor_tag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
LO_TextAttr tmp_attr;
|
|
lo_DocLists *doc_lists;
|
|
|
|
doc_lists = lo_GetCurrentDocLists(state);
|
|
|
|
/*
|
|
* Opening a new anchor
|
|
*/
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
LO_TextAttr *old_attr;
|
|
LO_TextAttr *attr;
|
|
char *url;
|
|
char *full_url;
|
|
PA_Block buff;
|
|
Bool is_new;
|
|
PA_Block anchor;
|
|
|
|
/*
|
|
* Get the NAME param for the name list
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_NAME);
|
|
if (buff != NULL)
|
|
{
|
|
char *name;
|
|
|
|
/*
|
|
* If we are looking to start at a name,
|
|
* fetch the NAME and compare.
|
|
*/
|
|
if (state->display_blocking_element_id == -1)
|
|
{
|
|
PA_LOCK(name, char *, buff);
|
|
if (XP_STRCMP(name,
|
|
(char *)(state->top_state->name_target + 1))
|
|
== 0)
|
|
{
|
|
XP_FREE(state->top_state->name_target);
|
|
state->top_state->name_target = NULL;
|
|
state->display_blocking_element_id =
|
|
state->top_state->element_id;
|
|
}
|
|
PA_UNLOCK(buff);
|
|
}
|
|
|
|
/* Add the named anchor to the name_list. */
|
|
|
|
/*
|
|
if (state->current_named_anchor != NULL)
|
|
PA_FREE(state->current_named_anchor);
|
|
*/
|
|
|
|
state->current_named_anchor = buff;
|
|
if (lo_SetNamedAnchor(state, buff))
|
|
{
|
|
lo_BindNamedAnchorToElement(state, buff, NULL);
|
|
lo_ReflectNamedAnchor(context, state, tag,
|
|
doc_lists->name_list,
|
|
lo_CurrentLayerId(state));
|
|
}
|
|
}
|
|
|
|
is_new = TRUE;
|
|
state->current_anchor = NULL;
|
|
/*
|
|
* Fetch the HREF, and turn it into an
|
|
* absolute URL.
|
|
*/
|
|
anchor = lo_FetchParamValue(context, tag, PARAM_HREF);
|
|
if (anchor != NULL)
|
|
{
|
|
char *target;
|
|
PA_Block targ_buff;
|
|
|
|
PA_LOCK(url, char *, anchor);
|
|
if (url != NULL)
|
|
{
|
|
int32 len;
|
|
|
|
len = lo_StripTextWhitespace(url, XP_STRLEN(url));
|
|
}
|
|
url = NET_MakeAbsoluteURL(state->top_state->base_url, url);
|
|
if (url == NULL)
|
|
{
|
|
buff = NULL;
|
|
}
|
|
else
|
|
{
|
|
buff = PA_ALLOC(XP_STRLEN(url)+1);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(full_url, char *, buff);
|
|
XP_STRCPY(full_url, url);
|
|
PA_UNLOCK(buff);
|
|
|
|
if (GH_CheckGlobalHistory(url) != -1)
|
|
{
|
|
is_new = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
assert (state);
|
|
#endif
|
|
state->top_state->out_of_memory = TRUE;
|
|
}
|
|
|
|
XP_FREE(url);
|
|
}
|
|
PA_UNLOCK(anchor);
|
|
PA_FREE(anchor);
|
|
anchor = buff;
|
|
|
|
if (anchor != NULL)
|
|
{
|
|
targ_buff = lo_FetchParamValue(context, tag, PARAM_TARGET);
|
|
if (targ_buff != NULL)
|
|
{
|
|
int32 len;
|
|
|
|
PA_LOCK(target, char *, targ_buff);
|
|
len = lo_StripTextWhitespace(target,
|
|
XP_STRLEN(target));
|
|
if ((*target == '\0')||
|
|
(lo_IsValidTarget(target) == FALSE))
|
|
{
|
|
PA_UNLOCK(targ_buff);
|
|
PA_FREE(targ_buff);
|
|
targ_buff = NULL;
|
|
}
|
|
else
|
|
{
|
|
PA_UNLOCK(targ_buff);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If there was no target use the default one.
|
|
* (default provided by BASE tag)
|
|
*/
|
|
if ((targ_buff == NULL)&&
|
|
(state->top_state->base_target != NULL))
|
|
{
|
|
targ_buff = PA_ALLOC(XP_STRLEN(
|
|
state->top_state->base_target) + 1);
|
|
if (targ_buff != NULL)
|
|
{
|
|
char *targ;
|
|
|
|
PA_LOCK(targ, char *, targ_buff);
|
|
XP_STRCPY(targ,
|
|
state->top_state->base_target);
|
|
PA_UNLOCK(targ_buff);
|
|
}
|
|
else
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
}
|
|
}
|
|
|
|
state->current_anchor =
|
|
lo_NewAnchor(state, anchor, targ_buff);
|
|
if (state->current_anchor == NULL)
|
|
{
|
|
PA_FREE(anchor);
|
|
if (targ_buff != NULL)
|
|
{
|
|
PA_FREE(targ_buff);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If SUPPRESS attribute is present, suppress visual feedback (dashed rectangle)
|
|
when link is selected */
|
|
buff = lo_FetchParamValue(context, tag, PARAM_SUPPRESS);
|
|
if (buff && !XP_STRCASECMP((char*)buff, "true"))
|
|
{
|
|
state->current_anchor->flags |= ANCHOR_SUPPRESS_FEEDBACK;
|
|
}
|
|
|
|
/* Look for PRE subtag and store its value in prevalue */
|
|
buff = lo_FetchParamValue(context, tag, PARAM_PRE);
|
|
if (buff)
|
|
{
|
|
char * val;
|
|
PA_LOCK(val, char *, buff);
|
|
if (val)
|
|
{
|
|
state->current_anchor->prevalue = atof(val);
|
|
XP_FREE(val);
|
|
}
|
|
PA_UNLOCK(buff);
|
|
}
|
|
/*
|
|
* Add this url's block to the list
|
|
* of all allocated urls so we can free
|
|
* it later.
|
|
*/
|
|
lo_AddToUrlList(context, state, state->current_anchor);
|
|
if (state->top_state->out_of_memory != FALSE)
|
|
{
|
|
return;
|
|
}
|
|
lo_ReflectLink(context, state, tag, state->current_anchor,
|
|
lo_CurrentLayerId(state),
|
|
doc_lists->url_list_len - 1);
|
|
}
|
|
|
|
/*
|
|
* Change the anchor attribute only for
|
|
* anchors with hrefs.
|
|
*/
|
|
if (state->current_anchor != NULL)
|
|
{
|
|
StyleStruct *style_struct = NULL;
|
|
char *property=NULL;
|
|
|
|
if (state->top_state->style_stack)
|
|
style_struct = STYLESTACK_GetStyleByIndex(state->top_state->style_stack, 0);
|
|
else
|
|
style_struct = NULL;
|
|
|
|
old_attr = state->font_stack->text_attr;
|
|
lo_CopyTextAttr(old_attr, &tmp_attr);
|
|
|
|
tmp_attr.attrmask = (old_attr->attrmask | LO_ATTR_ANCHOR);
|
|
|
|
if(style_struct)
|
|
property = STYLESTRUCT_GetString(style_struct, TEXTDECORATION_STYLE);
|
|
if(property)
|
|
XP_FREE(property); /* don't underline here since SS will do it if neccessary */
|
|
else if(lo_underline_anchors())
|
|
tmp_attr.attrmask = (old_attr->attrmask | LO_ATTR_UNDERLINE);
|
|
|
|
if (is_new != FALSE)
|
|
{
|
|
if(style_struct)
|
|
property = STYLESTRUCT_GetString(style_struct, LINK_COLOR);
|
|
if (property)
|
|
{
|
|
LO_ParseStyleSheetRGB(property, &tmp_attr.fg.red, &tmp_attr.fg.green, &tmp_attr.fg.blue);
|
|
lo_make_link_color_different_than_default(state, &tmp_attr);
|
|
XP_FREE(property);
|
|
}
|
|
else
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(style_struct)
|
|
property = STYLESTRUCT_GetString(style_struct, VISITED_COLOR);
|
|
if (property)
|
|
{
|
|
LO_ParseStyleSheetRGB(property, &tmp_attr.fg.red, &tmp_attr.fg.green, &tmp_attr.fg.blue);
|
|
lo_make_link_color_different_than_default(state, &tmp_attr);
|
|
XP_FREE(property);
|
|
}
|
|
else
|
|
{
|
|
tmp_attr.fg.red = STATE_VISITED_ANCHOR_RED(state);
|
|
tmp_attr.fg.green = STATE_VISITED_ANCHOR_GREEN(state);
|
|
tmp_attr.fg.blue = STATE_VISITED_ANCHOR_BLUE(state);
|
|
}
|
|
}
|
|
attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
lo_PushFont(state, tag->type, attr);
|
|
}
|
|
}
|
|
/*
|
|
* Closing an anchor. Anchors can't be nested, so close
|
|
* ALL anchors.
|
|
*/
|
|
else
|
|
{
|
|
lo_PopAllAnchors(state);
|
|
state->current_anchor = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
lo_process_caption_tag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
if ((state->current_table != NULL)&&
|
|
(state->current_table->caption == NULL))
|
|
{
|
|
lo_TableRec *table;
|
|
|
|
table = state->current_table;
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
/*
|
|
* If there is an unterminated row open,
|
|
* terminate it now.
|
|
*/
|
|
if ((table->row_ptr != NULL)&&
|
|
(table->row_ptr->row_done == FALSE))
|
|
{
|
|
lo_EndTableRow(context, state, table);
|
|
}
|
|
lo_BeginTableCaption(context, state, table,tag);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This never happens because the
|
|
* close caption feeds into the subdoc.
|
|
*/
|
|
}
|
|
}
|
|
else if ((state->current_table == NULL)&&
|
|
(state->is_a_subdoc == SUBDOC_CAPTION))
|
|
{
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
lo_EndTableCaption(context, state);
|
|
}
|
|
}
|
|
else if ((state->current_table == NULL)&&
|
|
(state->is_a_subdoc == SUBDOC_CELL))
|
|
{
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
lo_TopState *top_state;
|
|
lo_DocState *new_state;
|
|
int32 doc_id;
|
|
|
|
/*
|
|
* This can only happen if the close cell
|
|
* was omitted, AND the close row
|
|
* after it was omitted.
|
|
*/
|
|
lo_EndTableCell(context, state, FALSE);
|
|
|
|
/*
|
|
* restore state to the new lowest level.
|
|
*/
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
new_state = lo_TopSubState(top_state);
|
|
|
|
/*
|
|
* Now act just like a new caption has started.
|
|
*/
|
|
if (new_state->current_table != NULL)
|
|
{
|
|
lo_TableRec *table;
|
|
|
|
table = new_state->current_table;
|
|
/*
|
|
* If there is an unterminated row open,
|
|
* terminate it now.
|
|
*/
|
|
if ((table->row_ptr != NULL)&&
|
|
(table->row_ptr->row_done == FALSE))
|
|
{
|
|
lo_EndTableRow(context,new_state,table);
|
|
}
|
|
lo_BeginTableCaption(context, new_state,
|
|
table, tag);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
lo_process_table_cell_tag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
if ((state->current_table != NULL)&&
|
|
(state->current_table->row_ptr != NULL)&&
|
|
(state->current_table->row_ptr->row_done == FALSE))
|
|
{
|
|
lo_TableRec *table;
|
|
Bool is_a_header;
|
|
|
|
if (tag->type == P_TABLE_HEADER)
|
|
{
|
|
is_a_header = TRUE;
|
|
}
|
|
else
|
|
{
|
|
is_a_header = FALSE;
|
|
}
|
|
|
|
table = state->current_table;
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
lo_BeginTableCell(context, state, table, tag, is_a_header);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This never happens because the
|
|
* close cell feeds into the subdoc.
|
|
*/
|
|
}
|
|
}
|
|
else if ((state->current_table != NULL)&&
|
|
((state->current_table->row_ptr == NULL)||
|
|
(state->current_table->row_ptr->row_done != FALSE)))
|
|
{
|
|
lo_TableRec *table;
|
|
Bool is_a_header;
|
|
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
table = state->current_table;
|
|
/*
|
|
* This is a starting data cell, with no open
|
|
* row. Open up the row for them.
|
|
*/
|
|
lo_BeginTableRow(context, state, table, tag);
|
|
|
|
if (tag->type == P_TABLE_HEADER)
|
|
{
|
|
is_a_header = TRUE;
|
|
}
|
|
else
|
|
{
|
|
is_a_header = FALSE;
|
|
}
|
|
|
|
lo_BeginTableCell(context, state, table, tag, is_a_header);
|
|
}
|
|
}
|
|
else if ((state->current_table == NULL)&&
|
|
(state->is_a_subdoc == SUBDOC_CELL))
|
|
{
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
lo_TopState *top_state;
|
|
lo_DocState *new_state;
|
|
int32 doc_id;
|
|
|
|
/*
|
|
* This can only happen if the close cell
|
|
* was omitted.
|
|
*/
|
|
lo_EndTableCell(context, state, FALSE);
|
|
|
|
/*
|
|
* restore state to the new lowest level.
|
|
*/
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
new_state = lo_TopSubState(top_state);
|
|
|
|
/*
|
|
* Now act just like a new cell has started
|
|
* as above.
|
|
*/
|
|
if ((new_state->current_table != NULL)&&
|
|
(new_state->current_table->row_ptr != NULL)&&
|
|
(new_state->current_table->row_ptr->row_done == FALSE))
|
|
{
|
|
lo_TableRec *table;
|
|
Bool is_a_header;
|
|
|
|
if (tag->type == P_TABLE_HEADER)
|
|
{
|
|
is_a_header = TRUE;
|
|
}
|
|
else
|
|
{
|
|
is_a_header = FALSE;
|
|
}
|
|
|
|
table = new_state->current_table;
|
|
lo_BeginTableCell(context, new_state, table, tag,
|
|
is_a_header);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lo_EndTableCell(context, state, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
lo_process_table_row_tag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
if (state->current_table != NULL)
|
|
{
|
|
lo_TableRec *table;
|
|
|
|
table = state->current_table;
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
/*
|
|
* If there is an unterminated row open,
|
|
* terminate it now.
|
|
*/
|
|
if ((table->row_ptr != NULL)&&
|
|
(table->row_ptr->row_done == FALSE))
|
|
{
|
|
lo_EndTableRow(context, state, table);
|
|
}
|
|
lo_BeginTableRow(context, state, table, tag);
|
|
}
|
|
else
|
|
{
|
|
if ((table->row_ptr != NULL)&&
|
|
(table->row_ptr->row_done == FALSE))
|
|
{
|
|
lo_EndTableRow(context, state, table);
|
|
}
|
|
}
|
|
}
|
|
else if ((state->current_table == NULL)&&
|
|
(state->is_a_subdoc == SUBDOC_CELL))
|
|
{
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
lo_TopState *top_state;
|
|
lo_DocState *new_state;
|
|
int32 doc_id;
|
|
|
|
/*
|
|
* This can only happen if the close cell
|
|
* was omitted, AND the close row
|
|
* after it was omitted.
|
|
*/
|
|
lo_EndTableCell(context, state, FALSE);
|
|
|
|
/*
|
|
* restore state to the new lowest level.
|
|
*/
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
new_state = lo_TopSubState(top_state);
|
|
|
|
/*
|
|
* Now act just like a new row has started.
|
|
*/
|
|
if (new_state->current_table != NULL)
|
|
{
|
|
lo_TableRec *table;
|
|
|
|
table = new_state->current_table;
|
|
/*
|
|
* If there is an unterminated row open,
|
|
* terminate it now.
|
|
*/
|
|
if ((table->row_ptr != NULL)&&
|
|
(table->row_ptr->row_done == FALSE))
|
|
{
|
|
lo_EndTableRow(context, new_state, table);
|
|
}
|
|
lo_BeginTableRow(context, new_state, table, tag);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* This can only happen if the close cell
|
|
* was omitted.
|
|
*/
|
|
lo_EndTableCell(context, state, FALSE);
|
|
|
|
/*
|
|
* We should really go on to repeat all the code
|
|
* here to process the end row tag, but since
|
|
* we know our layout can recover fine without
|
|
* it, we don't bother.
|
|
*/
|
|
}
|
|
}
|
|
else if ((state->current_table == NULL)&&
|
|
(state->is_a_subdoc == SUBDOC_CAPTION))
|
|
{
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
lo_TopState *top_state;
|
|
lo_DocState *new_state;
|
|
int32 doc_id;
|
|
|
|
/*
|
|
* This can only happen if the close caption
|
|
* was omitted.
|
|
*/
|
|
lo_EndTableCaption(context, state);
|
|
|
|
/*
|
|
* restore state to the new lowest level.
|
|
*/
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
new_state = lo_TopSubState(top_state);
|
|
|
|
/*
|
|
* Now act just like a new row has started.
|
|
*/
|
|
if (new_state->current_table != NULL)
|
|
{
|
|
lo_TableRec *table;
|
|
|
|
table = new_state->current_table;
|
|
/*
|
|
* If there is an unterminated row open,
|
|
* terminate it now.
|
|
*/
|
|
if ((table->row_ptr != NULL)&&
|
|
(table->row_ptr->row_done == FALSE))
|
|
{
|
|
lo_EndTableRow(context, new_state, table);
|
|
}
|
|
lo_BeginTableRow(context, new_state, table, tag);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
MODULE_PRIVATE void
|
|
lo_CloseTable(MWContext *context, lo_DocState *state)
|
|
{
|
|
if (state->current_table != NULL)
|
|
{
|
|
lo_TableRec *table;
|
|
|
|
table = state->current_table;
|
|
/*
|
|
* If there is an unterminated row open,
|
|
* terminate it now.
|
|
*/
|
|
if ((table->row_ptr != NULL)&&
|
|
(table->row_ptr->row_done == FALSE))
|
|
{
|
|
lo_EndTableRow(context, state, table);
|
|
}
|
|
lo_EndTable(context, state, state->current_table, FALSE);
|
|
}
|
|
else if ((state->current_table == NULL)&&
|
|
(state->is_a_subdoc == SUBDOC_CELL))
|
|
{
|
|
lo_TopState *top_state;
|
|
lo_DocState *new_state;
|
|
int32 doc_id;
|
|
|
|
/*
|
|
* This can only happen if the close cell
|
|
* was omitted, AND the close row
|
|
* after it was omitted.
|
|
*/
|
|
lo_EndTableCell(context, state, FALSE);
|
|
|
|
/*
|
|
* restore state to the new lowest level.
|
|
*/
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
new_state = lo_TopSubState(top_state);
|
|
|
|
/*
|
|
* Now act like a close table with
|
|
* an omitted close row.
|
|
*/
|
|
if (new_state->current_table != NULL)
|
|
{
|
|
lo_TableRec *table;
|
|
|
|
table = new_state->current_table;
|
|
/*
|
|
* If there is an unterminated row open,
|
|
* terminate it now.
|
|
*/
|
|
if ((table->row_ptr != NULL)&&
|
|
(table->row_ptr->row_done == FALSE))
|
|
{
|
|
lo_EndTableRow(context, new_state, table);
|
|
}
|
|
lo_EndTable(context, new_state,
|
|
new_state->current_table, FALSE);
|
|
}
|
|
}
|
|
else if ((state->current_table == NULL)&&
|
|
(state->is_a_subdoc == SUBDOC_CAPTION))
|
|
{
|
|
lo_TopState *top_state;
|
|
lo_DocState *new_state;
|
|
int32 doc_id;
|
|
|
|
/*
|
|
* This can only happen if the close
|
|
* caption was omitted.
|
|
*/
|
|
lo_EndTableCaption(context, state);
|
|
|
|
/*
|
|
* restore state to the new lowest level.
|
|
*/
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
new_state = lo_TopSubState(top_state);
|
|
|
|
/*
|
|
* Now act like a close table.
|
|
*/
|
|
if (new_state->current_table != NULL)
|
|
{
|
|
lo_TableRec *table;
|
|
|
|
table = new_state->current_table;
|
|
/*
|
|
* If there is an unterminated row open,
|
|
* terminate it now.
|
|
*/
|
|
if ((table->row_ptr != NULL)&&
|
|
(table->row_ptr->row_done == FALSE))
|
|
{
|
|
lo_EndTableRow(context, new_state, table);
|
|
}
|
|
lo_EndTable(context, new_state,
|
|
new_state->current_table, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
lo_process_table_tag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
lo_BeginTable(context, state, tag);
|
|
}
|
|
else
|
|
{
|
|
lo_CloseTable(context, state);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
lo_CloseOutTable(MWContext *context, lo_DocState *state)
|
|
{
|
|
lo_CloseTable(context, state);
|
|
}
|
|
|
|
|
|
#ifdef HTML_CERTIFICATE_SUPPORT
|
|
|
|
static void
|
|
lo_process_certificate_tag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
Bool tmp_in_head;
|
|
lo_Certificate *lo_cert;
|
|
|
|
/*
|
|
* Do not let closing a paragraph before a cert change the
|
|
* in_head state.
|
|
*/
|
|
tmp_in_head = state->top_state->in_head;
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
state->top_state->in_head = tmp_in_head;
|
|
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
/*
|
|
* Flush the line buffer so we can start
|
|
* storing the certificate data there.
|
|
*/
|
|
lo_FlushTextBlock(context, state);
|
|
|
|
state->line_buf_len = 0;
|
|
|
|
lo_cert = XP_NEW(lo_Certificate);
|
|
if (lo_cert == NULL)
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
return;
|
|
}
|
|
|
|
lo_cert->cert = NULL;
|
|
lo_cert->name = lo_FetchParamValue(context, tag, PARAM_NAME);
|
|
|
|
lo_cert->next = state->top_state->cert_list;
|
|
state->top_state->cert_list = lo_cert;
|
|
|
|
state->text_divert = tag->type;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Get this out of the way so we can just return if we hit
|
|
* errors below.
|
|
*/
|
|
state->text_divert = P_UNKNOWN;
|
|
|
|
/*
|
|
* Only if we captured a certificate
|
|
*/
|
|
if (state->line_buf_len != 0)
|
|
{
|
|
char *tptr;
|
|
|
|
state->line_buf_len = 0;
|
|
|
|
lo_cert = state->top_state->cert_list;
|
|
if (lo_cert == NULL)
|
|
return;
|
|
|
|
PA_LOCK(tptr, char *, state->line_buf);
|
|
lo_cert->cert = XP_STRDUP(tptr);
|
|
PA_UNLOCK(state->line_buf);
|
|
|
|
if (lo_cert->cert == NULL)
|
|
{
|
|
state->top_state->cert_list = lo_cert->next;
|
|
PA_FREE(lo_cert->name);
|
|
XP_FREE(lo_cert);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif /* HTML_CERTIFICATE_SUPPORT */
|
|
|
|
|
|
/* Prepends a tall thin bullet to all lines numbered between start and end, inclusive.
|
|
Used for mailing quotations. */
|
|
void
|
|
lo_add_leading_bullets(MWContext *context, lo_DocState *state,
|
|
int32 start,int32 end,int32 mquote_x)
|
|
{
|
|
int32 n;
|
|
LO_Element **line_array;
|
|
LO_TextAttr tmp_attr;
|
|
LO_TextAttr *bullet_attr;
|
|
XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
|
|
|
|
/* Make bullets blue. */
|
|
lo_SetDefaultFontAttr(state, &tmp_attr, context);
|
|
tmp_attr.fg.red = 0;
|
|
tmp_attr.fg.green = 0;
|
|
tmp_attr.fg.blue = 255;
|
|
bullet_attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
|
|
for (n = start; n <= end; n++)
|
|
{
|
|
LO_BulletStruct *bullet;
|
|
|
|
bullet = (LO_BulletStruct *)lo_NewElement(context, state, LO_BULLET, NULL, 0);
|
|
if (bullet == NULL)
|
|
{
|
|
/* Don't just return, still want to unlock block. */
|
|
n = end;
|
|
break;
|
|
}
|
|
|
|
bullet->type = LO_BULLET;
|
|
bullet->ele_id = NEXT_ELEMENT;
|
|
|
|
bullet->x = mquote_x;
|
|
bullet->x_offset = 0; /* LIST_MARGIN_INC / 2; */
|
|
bullet->y = line_array[n]->lo_any.y;
|
|
bullet->y_offset = 0;
|
|
bullet->width = 5; /* 2 larger than actual size */
|
|
bullet->height = line_array[n]->lo_any.line_height;
|
|
bullet->FE_Data = NULL;
|
|
bullet->line_height = line_array[n]->lo_any.line_height;
|
|
|
|
/* No easy way of finding out what the bullet level actually is */
|
|
bullet->level = 0;
|
|
|
|
bullet->bullet_type = BULLET_MQUOTE;
|
|
|
|
bullet->text_attr = bullet_attr;
|
|
bullet->ele_attrmask = 0;
|
|
|
|
bullet->sel_start = -1;
|
|
bullet->sel_end = -1;
|
|
|
|
bullet->next = line_array[n];
|
|
bullet->prev = line_array[n]->lo_any.prev;
|
|
if (line_array[n]->lo_any.prev)
|
|
{
|
|
line_array[n]->lo_any.prev->lo_any.next = (LO_Element *)bullet;
|
|
}
|
|
line_array[n]->lo_any.prev = (LO_Element *)bullet;
|
|
line_array[n] = (LO_Element *)bullet;
|
|
/*
|
|
** Since these elements are added behind the layout stream, that
|
|
** means they would only be displayed if the area was refreshed
|
|
** or not visible as layout streamed through. In case that would
|
|
** be bad, we ask them to be displayed as we create them.
|
|
** --mtoy & ltabb
|
|
*/
|
|
if ( state != NULL && (!state->display_blocked) )
|
|
{
|
|
lo_DisplayBullet(context, bullet);
|
|
}
|
|
}
|
|
|
|
XP_UNLOCK_BLOCK(state->line_array);
|
|
}
|
|
|
|
PRIVATE
|
|
char *
|
|
lo_MakeAttrLowerCase(char *ptr)
|
|
{
|
|
char *tmp_ptr;
|
|
|
|
if(!ptr)
|
|
return NULL;
|
|
|
|
for(tmp_ptr = ptr; *tmp_ptr; tmp_ptr++)
|
|
*tmp_ptr = tolower(*tmp_ptr);
|
|
|
|
return(ptr);
|
|
}
|
|
|
|
/* finds the first token in a space separated list
|
|
* If there are more characters after the first token
|
|
* *end_token will be set to the next set of characters
|
|
* otherwise it will be set to NULL
|
|
*/
|
|
|
|
PRIVATE
|
|
char *
|
|
lo_find_first_style_token(char *string_ptr, char **end_token)
|
|
{
|
|
if(!string_ptr)
|
|
return NULL;
|
|
|
|
while(isspace(*string_ptr))
|
|
string_ptr++;
|
|
|
|
for(*end_token = string_ptr; **end_token && !isspace(**end_token); (*end_token)++)
|
|
; /* null body */
|
|
|
|
if(!**end_token)
|
|
*end_token = NULL; /* set end_token to NULL if this is the last token */
|
|
else
|
|
*((*end_token)++) = '\0';
|
|
|
|
/* if it's an empty string return NULL */
|
|
if(!*string_ptr)
|
|
return(NULL);
|
|
else
|
|
return(string_ptr);
|
|
}
|
|
|
|
/* finds the next token in a space separated list.
|
|
* the previous value of end_token is used to find
|
|
* the beginning of the next token
|
|
*/
|
|
PRIVATE
|
|
char *
|
|
lo_find_next_style_token(char **end_token)
|
|
{
|
|
if(end_token)
|
|
return lo_find_first_style_token(*end_token, end_token);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
MODULE_PRIVATE int
|
|
lo_list_bullet_type(char *type_string, TagType tag_type)
|
|
{
|
|
int type = BULLET_BASIC;
|
|
|
|
if(!type_string)
|
|
return type;
|
|
|
|
if (!strcasecomp(type_string, "none"))
|
|
type = BULLET_NONE;
|
|
else if (!strcasecomp(type_string, "disc"))
|
|
type = BULLET_BASIC;
|
|
else if (!strcasecomp(type_string, "circle") || !strcasecomp(type_string, "round"))
|
|
type = BULLET_ROUND;
|
|
else if (!strcasecomp(type_string, "square"))
|
|
type = BULLET_SQUARE;
|
|
else if (!strcasecomp(type_string, "decimal"))
|
|
type = BULLET_NUM;
|
|
else if (!strcasecomp(type_string, "lower-roman"))
|
|
type = BULLET_NUM_S_ROMAN;
|
|
else if (!strcasecomp(type_string, "upper-roman"))
|
|
type = BULLET_NUM_L_ROMAN;
|
|
else if (!strcasecomp(type_string, "lower-alpha"))
|
|
type = BULLET_ALPHA_S;
|
|
else if (!strcasecomp(type_string, "upper-alpha"))
|
|
type = BULLET_ALPHA_L;
|
|
else if (*type_string == 'A')
|
|
type = BULLET_ALPHA_L;
|
|
else if (*type_string == 'a')
|
|
type = BULLET_ALPHA_S;
|
|
else if (*type_string == 'I')
|
|
type = BULLET_NUM_L_ROMAN;
|
|
else if (*type_string == 'i')
|
|
type = BULLET_NUM_S_ROMAN;
|
|
else if(tag_type == P_NUM_LIST)
|
|
type = BULLET_NUM;
|
|
else
|
|
type = BULLET_BASIC;
|
|
|
|
return type;
|
|
}
|
|
|
|
void
|
|
lo_SetupStateForList(MWContext *context,
|
|
lo_DocState *state,
|
|
LO_ListStruct *list,
|
|
XP_Bool in_resize_reflow)
|
|
{
|
|
int32 old_right_margin;
|
|
|
|
if (state->list_stack->level == 0)
|
|
{
|
|
/*
|
|
lo_SetSoftLineBreakState(context, state,
|
|
FALSE, 2);
|
|
*/
|
|
lo_SetLineBreakState (context, state, FALSE, LO_LINEFEED_BREAK_SOFT, 2, in_resize_reflow);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
lo_SetSoftLineBreakState(context, state,
|
|
FALSE, 1);
|
|
*/
|
|
lo_SetLineBreakState (context, state, FALSE, LO_LINEFEED_BREAK_SOFT, 1, in_resize_reflow);
|
|
}
|
|
/* THIS BREAKS FLOATING IMAGES IN LISTS
|
|
state->list_stack->old_left_margin =
|
|
state->left_margin;
|
|
state->list_stack->old_right_margin =
|
|
state->right_margin;
|
|
*/
|
|
/*
|
|
* For lists, we leave the right
|
|
* margin unchanged.
|
|
*/
|
|
old_right_margin =
|
|
state->list_stack->old_right_margin;
|
|
|
|
/*
|
|
* handle nested description lists
|
|
* with the right amount of indenting
|
|
*/
|
|
if ((list->tag->type == P_DESC_LIST)&&
|
|
(state->list_stack->type == P_DESC_LIST)&&
|
|
(state->list_stack->value == 1))
|
|
{
|
|
state->left_margin += LIST_MARGIN_INC;
|
|
}
|
|
|
|
lo_PushList(state, list->tag, list->quote_type);
|
|
|
|
/* <MQUOTE> gives less indent than other lists */
|
|
if (list->quote_type == QUOTE_MQUOTE)
|
|
{
|
|
state->left_margin += MQUOTE_MARGIN_INC;
|
|
}
|
|
else if (list->tag->type != P_DESC_LIST)
|
|
{
|
|
state->left_margin += LIST_MARGIN_INC;
|
|
}
|
|
|
|
state->x = state->left_margin;
|
|
state->list_stack->old_left_margin =
|
|
state->left_margin;
|
|
/*
|
|
* Set right margin to be last list's right,
|
|
* irregardless of current margin stack.
|
|
*/
|
|
state->list_stack->old_right_margin =
|
|
old_right_margin;
|
|
state->list_stack->bullet_type = (intn) list->bullet_type;
|
|
state->list_stack->value = list->bullet_start;
|
|
state->list_stack->compact = list->compact;
|
|
}
|
|
|
|
void
|
|
lo_UpdateStateAfterList(MWContext *context,
|
|
lo_DocState *state,
|
|
LO_ListStruct *list,
|
|
XP_Bool in_resize_reflow)
|
|
{
|
|
lo_ListStack *lptr;
|
|
Bool mquote = FALSE;
|
|
int32 mquote_line_num = 0;
|
|
int32 mquote_x = 0;
|
|
|
|
/*
|
|
* Reset fake linefeed state used to
|
|
* fake out headers in lists.
|
|
*/
|
|
if (state->at_begin_line == FALSE)
|
|
{
|
|
state->linefeed_state = 0;
|
|
}
|
|
|
|
lptr = lo_PopList(state, NULL);
|
|
if (lptr != NULL)
|
|
{
|
|
if (lptr->quote_type == QUOTE_MQUOTE) {
|
|
mquote = TRUE;
|
|
}
|
|
else {
|
|
mquote = FALSE;
|
|
}
|
|
mquote_line_num = lptr->mquote_line_num;
|
|
mquote_x = lptr->mquote_x;
|
|
XP_DELETE(lptr);
|
|
}
|
|
state->left_margin =
|
|
state->list_stack->old_left_margin;
|
|
state->right_margin =
|
|
state->list_stack->old_right_margin;
|
|
/*
|
|
* Reset the margins properly in case
|
|
* there is a right-aligned image here.
|
|
*/
|
|
lo_FindLineMargins(context, state, !in_resize_reflow);
|
|
if (state->list_stack->level == 0)
|
|
{
|
|
if (state->linefeed_state >= 2)
|
|
{
|
|
lo_FindLineMargins(context, state, !in_resize_reflow);
|
|
}
|
|
else
|
|
{
|
|
if (!in_resize_reflow)
|
|
{
|
|
/* we only want to append a paragraph break if we're initially laying out the
|
|
document, not if we're reflowing it. */
|
|
lo_SetLineBreakState(context, state, FALSE,
|
|
LO_LINEFEED_BREAK_PARAGRAPH, 2, FALSE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (state->linefeed_state >= 1)
|
|
{
|
|
lo_FindLineMargins(context, state, !in_resize_reflow);
|
|
}
|
|
else
|
|
{
|
|
/* lo_SetSoftLineBreakState(context, state, FALSE, 1); */
|
|
lo_SetLineBreakState (context, state, FALSE, LO_LINEFEED_BREAK_SOFT, 1, in_resize_reflow);
|
|
}
|
|
}
|
|
|
|
/* I don't think we need to do this as the linefeeds should reset x for us. However, if we do
|
|
need this, make sure to reimplement it in a way so as to not break the editor during reflow
|
|
(max_width was not being correctly set due to the fact that we were resetting x here) */
|
|
#if 0
|
|
state->x = state->left_margin;
|
|
#endif
|
|
|
|
/* Go back and add bullets to all lines between here and the
|
|
beginning of the mailing quotation. */
|
|
if (mquote)
|
|
{
|
|
lo_add_leading_bullets(context,state,
|
|
mquote_line_num - 1,
|
|
state->line_num - state->linefeed_state - 1,
|
|
mquote_x);
|
|
}
|
|
}
|
|
|
|
PRIVATE void
|
|
lo_setup_list(lo_DocState *state,
|
|
MWContext *context,
|
|
PA_Tag *tag,
|
|
char *bullet_type,
|
|
char *bullet_start,
|
|
char *compact_attr)
|
|
{
|
|
|
|
int32 type;
|
|
int32 val;
|
|
Bool compact;
|
|
int8 quote_type = QUOTE_NONE;
|
|
char *list_style_prop=NULL;
|
|
LO_ListStruct *list;
|
|
|
|
if (tag->type == P_MQUOTE)
|
|
{
|
|
quote_type = QUOTE_MQUOTE;
|
|
}
|
|
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
|
|
list = (LO_ListStruct*)lo_NewElement(context, state, LO_LIST, NULL, 0);
|
|
XP_ASSERT(list);
|
|
if (!list) return;
|
|
|
|
/* check for a bullet type from style sheets */
|
|
if(state && state->top_state && state->top_state->style_stack && tag->type != P_DESC_LIST)
|
|
{
|
|
StyleStruct *style_struct = STYLESTACK_GetStyleByIndex(state->top_state->style_stack, 0);
|
|
|
|
if(style_struct)
|
|
{
|
|
list_style_prop = STYLESTRUCT_GetString(style_struct, LIST_STYLE_TYPE_STYLE);
|
|
if(list_style_prop)
|
|
{
|
|
bullet_type = list_style_prop;
|
|
}
|
|
}
|
|
}
|
|
|
|
type = BULLET_BASIC;
|
|
if(bullet_type && tag->type != P_DESC_LIST)
|
|
{
|
|
type = lo_list_bullet_type(bullet_type, tag->type);
|
|
|
|
XP_FREEIF(list_style_prop);
|
|
}
|
|
else if (tag->type == P_NUM_LIST)
|
|
{
|
|
type = BULLET_NUM;
|
|
}
|
|
else if (tag->type != P_DESC_LIST)
|
|
{
|
|
intn lev;
|
|
|
|
lev = state->list_stack->level;
|
|
if (lev < 1)
|
|
{
|
|
type = BULLET_BASIC;
|
|
}
|
|
else if (lev == 1)
|
|
{
|
|
type = BULLET_ROUND;
|
|
}
|
|
else if (lev > 1)
|
|
{
|
|
type = BULLET_SQUARE;
|
|
}
|
|
}
|
|
|
|
if (bullet_start != NULL)
|
|
{
|
|
|
|
val = XP_ATOI(bullet_start);
|
|
if (val < 1)
|
|
{
|
|
val = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
val = 1;
|
|
}
|
|
|
|
if (compact_attr)
|
|
{
|
|
compact = TRUE;
|
|
}
|
|
else
|
|
{
|
|
compact = FALSE;
|
|
}
|
|
|
|
list->lo_any.type = LO_LIST;
|
|
list->lo_any.ele_id = NEXT_ELEMENT;
|
|
list->is_end = FALSE;
|
|
list->bullet_type = type;
|
|
list->bullet_start = val;
|
|
list->quote_type = quote_type;
|
|
list->compact = compact;
|
|
list->tag = PA_CloneMDLTag(tag);
|
|
|
|
list->lo_any.x = state->x;
|
|
list->lo_any.y = state->y;
|
|
list->lo_any.x_offset = 0;
|
|
list->lo_any.y_offset = 0;
|
|
list->lo_any.width = 0;
|
|
list->lo_any.height = 0;
|
|
list->lo_any.line_height = 0;
|
|
|
|
lo_AppendToLineList(context, state, (LO_Element*)list, 0);
|
|
lo_SetupStateForList(context, state, list, FALSE);
|
|
}
|
|
|
|
MODULE_PRIVATE void
|
|
lo_TeardownList(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
LO_ListStruct *list;
|
|
|
|
if (tag && state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
|
|
list = (LO_ListStruct*)lo_NewElement(context, state, LO_LIST, NULL, 0);
|
|
XP_ASSERT(list);
|
|
if (!list) return;
|
|
|
|
list->lo_any.type = LO_LIST;
|
|
list->lo_any.ele_id = NEXT_ELEMENT;
|
|
list->is_end = TRUE;
|
|
list->tag = PA_CloneMDLTag(tag);
|
|
|
|
list->lo_any.x = state->x;
|
|
list->lo_any.y = state->y;
|
|
list->lo_any.x_offset = 0;
|
|
list->lo_any.y_offset = 0;
|
|
list->lo_any.width = 0;
|
|
list->lo_any.height = 0;
|
|
list->lo_any.line_height = 0;
|
|
|
|
lo_AppendToLineList(context, state, (LO_Element*)list, 0);
|
|
lo_UpdateStateAfterList(context, state, list, FALSE);
|
|
}
|
|
|
|
PRIVATE int32
|
|
lo_calc_distance_to_bottom_of_prev_line(lo_DocState *state)
|
|
{
|
|
int32 height;
|
|
LO_LinefeedStruct *linefeed;
|
|
|
|
if(!state->end_last_line)
|
|
return 0;
|
|
|
|
linefeed = (LO_LinefeedStruct*)state->end_last_line;
|
|
height = state->y - (linefeed->y + linefeed->y_offset + linefeed->line_height);
|
|
|
|
|
|
#ifndef ALLOW_NEG_MARGINS
|
|
XP_ASSERT(height > -1); /* this happens w/ neg margins */
|
|
#endif
|
|
|
|
if(height < 0)
|
|
height = 0;
|
|
|
|
return height;
|
|
}
|
|
|
|
/**************************
|
|
* Should be moved to laystyle.c eventually
|
|
*/
|
|
PRIVATE
|
|
void
|
|
lo_SetNewMarginsForStyle(lo_DocState *state,
|
|
PA_Tag *tag,
|
|
StyleStruct *style_struct,
|
|
int32 text_width,
|
|
int32 left_margin_offset,
|
|
int32 right_margin_offset)
|
|
{
|
|
|
|
/* set left and right margins */
|
|
lo_PushList(state, tag, QUOTE_NONE);
|
|
|
|
if(left_margin_offset)
|
|
{
|
|
state->left_margin += left_margin_offset;
|
|
if(state->left_margin < 0)
|
|
state->left_margin = 0;
|
|
}
|
|
|
|
/* can't be negative */
|
|
if(right_margin_offset)
|
|
state->right_margin -= right_margin_offset;
|
|
|
|
if(text_width > 0)
|
|
{
|
|
int32 cur_width = state->right_margin - state->left_margin;
|
|
int32 width_diff = text_width - cur_width;
|
|
|
|
/* right_margin loses when a width is set */
|
|
state->right_margin += width_diff;
|
|
|
|
/* keep the right margin on the screen */
|
|
if(state->right_margin > (state->win_width - state->left_margin))
|
|
state->right_margin = state->win_width - state->left_margin;
|
|
}
|
|
|
|
state->x = state->left_margin;
|
|
|
|
state->list_stack->old_left_margin = state->left_margin;
|
|
state->list_stack->old_right_margin = state->right_margin;
|
|
}
|
|
|
|
PRIVATE
|
|
int32
|
|
lo_CalcCurrentLineHeight(MWContext *context, lo_DocState *state)
|
|
{
|
|
|
|
if(state->line_height)
|
|
{
|
|
return(state->line_height);
|
|
}
|
|
else
|
|
{
|
|
int32 new_height = state->text_info.ascent +
|
|
state->text_info.descent;
|
|
|
|
if ((new_height <= 0)&&(state->font_stack != NULL)&&
|
|
(state->font_stack->text_attr != NULL))
|
|
{
|
|
lo_fillin_text_info(context, state);
|
|
|
|
new_height = state->text_info.ascent + state->text_info.descent;
|
|
|
|
}
|
|
|
|
return new_height;
|
|
}
|
|
}
|
|
|
|
int PR_CALLBACK lo_face_attribute_pref_callback(const char *pref_name, void *);
|
|
/*************************************
|
|
* Function: lo_face_attribute_pref_callback
|
|
*
|
|
* Description: This function is registered with XP prefs and is called whenever
|
|
* the value of "browser.use_document_fonts" is changed.
|
|
*
|
|
* Params: pref_name is the pref string that the call is registered with,
|
|
* "browser.use_document_fonts" and a void pointer that is registered as NULL.
|
|
*
|
|
* Returns: PREF_NOERROR (which is ignored)
|
|
*************************************/
|
|
PRIVATE Bool lo_do_face_attribute = TRUE;
|
|
|
|
int PR_CALLBACK
|
|
lo_face_attribute_pref_callback(const char *pref_name, void *notUsed)
|
|
{
|
|
int32 fontPrefValue;
|
|
PREF_GetIntPref(pref_name, &fontPrefValue);
|
|
|
|
/* We use the face attribute if the pref value is 1 (face attribute only)
|
|
or 2 (face attribute and web fonts), but not if the it is 0 (never use
|
|
document fonts). */
|
|
lo_do_face_attribute = fontPrefValue ? TRUE: FALSE;
|
|
|
|
return PREF_NOERROR;
|
|
}
|
|
|
|
#define PICS_SUPPORT
|
|
#ifdef PICS_SUPPORT
|
|
|
|
PRIVATE Bool
|
|
lo_is_url_excluded_from_pics(URL_Struct *URL_s)
|
|
{
|
|
int type = NET_URL_Type(URL_s->address);
|
|
|
|
switch(type)
|
|
{
|
|
case SECURITY_TYPE_URL:
|
|
case ABOUT_TYPE_URL:
|
|
case ADDRESS_BOOK_TYPE_URL:
|
|
case ADDRESS_BOOK_LDAP_TYPE_URL:
|
|
case INTERNAL_IMAGE_TYPE_URL:
|
|
case HTML_DIALOG_HANDLER_TYPE_URL:
|
|
case HTML_PANEL_HANDLER_TYPE_URL:
|
|
case INTERNAL_SECLIB_TYPE_URL:
|
|
case INTERNAL_CERTLDAP_TYPE_URL:
|
|
case MOCHA_TYPE_URL:
|
|
case NETHELP_TYPE_URL:
|
|
case VIEW_SOURCE_TYPE_URL:
|
|
case WYSIWYG_TYPE_URL:
|
|
return TRUE;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Interpret a PICS label if there is one.
|
|
* returns true to interrupt layout
|
|
*/
|
|
PRIVATE
|
|
PICS_PassFailReturnVal
|
|
lo_ProcessPicsLabel(MWContext *context, lo_DocState *state, char **fail_url)
|
|
{
|
|
URL_Struct *URL_s;
|
|
PICS_RatingsStruct *rs = NULL;
|
|
PICS_PassFailReturnVal status = PICS_NO_RATINGS;
|
|
uint i;
|
|
|
|
#define PICS_HEADER "PICS-Label"
|
|
|
|
if(!PICS_IsPICSEnabledByUser())
|
|
return PICS_RATINGS_PASSED;
|
|
|
|
if(!state->top_state
|
|
|| !state->top_state->nurl
|
|
|| !*state->top_state->nurl->address)
|
|
return PICS_RATINGS_PASSED;
|
|
|
|
URL_s = state->top_state->nurl;
|
|
|
|
if(lo_is_url_excluded_from_pics(URL_s))
|
|
return PICS_RATINGS_PASSED;
|
|
|
|
/* check the URL struct for a pics label */
|
|
for(i=0 ;i < URL_s->all_headers.empty_index; i++)
|
|
{
|
|
if(!strcasecomp(URL_s->all_headers.key[i], PICS_HEADER))
|
|
{
|
|
PICS_PassFailReturnVal tmp_status;
|
|
char *ptr;
|
|
|
|
/* found a pics header */
|
|
|
|
/* change any \n or \r to space */
|
|
if(URL_s->all_headers.value[i])
|
|
for(ptr = URL_s->all_headers.value[i]; *ptr; ptr++)
|
|
if(*ptr == '\r' || *ptr == '\n')
|
|
*ptr = ' ';
|
|
|
|
/* parse it */
|
|
rs = PICS_ParsePICSLable(URL_s->all_headers.value[i]);
|
|
|
|
/* compare to the prefs
|
|
*/
|
|
tmp_status = PICS_CompareToUserSettings(rs, URL_s->address);
|
|
|
|
if(tmp_status == PICS_RATINGS_FAILED)
|
|
{
|
|
status = tmp_status;
|
|
*fail_url = PICS_RStoURL(rs, URL_s->address);
|
|
break; /* finished */
|
|
}
|
|
else if(tmp_status == PICS_RATINGS_PASSED)
|
|
{
|
|
/* don't overwrite previous passed ratings with
|
|
* no_rating
|
|
*/
|
|
status = tmp_status;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(status == PICS_RATINGS_FAILED)
|
|
{
|
|
/* keep the URL from being cached
|
|
* Do it here since we have the URL struct
|
|
*/
|
|
URL_s->dont_cache = TRUE;
|
|
}
|
|
|
|
PICS_FreeRatingsStruct(rs); /* handles NULL */
|
|
|
|
return status;
|
|
}
|
|
#endif /* PICS_SUPPORT */
|
|
|
|
Bool lo_face_attribute();
|
|
/*************************************
|
|
* Function: lo_face_attribute
|
|
* Description: This function determines whether or not we should use the FACE
|
|
* attribute in FONT tags. It cache the answer in a static and only
|
|
* calls into the prefs the first time. A callback is registered to
|
|
* keep the cache value current.
|
|
*
|
|
* Params: none.
|
|
*
|
|
* Returns: true iff the current pref setting calls for the use of the FACE
|
|
* attribute in FONT tags
|
|
*************************************/
|
|
Bool
|
|
lo_face_attribute()
|
|
{
|
|
static Bool first_time = TRUE;
|
|
|
|
if(first_time)
|
|
{
|
|
int32 fontPrefValue;
|
|
first_time = FALSE;
|
|
PREF_GetIntPref("browser.use_document_fonts", &fontPrefValue);
|
|
/* We use the face attribute if the pref value is 1 (face attribute only)
|
|
or 2 (face attribute and web fonts), but not if the it is 0 (never use
|
|
document fonts). */
|
|
lo_do_face_attribute = fontPrefValue ? TRUE: FALSE;
|
|
PREF_RegisterCallback( "browser.use_document_fonts",
|
|
lo_face_attribute_pref_callback,
|
|
NULL);
|
|
}
|
|
|
|
return (lo_do_face_attribute);
|
|
}
|
|
|
|
/* returns true if the tag can or did push a font
|
|
* of a different size onto the font stack
|
|
*/
|
|
PRIVATE
|
|
Bool
|
|
lo_tag_pushes_different_size_font(TagType type)
|
|
{
|
|
if(type == P_FONT
|
|
|| type == P_HEADER_1
|
|
|| type == P_HEADER_2
|
|
|| type == P_HEADER_3
|
|
|| type == P_HEADER_4
|
|
|| type == P_HEADER_5
|
|
|| type == P_HEADER_6
|
|
)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
lo_adjust_border_thickness_for_style(StyleStruct *style_struct, SS_Number *width)
|
|
{
|
|
char *border_style_value;
|
|
|
|
if(!width)
|
|
return;
|
|
|
|
border_style_value = STYLESTRUCT_GetString(style_struct,
|
|
BORDER_STYLE_STYLE);
|
|
|
|
if(!border_style_value)
|
|
return;
|
|
|
|
if(!strcasecomp(border_style_value, "groove")
|
|
|| !strcasecomp(border_style_value, "ridge"))
|
|
{
|
|
width->value *= 2;
|
|
}
|
|
else if(!strcasecomp(border_style_value, "double"))
|
|
{
|
|
width->value *= 3;
|
|
}
|
|
}
|
|
|
|
PRIVATE
|
|
void
|
|
lo_SetStyleSheetFontProperties(MWContext *context,
|
|
lo_DocState *state,
|
|
StyleStruct *style_struct,
|
|
PA_Tag *tag,
|
|
XP_Bool use_background_color)
|
|
{
|
|
char *property;
|
|
SS_Number *ss_num;
|
|
LO_TextAttr *attr;
|
|
LO_TextAttr tmp_attr;
|
|
XP_Bool push_font = FALSE;
|
|
|
|
if(!style_struct || !state)
|
|
return;
|
|
|
|
if(state->font_stack)
|
|
{
|
|
lo_CopyTextAttr(state->font_stack->text_attr, &tmp_attr);
|
|
}
|
|
else
|
|
{
|
|
XP_MEMSET(&tmp_attr,0,sizeof(tmp_attr));
|
|
}
|
|
|
|
property = STYLESTRUCT_GetString(style_struct, COLOR_STYLE);
|
|
if (property)
|
|
{
|
|
LO_ParseStyleSheetRGB(property, &tmp_attr.fg.red, &tmp_attr.fg.green, &tmp_attr.fg.blue);
|
|
XP_FREE(property);
|
|
push_font = TRUE;
|
|
}
|
|
|
|
/* don't inherit text background colors from the body and table tags */
|
|
if(use_background_color
|
|
&& tag->type != P_UNKNOWN /* table relayout dummy tag */
|
|
&& tag->type != P_BODY
|
|
&& tag->type != P_TABLE
|
|
&& tag->type != P_TABLE_DATA
|
|
&& tag->type != P_TABLE_HEADER
|
|
&& tag->type != P_TABLE_ROW)
|
|
{
|
|
property = STYLESTRUCT_GetString(style_struct, BG_COLOR_STYLE);
|
|
if (property)
|
|
{
|
|
LO_ParseStyleSheetRGB(property, &tmp_attr.bg.red, &tmp_attr.bg.green, &tmp_attr.bg.blue);
|
|
if(strcasecomp(property, "transparent"))
|
|
tmp_attr.no_background = FALSE; /* force layout to show the background color */
|
|
else
|
|
tmp_attr.no_background = TRUE; /* no background */
|
|
XP_FREE(property);
|
|
push_font = TRUE;
|
|
}
|
|
}
|
|
|
|
if (lo_face_attribute())
|
|
{
|
|
property = STYLESTRUCT_GetString(style_struct, FONTFACE_STYLE);
|
|
if (property)
|
|
{
|
|
tmp_attr.font_face = lo_FetchFontFace(context, state, property);
|
|
XP_FREE(property);
|
|
push_font = TRUE;
|
|
}
|
|
}
|
|
|
|
property = STYLESTRUCT_GetString(style_struct, FONTSIZE_STYLE);
|
|
if(property)
|
|
{
|
|
/* scaling factor of 1.4 with medium at 10pts
|
|
*/
|
|
if(!strcasecomp(property, "xx-small"))
|
|
{
|
|
ss_num = STYLESTRUCT_StringToSSNumber(style_struct, "6pt");
|
|
}
|
|
else if(!strcasecomp(property, "x-small"))
|
|
{
|
|
ss_num = STYLESTRUCT_StringToSSNumber(style_struct, "8pt");
|
|
}
|
|
else if(!strcasecomp(property, "small"))
|
|
{
|
|
ss_num = STYLESTRUCT_StringToSSNumber(style_struct, "10pt");
|
|
}
|
|
else if(!strcasecomp(property, "medium"))
|
|
{
|
|
ss_num = STYLESTRUCT_StringToSSNumber(style_struct, "12pt");
|
|
}
|
|
else if(!strcasecomp(property, "large"))
|
|
{
|
|
ss_num = STYLESTRUCT_StringToSSNumber(style_struct, "18pt");
|
|
}
|
|
else if(!strcasecomp(property, "x-large"))
|
|
{
|
|
ss_num = STYLESTRUCT_StringToSSNumber(style_struct, "27pt");
|
|
}
|
|
else if(!strcasecomp(property, "xx-large"))
|
|
{
|
|
ss_num = STYLESTRUCT_StringToSSNumber(style_struct, "40pt");
|
|
}
|
|
else if(!strcasecomp(property, "larger"))
|
|
{
|
|
/* scale up by 1.5 */
|
|
ss_num = STYLESTRUCT_StringToSSNumber(style_struct, "1.5em");
|
|
}
|
|
else if(!strcasecomp(property, "smaller"))
|
|
{
|
|
/* scale down by 1.5 */
|
|
ss_num = STYLESTRUCT_StringToSSNumber(style_struct, "0.66667em");
|
|
}
|
|
else
|
|
{
|
|
/* interpret the property as a number
|
|
* invalid properties will resolve to 0 and be ignored
|
|
*/
|
|
ss_num = STYLESTRUCT_StringToSSNumber(style_struct, property);
|
|
}
|
|
|
|
if(ss_num && ss_num->value > 0)
|
|
{
|
|
LO_TextAttr *top_font=NULL;
|
|
|
|
/* if the current tag pushed a font of different
|
|
* size on the stack, temporarily pop it off
|
|
* so that we can calculate EM units from the parent's
|
|
* tag size rather than this tags size
|
|
*/
|
|
if(lo_tag_pushes_different_size_font(tag->type))
|
|
top_font = lo_PopFont(state, tag->type);
|
|
|
|
LO_AdjustSSUnits(ss_num, FONTSIZE_STYLE, context, state);
|
|
tmp_attr.point_size = ss_num->value;
|
|
push_font = TRUE;
|
|
|
|
/* push the old font back on the stack */
|
|
if(top_font)
|
|
lo_PushFont(state, tag->type, top_font);
|
|
}
|
|
|
|
if(ss_num)
|
|
STYLESTRUCT_FreeSSNumber(style_struct, ss_num);
|
|
|
|
XP_FREE(property);
|
|
}
|
|
|
|
property = STYLESTRUCT_GetString(style_struct, FONTWEIGHT_STYLE);
|
|
if(property)
|
|
{
|
|
push_font = TRUE;
|
|
|
|
if(!strcasecomp(property, "normal"))
|
|
tmp_attr.font_weight = 400;
|
|
else if(!strcasecomp(property, "bold"))
|
|
tmp_attr.font_weight = 700;
|
|
else if(!strcasecomp(property, "bolder"))
|
|
{
|
|
if(!tmp_attr.font_weight)
|
|
tmp_attr.font_weight = 400; /* normal */
|
|
tmp_attr.font_weight += 100;
|
|
if(tmp_attr.font_weight < 100)
|
|
tmp_attr.font_weight = 100;
|
|
}
|
|
else if(!strcasecomp(property, "lighter"))
|
|
{
|
|
if(!tmp_attr.font_weight)
|
|
tmp_attr.font_weight = 400; /* normal */
|
|
tmp_attr.font_weight -= 100;
|
|
if(tmp_attr.font_weight > 900)
|
|
tmp_attr.font_weight = 900;
|
|
}
|
|
else
|
|
{
|
|
ss_num = STYLESTRUCT_StringToSSNumber(style_struct, property);
|
|
|
|
if(ss_num && ss_num->value > 0)
|
|
{
|
|
/* normalize value */
|
|
/* 100, 200, 300, 400, 500, 600, 700, 800, or 900 */
|
|
uint32 weight = (int32) ss_num->value;
|
|
weight = weight - (weight % 100);
|
|
|
|
if(weight > 0)
|
|
{
|
|
tmp_attr.font_weight = (uint16)weight;
|
|
}
|
|
STYLESTRUCT_FreeSSNumber(style_struct, ss_num);
|
|
}
|
|
}
|
|
|
|
XP_FREE(property);
|
|
}
|
|
|
|
property = STYLESTRUCT_GetString(style_struct, FONTSTYLE_STYLE);
|
|
if(property)
|
|
{
|
|
tmp_attr.fontmask &= (~LO_FONT_ITALIC);
|
|
|
|
if(!strcasecomp(property, NORMAL_STYLE))
|
|
{
|
|
/* do nothing */
|
|
}
|
|
else if(!strcasecomp(property, ITALIC_STYLE))
|
|
{
|
|
tmp_attr.fontmask |= LO_FONT_ITALIC;
|
|
}
|
|
else if(!strcasecomp(property, OBLIQUE_STYLE))
|
|
{
|
|
/* no oblique property yet */
|
|
}
|
|
XP_FREE(property);
|
|
push_font = TRUE;
|
|
}
|
|
|
|
property = STYLESTRUCT_GetString(style_struct, TEXTDECORATION_STYLE);
|
|
if(property)
|
|
{
|
|
char *token, *end_token;
|
|
|
|
tmp_attr.attrmask &= (~(LO_ATTR_BLINK | LO_ATTR_STRIKEOUT | LO_ATTR_UNDERLINE));
|
|
|
|
/* parse out separate tokens */
|
|
token = lo_find_first_style_token(property, &end_token);
|
|
while(token)
|
|
{
|
|
if(!strcasecomp(token, "none"))
|
|
{
|
|
/* do nothing */
|
|
}
|
|
else if(!strcasecomp(token, BLINK_STYLE))
|
|
{
|
|
tmp_attr.attrmask |= LO_ATTR_BLINK;
|
|
}
|
|
else if(!strcasecomp(token, STRIKEOUT_STYLE))
|
|
{
|
|
tmp_attr.attrmask |= LO_ATTR_STRIKEOUT;
|
|
}
|
|
else if(!strcasecomp(token, UNDERLINE_STYLE))
|
|
{
|
|
tmp_attr.attrmask |= LO_ATTR_UNDERLINE;
|
|
}
|
|
else
|
|
{
|
|
/* all others no decoration */
|
|
}
|
|
|
|
token = lo_find_next_style_token(&end_token);
|
|
}
|
|
push_font = TRUE;
|
|
XP_FREE(property);
|
|
}
|
|
|
|
if(push_font)
|
|
{
|
|
attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
/* don't use the tag type since the A tag is treated special
|
|
* and we don't want that special treatment
|
|
*/
|
|
lo_PushFont(state, P_UNKNOWN, attr);
|
|
STYLESTRUCT_SetString(style_struct, STYLE_NEED_TO_POP_FONT, "1", 0);
|
|
}
|
|
|
|
}
|
|
|
|
static SS_Number *
|
|
lo_get_border_width(MWContext *context, lo_DocState *state, StyleStruct *style_struct, char *which_one)
|
|
{
|
|
char *property = STYLESTRUCT_GetString(style_struct, which_one);
|
|
SS_Number *width;
|
|
|
|
if(!property)
|
|
return NULL;
|
|
|
|
if(!strcasecomp(property, "thin"))
|
|
{
|
|
width = STYLESTRUCT_StringToSSNumber(style_struct, "1px");
|
|
lo_adjust_border_thickness_for_style(style_struct, width);
|
|
}
|
|
else if(!strcasecomp(property, "medium"))
|
|
{
|
|
width = STYLESTRUCT_StringToSSNumber(style_struct, "3px");
|
|
lo_adjust_border_thickness_for_style(style_struct, width);
|
|
}
|
|
else if(!strcasecomp(property, "thick"))
|
|
{
|
|
width = STYLESTRUCT_StringToSSNumber(style_struct, "5px");
|
|
lo_adjust_border_thickness_for_style(style_struct, width);
|
|
}
|
|
else
|
|
{
|
|
width = STYLESTRUCT_StringToSSNumber(style_struct, property);
|
|
}
|
|
|
|
LO_AdjustSSUnits(width, which_one, context, state);
|
|
|
|
return(width);
|
|
}
|
|
|
|
PRIVATE
|
|
void
|
|
lo_SetStyleSheetBoxProperties(MWContext *context,
|
|
lo_DocState *state,
|
|
StyleStruct *style_struct,
|
|
PA_Tag *tag)
|
|
{
|
|
SS_Number *left_margin, *right_margin;
|
|
SS_Number *left_padding, *right_padding;
|
|
SS_Number *text_indent;
|
|
SS_Number *text_width;
|
|
char *align_value, *bgimage_value, *border_style_value, *border_color_value;
|
|
SS_Number *borderwidth_value;
|
|
SS_Number *bordertopwidth_value;
|
|
SS_Number *borderbottomwidth_value;
|
|
SS_Number *borderrightwidth_value;
|
|
SS_Number *borderleftwidth_value;
|
|
char *display_prop, *align_property, *page_break_property;
|
|
Bool use_table_for_box=FALSE;
|
|
int32 left_margin_offset=0, right_margin_offset=0;
|
|
Bool is_table_relayout_begin_dummy_tag=FALSE;
|
|
|
|
if(!style_struct)
|
|
return;
|
|
|
|
/* if we get in here and the tag->type is UKNOWN then this must
|
|
* be a dummy tag inserted for the sake of table relayout passes
|
|
* Mark it as such
|
|
*/
|
|
if(tag->type == P_UNKNOWN)
|
|
is_table_relayout_begin_dummy_tag = TRUE;
|
|
|
|
page_break_property = STYLESTRUCT_GetString(style_struct, PAGE_BREAK_BEFORE_STYLE);
|
|
if (page_break_property)
|
|
{
|
|
if(!strcasecomp(page_break_property, "auto"))
|
|
{
|
|
/* not currently supported */
|
|
}
|
|
else if(!strcasecomp(page_break_property, "always"))
|
|
{
|
|
/* not currently supported */
|
|
}
|
|
else if(!strcasecomp(page_break_property, "left"))
|
|
{
|
|
/* not currently supported */
|
|
}
|
|
else if(!strcasecomp(page_break_property, "right"))
|
|
{
|
|
/* not currently supported */
|
|
}
|
|
XP_FREE(page_break_property);
|
|
}
|
|
|
|
/* add a linebreak for block elements */
|
|
display_prop = STYLESTRUCT_GetString(style_struct, DISPLAY_STYLE);
|
|
if(display_prop && !strcasecomp(display_prop, BLOCK_STYLE))
|
|
{
|
|
if (state->in_paragraph)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
|
|
lo_SetLineBreakState(context, state, FALSE,
|
|
LO_LINEFEED_BREAK_PARAGRAPH, 1, FALSE);
|
|
}
|
|
else if(display_prop && !strcasecomp(display_prop, LIST_ITEM_STYLE))
|
|
{
|
|
char *bullet_type;
|
|
char *list_style_prop = STYLESTRUCT_GetString(style_struct,
|
|
LIST_STYLE_TYPE_STYLE);
|
|
|
|
|
|
if(list_style_prop)
|
|
bullet_type = list_style_prop;
|
|
else
|
|
bullet_type = "disc";
|
|
|
|
lo_setup_list(state,
|
|
context,
|
|
tag,
|
|
bullet_type,
|
|
NULL, /* start point */
|
|
NULL); /* compact */
|
|
|
|
XP_FREEIF(list_style_prop);
|
|
|
|
STYLESTRUCT_SetString(style_struct, STYLE_NEED_TO_POP_LIST, "1", 0);
|
|
|
|
}
|
|
XP_FREEIF(display_prop);
|
|
|
|
left_margin = STYLESTRUCT_GetNumber(style_struct, LEFTMARGIN_STYLE);
|
|
LO_AdjustSSUnits(left_margin, LEFTMARGIN_STYLE, context, state);
|
|
right_margin = STYLESTRUCT_GetNumber(style_struct, RIGHTMARGIN_STYLE);
|
|
LO_AdjustSSUnits(right_margin, RIGHTMARGIN_STYLE, context, state);
|
|
|
|
|
|
/* don't allow negative margins */
|
|
if(left_margin
|
|
#ifndef ALLOW_NEG_MARGINS
|
|
&& left_margin->value > 0
|
|
#endif
|
|
)
|
|
left_margin_offset = (int32)left_margin->value;
|
|
|
|
if(right_margin && right_margin->value > 0)
|
|
right_margin_offset = (int32)right_margin->value;
|
|
|
|
left_padding = STYLESTRUCT_GetNumber(style_struct, LEFTPADDING_STYLE);
|
|
LO_AdjustSSUnits(left_padding, LEFTPADDING_STYLE, context, state);
|
|
|
|
right_padding = STYLESTRUCT_GetNumber(style_struct, RIGHTPADDING_STYLE);
|
|
LO_AdjustSSUnits(right_padding, RIGHTPADDING_STYLE, context, state);
|
|
|
|
text_indent = STYLESTRUCT_GetNumber(style_struct, TEXTINDENT_STYLE);
|
|
LO_AdjustSSUnits(text_indent, TEXTINDENT_STYLE, context, state);
|
|
|
|
text_width = STYLESTRUCT_GetNumber(style_struct, WIDTH_STYLE);
|
|
LO_AdjustSSUnits(text_width, WIDTH_STYLE, context, state);
|
|
|
|
/* process bottom margin and padding in PopTag
|
|
* bottom_margin = STYLESTRUCT_GetNumber(style_struct, BOTTOMMARGIN_STYLE);
|
|
*/
|
|
|
|
align_value = STYLESTRUCT_GetString(style_struct, HORIZONTAL_ALIGN_STYLE);
|
|
if(align_value && !strcasecomp(align_value, "none"))
|
|
{
|
|
XP_FREE(align_value);
|
|
align_value = NULL;
|
|
}
|
|
|
|
bgimage_value = STYLESTRUCT_GetString(style_struct, BG_IMAGE_STYLE);
|
|
if(bgimage_value && !strcasecomp(bgimage_value, "none"))
|
|
{
|
|
XP_FREEIF(bgimage_value);
|
|
bgimage_value = NULL;
|
|
}
|
|
|
|
if(bgimage_value
|
|
&& (tag->type == P_BODY
|
|
|| tag->type == P_TABLE_DATA /* these tags can do their own backgrounds */
|
|
|| tag->type == P_TABLE_HEADER
|
|
|| tag->type == P_TABLE_ROW))
|
|
{
|
|
XP_FREEIF(bgimage_value);
|
|
bgimage_value = NULL;
|
|
}
|
|
|
|
borderwidth_value = lo_get_border_width(context, state, style_struct,
|
|
BORDERWIDTH_STYLE);
|
|
bordertopwidth_value = lo_get_border_width(context, state, style_struct,
|
|
BORDERTOPWIDTH_STYLE);
|
|
borderbottomwidth_value = lo_get_border_width(context, state, style_struct,
|
|
BORDERBOTTOMWIDTH_STYLE);
|
|
borderleftwidth_value = lo_get_border_width(context, state, style_struct,
|
|
BORDERLEFTWIDTH_STYLE);
|
|
borderrightwidth_value = lo_get_border_width(context, state, style_struct,
|
|
BORDERRIGHTWIDTH_STYLE);
|
|
|
|
border_style_value = STYLESTRUCT_GetString(style_struct, BORDER_STYLE_STYLE);
|
|
border_color_value = STYLESTRUCT_GetString(style_struct, BORDER_COLOR_STYLE);
|
|
|
|
/* do backgrounds and floats using tables */
|
|
if(!(tag->type == P_TABLE
|
|
|| tag->type == P_TABLE_DATA
|
|
|| tag->type == P_TABLE_HEADER
|
|
|| tag->type == P_TABLE_ROW) /* never create a table for these tags */
|
|
&& (align_value
|
|
|| bgimage_value
|
|
|| (borderwidth_value && borderwidth_value->value > 0)
|
|
|| (bordertopwidth_value && bordertopwidth_value->value > 0)
|
|
|| (borderbottomwidth_value && borderbottomwidth_value->value > 0)
|
|
|| (borderrightwidth_value && borderrightwidth_value->value > 0)
|
|
|| (borderleftwidth_value && borderleftwidth_value->value > 0)))
|
|
{
|
|
use_table_for_box = TRUE;
|
|
|
|
/* use a solid border as the default */
|
|
if(!border_style_value)
|
|
border_style_value = XP_STRDUP("solid");
|
|
}
|
|
else
|
|
{
|
|
/* add the paddings to the margin widths since there will not
|
|
* be a table
|
|
*/
|
|
if(left_padding && left_padding->value > 0)
|
|
left_margin_offset += (int32)left_padding->value;
|
|
|
|
if(right_padding && right_padding->value > 0)
|
|
right_margin_offset += (int32)right_padding->value;
|
|
}
|
|
|
|
|
|
if(left_margin_offset || right_margin_offset || (text_width && !use_table_for_box))
|
|
{
|
|
/* only set the list to be pop'd for styles outside
|
|
* a table created by this tag
|
|
*
|
|
* TRICKY!: don't set margins for the dummy tag that started
|
|
* a table, since a table already has established margins
|
|
*/
|
|
if(!is_table_relayout_begin_dummy_tag)
|
|
{
|
|
STYLESTRUCT_SetString(style_struct, STYLE_NEED_TO_POP_MARGINS, "1", 0);
|
|
lo_SetNewMarginsForStyle(state,
|
|
tag,
|
|
style_struct,
|
|
text_width ? (int32)text_width->value : 0,
|
|
left_margin_offset,
|
|
right_margin_offset);
|
|
}
|
|
}
|
|
|
|
/* handle text alignment here too, even though it's not really a font */
|
|
align_property = STYLESTRUCT_GetString(style_struct, TEXT_ALIGN_STYLE);
|
|
if (align_property)
|
|
{
|
|
intn alignment = lo_EvalDivisionAlignParam(align_property);
|
|
|
|
lo_PushAlignment(state, tag->type, alignment);
|
|
STYLESTRUCT_SetString(style_struct,
|
|
STYLE_NEED_TO_POP_ALIGNMENT,
|
|
"1",
|
|
0);
|
|
XP_FREE(align_property);
|
|
}
|
|
|
|
|
|
|
|
/* add vertical formatting
|
|
* but only if we are at the beginning of a line
|
|
*/
|
|
if(state->at_begin_line)
|
|
{
|
|
int32 move_size;
|
|
SS_Number *ss_num=NULL;
|
|
char *line_height_char_prop;
|
|
SS_Number *top_margin;
|
|
int32 line_height_diff = 0;
|
|
|
|
line_height_char_prop = STYLESTRUCT_GetString(style_struct, LINE_HEIGHT_STYLE);
|
|
|
|
if(line_height_char_prop)
|
|
{
|
|
if(!strcasecomp(line_height_char_prop, "normal"))
|
|
{
|
|
ss_num = NULL;
|
|
}
|
|
else
|
|
{
|
|
ss_num = STYLESTRUCT_StringToSSNumber(style_struct,
|
|
line_height_char_prop);
|
|
}
|
|
LO_AdjustSSUnits(ss_num, LINE_HEIGHT_STYLE, context, state);
|
|
|
|
XP_FREE(line_height_char_prop);
|
|
}
|
|
|
|
/* calculate a y offset to do a correct line height */
|
|
if (ss_num)
|
|
{
|
|
int32 cur_line_height = lo_CalcCurrentLineHeight(context, state);
|
|
|
|
if(ss_num->value < 0)
|
|
ss_num->value = 0; /* negative lineheight values are always illegal */
|
|
|
|
/* adjust line-height for printing */
|
|
ss_num->value = FEUNITS_Y(ss_num->value, context);
|
|
|
|
line_height_diff = ((int32)ss_num->value) - cur_line_height;
|
|
|
|
#ifndef ALLOW_NEG_MARGINS
|
|
/* only allow increasing line heights
|
|
* explicitly disallow negative diffs
|
|
* NOTE: this is different than an explicit negative line-height.
|
|
*/
|
|
if(line_height_diff < 0)
|
|
{
|
|
line_height_diff = 0;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* push the height onto the line height stack so that it can be used
|
|
* at the beginning of every line
|
|
* Note: this wont happen if the margin would cause negative spacing.
|
|
*/
|
|
lo_PushLineHeight(state, (int32)ss_num->value);
|
|
STYLESTRUCT_SetString(style_struct, STYLE_NEED_TO_POP_LINE_HEIGHT, "1", 0);
|
|
}
|
|
|
|
STYLESTRUCT_FreeSSNumber(style_struct, ss_num);
|
|
|
|
/* don't apply y offset if we are in the table
|
|
* relayout step
|
|
*/
|
|
if(!is_table_relayout_begin_dummy_tag)
|
|
state->y += line_height_diff;
|
|
}
|
|
|
|
top_margin = STYLESTRUCT_GetNumber(style_struct, TOPMARGIN_STYLE);
|
|
LO_AdjustSSUnits(top_margin, TOPMARGIN_STYLE, context, state);
|
|
|
|
/* don't apply y offset if we are in the table
|
|
* relayout step
|
|
*/
|
|
if(!is_table_relayout_begin_dummy_tag
|
|
&& top_margin
|
|
#ifndef ALLOW_NEG_MARGINS
|
|
&& top_margin->value > -1
|
|
#endif
|
|
)
|
|
{
|
|
int32 prev_line_dist = lo_calc_distance_to_bottom_of_prev_line(state);
|
|
|
|
move_size = FEUNITS_Y((int32)top_margin->value, context) - prev_line_dist;
|
|
|
|
#ifndef ALLOW_NEG_MARGINS
|
|
if(move_size > 0)
|
|
#endif
|
|
state->y += move_size;
|
|
}
|
|
|
|
STYLESTRUCT_FreeSSNumber(style_struct, top_margin);
|
|
|
|
#ifdef ALLOW_NEG_MARGINS
|
|
if(state->y < 0)
|
|
state->y = 0;
|
|
#endif
|
|
|
|
/* now add top padding if there is any and we arn't using table padding */
|
|
if(!use_table_for_box)
|
|
{
|
|
SS_Number *top_padding = STYLESTRUCT_GetNumber(style_struct, TOPPADDING_STYLE);
|
|
LO_AdjustSSUnits(top_padding, TOPPADDING_STYLE, context, state);
|
|
|
|
if(top_padding && top_padding->value > 0)
|
|
{
|
|
move_size = (int32)top_padding->value;
|
|
state->y += FEUNITS_Y(move_size, context);
|
|
}
|
|
|
|
STYLESTRUCT_FreeSSNumber(style_struct, top_padding);
|
|
}
|
|
|
|
}
|
|
|
|
/* the only way an unknown tag can get in here is if
|
|
* its a special relayout place holder for table styles
|
|
* we never want to redo the table in that case
|
|
*/
|
|
if(use_table_for_box
|
|
&& !is_table_relayout_begin_dummy_tag)
|
|
{
|
|
/* begin a table */
|
|
lo_DocState *prev_state;
|
|
LO_TextAttr tmp_attr, *attr;
|
|
|
|
SS_Number *top_padding, *bottom_padding;
|
|
|
|
/* begin table attributes */
|
|
char *align_attr= align_value;
|
|
char *border_attr=NULL;
|
|
char *border_top_attr=NULL;
|
|
char *border_bottom_attr=NULL;
|
|
char *border_left_attr=NULL;
|
|
char *border_right_attr=NULL;
|
|
char *border_color_attr=border_color_value;
|
|
char *border_style_attr=border_style_value;
|
|
char *vspace_attr=NULL;
|
|
char *hspace_attr=NULL;
|
|
char *bgcolor_attr=NULL;
|
|
char *width_attr= NULL;
|
|
char *height_attr=NULL;
|
|
char *cellpad_attr="0"; /* default to zero */
|
|
char *toppad_attr=NULL;
|
|
char *bottompad_attr=NULL;
|
|
char *leftpad_attr=NULL;
|
|
char *rightpad_attr=NULL;
|
|
char *cellspace_attr=NULL;
|
|
char *cols_attr=NULL;
|
|
|
|
/* begin table row attributes */
|
|
char * row_valign_attr= NULL;
|
|
char * row_halign_attr= NULL;
|
|
|
|
/* begin table data attributes */
|
|
char * colspan_attr= NULL;
|
|
char * rowspan_attr= NULL;
|
|
char * nowrap_attr= NULL;
|
|
char * cell_bgcolor_attr= NULL;
|
|
char * cell_bgimage_attr=lo_ParseStyleSheetURL(bgimage_value);
|
|
char * cell_valign_attr= NULL;
|
|
char * cell_halign_attr= NULL;
|
|
char * cell_width_attr= NULL;
|
|
char * cell_height_attr= NULL;
|
|
Bool is_a_header = FALSE;
|
|
|
|
/* the right_margin setting doesn't really effect tables so we need to use
|
|
* the table width setting to get the desired effect
|
|
*
|
|
* if right_margin == 5000 then the margin is really unknown and we cant
|
|
* do correct margin calculations
|
|
*/
|
|
if(text_width || (state->right_margin != 5000 && (right_margin || left_margin)))
|
|
{
|
|
int32 table_width = state->right_margin - state->left_margin;
|
|
|
|
if(text_width && table_width > text_width->value)
|
|
table_width = (int32)text_width->value;
|
|
|
|
width_attr = PR_smprintf("%ld", table_width);
|
|
}
|
|
|
|
if(left_padding)
|
|
leftpad_attr = PR_smprintf("%ld", (int32)left_padding->value);
|
|
if(right_padding)
|
|
rightpad_attr = PR_smprintf("%ld", (int32)right_padding->value);
|
|
|
|
/* top and bottom padding values */
|
|
top_padding = STYLESTRUCT_GetNumber(style_struct, TOPPADDING_STYLE);
|
|
LO_AdjustSSUnits(top_padding, TOPPADDING_STYLE, context, state);
|
|
|
|
if(top_padding)
|
|
{
|
|
toppad_attr = PR_smprintf("%ld", (int32)top_padding->value);
|
|
STYLESTRUCT_FreeSSNumber(style_struct, top_padding);
|
|
}
|
|
|
|
bottom_padding = STYLESTRUCT_GetNumber(style_struct, BOTTOMPADDING_STYLE);
|
|
LO_AdjustSSUnits(bottom_padding, BOTTOMPADDING_STYLE, context, state);
|
|
|
|
if(bottom_padding)
|
|
{
|
|
bottompad_attr = PR_smprintf("%ld", (int32)bottom_padding->value);
|
|
STYLESTRUCT_FreeSSNumber(style_struct, bottom_padding);
|
|
}
|
|
|
|
if(borderwidth_value)
|
|
border_attr = PR_smprintf("%ld", (int32)borderwidth_value->value);
|
|
if(bordertopwidth_value)
|
|
border_top_attr = PR_smprintf("%ld", (int32)bordertopwidth_value->value);
|
|
if(borderbottomwidth_value)
|
|
border_bottom_attr = PR_smprintf("%ld", (int32)borderbottomwidth_value->value);
|
|
if(borderleftwidth_value)
|
|
border_left_attr = PR_smprintf("%ld", (int32)borderleftwidth_value->value);
|
|
if(borderrightwidth_value)
|
|
border_right_attr = PR_smprintf("%ld", (int32)borderrightwidth_value->value);
|
|
|
|
|
|
bgcolor_attr = STYLESTRUCT_GetString(style_struct, BG_COLOR_STYLE);
|
|
if(bgcolor_attr && !strcasecomp(bgcolor_attr, "transparent"))
|
|
{
|
|
XP_FREEIF(bgcolor_attr);
|
|
bgcolor_attr = NULL;
|
|
}
|
|
|
|
/* mark that we are in a table */
|
|
STYLESTRUCT_SetString(style_struct, STYLE_NEED_TO_POP_TABLE, "1", 0);
|
|
|
|
lo_BeginTableAttributes(context,
|
|
state,
|
|
align_attr,
|
|
border_attr,
|
|
border_top_attr,
|
|
border_bottom_attr,
|
|
border_left_attr,
|
|
border_right_attr,
|
|
border_color_attr,
|
|
border_style_attr,
|
|
vspace_attr,
|
|
hspace_attr,
|
|
bgcolor_attr,
|
|
NULL, /* Backdrop URL */
|
|
width_attr,
|
|
height_attr,
|
|
cellpad_attr,
|
|
toppad_attr,
|
|
bottompad_attr,
|
|
leftpad_attr,
|
|
rightpad_attr,
|
|
cellspace_attr,
|
|
cols_attr);
|
|
|
|
XP_FREEIF(width_attr);
|
|
|
|
if(state->sub_state)
|
|
state = state->sub_state;
|
|
|
|
if(state->current_table)
|
|
{
|
|
|
|
/* change the vertical alignment to top so that top and bottom
|
|
* margins work correctly. "Center" seems to be the default
|
|
*/
|
|
row_valign_attr = strdup("TOP");
|
|
|
|
/* begin a table row */
|
|
lo_BeginTableRowAttributes(context,
|
|
state,
|
|
state->current_table,
|
|
bgcolor_attr,
|
|
NULL, /* Backdrop URL */
|
|
row_valign_attr,
|
|
row_halign_attr);
|
|
|
|
XP_FREEIF(row_valign_attr);
|
|
|
|
if(state->sub_state)
|
|
state = state->sub_state;
|
|
|
|
if(state->current_table->row_ptr)
|
|
{
|
|
/* now begin the table data */
|
|
lo_BeginTableCellAttributes(context,
|
|
state,
|
|
state->current_table,
|
|
colspan_attr,
|
|
rowspan_attr,
|
|
nowrap_attr,
|
|
cell_bgcolor_attr,
|
|
cell_bgimage_attr, /* Backdrop URL */
|
|
LO_TILE_BOTH, /* Backdrop tiling mode */
|
|
cell_valign_attr,
|
|
cell_halign_attr,
|
|
cell_width_attr,
|
|
cell_height_attr,
|
|
is_a_header,
|
|
FALSE); /* no cell borders */
|
|
}
|
|
}
|
|
|
|
prev_state = state;
|
|
if(state->sub_state)
|
|
state = state->sub_state;
|
|
|
|
XP_FREEIF(bgcolor_attr);
|
|
|
|
/* inherit the font stack from the prev state to allow proper inheritance
|
|
* rules -- as far as I know we don't need to inherit any other props now
|
|
*/
|
|
|
|
lo_CopyTextAttr(prev_state->font_stack->text_attr, &tmp_attr);
|
|
attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
attr->no_background = TRUE; /* don't inherit text background colors */
|
|
|
|
/* don't use the tag type since the A tag is treated special
|
|
* and we don't want that special treatment
|
|
*/
|
|
lo_PushFont(state, P_UNKNOWN, attr);
|
|
|
|
if(tag->type == P_PARAGRAPH)
|
|
state->in_paragraph = TRUE;
|
|
|
|
/* after starting a table we must reset all the font properties */
|
|
lo_SetStyleSheetFontProperties(context, state, style_struct, tag, FALSE);
|
|
|
|
}
|
|
|
|
/* add text indent to the current X position */
|
|
if(text_indent)
|
|
state->x += (int32)text_indent->value;
|
|
|
|
XP_FREEIF(align_value);
|
|
XP_FREEIF(bgimage_value);
|
|
XP_FREEIF(border_style_value);
|
|
XP_FREEIF(border_color_value);
|
|
|
|
STYLESTRUCT_FreeSSNumber(style_struct, right_margin);
|
|
STYLESTRUCT_FreeSSNumber(style_struct, left_margin);
|
|
|
|
STYLESTRUCT_FreeSSNumber(style_struct, right_padding);
|
|
STYLESTRUCT_FreeSSNumber(style_struct, left_padding);
|
|
|
|
STYLESTRUCT_FreeSSNumber(style_struct, text_indent);
|
|
STYLESTRUCT_FreeSSNumber(style_struct, text_width);
|
|
|
|
STYLESTRUCT_FreeSSNumber(style_struct, bordertopwidth_value);
|
|
STYLESTRUCT_FreeSSNumber(style_struct, borderbottomwidth_value);
|
|
STYLESTRUCT_FreeSSNumber(style_struct, borderrightwidth_value);
|
|
STYLESTRUCT_FreeSSNumber(style_struct, borderleftwidth_value);
|
|
}
|
|
|
|
PRIVATE
|
|
void
|
|
lo_SetStyleSheetRandomProperties(MWContext *context,
|
|
lo_DocState *state,
|
|
StyleStruct *style_struct,
|
|
PA_Tag *tag)
|
|
{
|
|
char * property;
|
|
|
|
property = STYLESTRUCT_GetString(style_struct, WHITESPACE_STYLE);
|
|
if (property)
|
|
{
|
|
if(!strcasecomp(property, "PRE"))
|
|
{
|
|
state->preformatted = PRE_TEXT_YES;
|
|
FE_BeginPreSection(context);
|
|
STYLESTRUCT_SetString(style_struct, STYLE_NEED_TO_POP_PRE, "1", 0);
|
|
}
|
|
else if(!strcasecomp(property, "NORMAL"))
|
|
{
|
|
if(state->preformatted == PRE_TEXT_YES)
|
|
{
|
|
state->preformatted = PRE_TEXT_NO;
|
|
FE_EndPreSection(context);
|
|
STYLESTRUCT_SetString(style_struct,
|
|
STYLE_NEED_TO_RESET_PRE,
|
|
"1",
|
|
0);
|
|
}
|
|
}
|
|
else if(!strcasecomp(property, "NOWRAP"))
|
|
{
|
|
/* not currently supported */
|
|
}
|
|
XP_FREE(property);
|
|
}
|
|
|
|
property = STYLESTRUCT_GetString(style_struct, CLEAR_STYLE);
|
|
if (property)
|
|
{
|
|
if (!strcasecomp(property, "left"))
|
|
{
|
|
lo_HardLineBreak(context, state, FALSE);
|
|
lo_ClearToLeftMargin(context, state);
|
|
}
|
|
else if (!strcasecomp(property, "right"))
|
|
{
|
|
lo_HardLineBreak(context, state, FALSE);
|
|
lo_ClearToRightMargin(context, state);
|
|
}
|
|
else if (!strcasecomp(property, "both"))
|
|
{
|
|
lo_HardLineBreak(context, state, FALSE);
|
|
lo_ClearToBothMargins(context, state);
|
|
}
|
|
/* note that "none" means to not clear or BR at all */
|
|
|
|
/*
|
|
* Reset the margins properly in case
|
|
* we are inside a list.
|
|
*/
|
|
lo_FindLineMargins(context, state, TRUE);
|
|
state->x = state->left_margin;
|
|
XP_FREE(property);
|
|
}
|
|
}
|
|
|
|
|
|
PRIVATE
|
|
void
|
|
lo_SetStyleSheetProperties(MWContext *context,
|
|
StyleStruct *style_struct,
|
|
PA_Tag *tag)
|
|
{
|
|
int32 doc_id;
|
|
lo_TopState *top_state;
|
|
lo_DocState *state;
|
|
|
|
if(tag->type == P_TEXT || !style_struct)
|
|
return;
|
|
|
|
/* get a fresh state since it may get free'd by
|
|
* previous table code
|
|
*/
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
state = lo_TopSubState(top_state);
|
|
|
|
if(!state)
|
|
return;
|
|
|
|
/* ignore uknown tags unless we are in the relayout phase */
|
|
if(tag->type == P_UNKNOWN && state->in_relayout == FALSE)
|
|
return;
|
|
|
|
/* if we are hiding content skip applying styles */
|
|
if(state->hide_content)
|
|
return;
|
|
|
|
/* optimization!
|
|
*
|
|
* if there we know that there are no styles for this tag
|
|
* return immediately
|
|
*/
|
|
if(STYLESTRUCT_Count(style_struct) == 0)
|
|
return;
|
|
|
|
/* check for display: none
|
|
* if set then we are ignoring tags and text for the duration
|
|
* of this tag span
|
|
*/
|
|
if(LO_CheckForContentHiding(state))
|
|
{
|
|
state->hide_content = TRUE;
|
|
STYLESTRUCT_SetString(style_struct,
|
|
STYLE_NEED_TO_POP_CONTENT_HIDING,
|
|
"1",
|
|
0);
|
|
/* don't apply any more properties */
|
|
return;
|
|
}
|
|
|
|
lo_SetStyleSheetFontProperties(context, state, style_struct, tag, TRUE);
|
|
|
|
/* if we get in here and the tag->type is UKNOWN then this must
|
|
* be a dummy tag inserted for the sake of table relayout passes
|
|
* don't open layers in this case because we would be creating
|
|
* two layers instead of just the one.
|
|
*/
|
|
if(tag->type != P_UNKNOWN)
|
|
lo_SetStyleSheetLayerProperties(context, state, style_struct, tag);
|
|
|
|
lo_SetStyleSheetBoxProperties (context, state, style_struct, tag);
|
|
|
|
/* BoxProperties may force a table. If that is the case then
|
|
* we need to get the new state
|
|
*/
|
|
doc_id = XP_DOCID(context);
|
|
top_state = lo_FetchTopState(doc_id);
|
|
state = lo_TopSubState(top_state);
|
|
|
|
if(!state)
|
|
return;
|
|
|
|
lo_SetStyleSheetRandomProperties(context, state, style_struct, tag);
|
|
|
|
}
|
|
|
|
/* return TRUE if the tag type is an empty tag. (not a container tag)
|
|
*/
|
|
Bool
|
|
lo_IsEmptyTag(TagType type)
|
|
{
|
|
if(type == P_OPTION
|
|
|| type == P_INPUT
|
|
|| type == P_INDEX
|
|
|| type == P_HRULE
|
|
|| type == P_HYPE
|
|
|| type == P_META
|
|
|| type == P_COLORMAP
|
|
|| type == P_LINEBREAK
|
|
|| type == P_SPACER
|
|
|| type == P_WORDBREAK
|
|
|| type == P_PARAM
|
|
|| type == P_IMAGE
|
|
|| type == P_NEW_IMAGE
|
|
|| type == P_EMBED
|
|
|| type == P_KEYGEN
|
|
|| type == P_JAVA_APPLET
|
|
#ifndef DOM
|
|
|| type == P_LIST_ITEM /* not really empty! */
|
|
|| type == P_DESC_TITLE
|
|
|| type == P_NSDT
|
|
|| type == P_DESC_TEXT
|
|
#endif
|
|
|| type == P_BASEFONT
|
|
|| type == P_AREA
|
|
|| type == P_BASE)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
static void lo_ProcessFontTag( lo_DocState *state, PA_Tag *tag, int32 fontSpecifier, int32 attrSpecifier )
|
|
{
|
|
LO_TextAttr tmp_attr;
|
|
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
LO_TextAttr *old_attr;
|
|
LO_TextAttr *attr;
|
|
|
|
old_attr = state->font_stack->text_attr;
|
|
lo_CopyTextAttr(old_attr, &tmp_attr);
|
|
tmp_attr.fontmask |= fontSpecifier;
|
|
tmp_attr.attrmask |= attrSpecifier;
|
|
attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
|
|
lo_PushFont(state, tag->type, attr);
|
|
}
|
|
else
|
|
{
|
|
LO_TextAttr *attr;
|
|
|
|
attr = lo_PopFont(state, tag->type);
|
|
}
|
|
}
|
|
|
|
/*************************************
|
|
* Function: lo_LayoutTag
|
|
*
|
|
* Description: This function begins the process of laying
|
|
* out a single MDL tag.
|
|
*
|
|
* Params: Window context and document state, and the tag to be laid out.
|
|
*
|
|
* Returns: Nothing
|
|
*************************************/
|
|
void
|
|
lo_LayoutTag(MWContext *context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
char *tptr;
|
|
char *tptr2;
|
|
#ifdef OJI
|
|
char* javaPlugin;
|
|
#endif
|
|
LO_TextAttr tmp_attr;
|
|
StyleStruct *style_struct=NULL;
|
|
XP_Bool has_ss_bottom_margin=FALSE;
|
|
XP_Bool started_in_head=FALSE;
|
|
INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(context);
|
|
|
|
XP_ASSERT(state);
|
|
|
|
if(state->top_state)
|
|
started_in_head = state->top_state->in_head;
|
|
|
|
#ifdef LOCAL_DEBUG
|
|
XP_TRACE(("lo_LayoutTag(%d)\n", tag->type));
|
|
#endif /* LOCAL_DEBUG */
|
|
|
|
/*
|
|
* see if there are any &{} constructs for this tag. If so,
|
|
* convert them now, if we can't convert them (since we couldn't
|
|
* get the mocha lock, for example) block layout and try to do
|
|
* this tag convertion again later
|
|
*/
|
|
/*
|
|
* Don't expand these inside XMP and PLAINTEXT or when the text
|
|
* is coming from a document.write()
|
|
*/
|
|
if (state->allow_amp_escapes != FALSE && tag->type != P_TEXT &&
|
|
(state->top_state->input_write_level == 0 ||
|
|
state->top_state->tag_from_inline_stream == TRUE))
|
|
{
|
|
if (lo_ConvertMochaEntities(context, state, tag) == FALSE) {
|
|
state->top_state->wedged_on_mocha = TRUE;
|
|
lo_CloneTagAndBlockLayout(context, state, tag);
|
|
return;
|
|
}
|
|
}
|
|
|
|
lo_PreLayoutTag(context, state, tag);
|
|
if ( state->top_state->out_of_memory )
|
|
return;
|
|
|
|
/* implicitly pop tags before we push a new one on the stack
|
|
* the call to this may alter the state variable due to
|
|
* a table closure
|
|
*/
|
|
LO_ImplicitPop(context, &state, tag);
|
|
|
|
/* Call into the style sheet code to let it know
|
|
* that we are starting a new tag
|
|
*
|
|
* Since we need to get the id and class out of
|
|
* the tag->data call an LO function first
|
|
*/
|
|
if(!tag->is_end)
|
|
{
|
|
PushTagStatus push_status;
|
|
|
|
push_status = LO_PushTagOnStyleStack(context, state, tag);
|
|
|
|
if(push_status == PUSH_TAG_SUCCESS) {
|
|
if(state->top_state->style_stack)
|
|
style_struct = STYLESTACK_GetStyleByIndex(state->top_state->style_stack, 0);
|
|
else
|
|
style_struct = NULL;
|
|
}
|
|
else if(push_status == PUSH_TAG_BLOCKED)
|
|
/* layout has been locked */
|
|
return;
|
|
else
|
|
style_struct = NULL; /* push error */
|
|
}
|
|
else
|
|
{
|
|
/* get has_bottom_margin flag for use in end P */
|
|
char *prop;
|
|
|
|
/* don't pop on unknown or empty tags.
|
|
* we have already pop'd empty tags.
|
|
*/
|
|
if((tag->type != P_UNKNOWN || state->in_relayout)
|
|
&& !lo_IsEmptyTag(tag->type))
|
|
{
|
|
if(state->top_state->style_stack)
|
|
style_struct = STYLESTACK_GetStyleByIndex(state->top_state->style_stack, 0);
|
|
else
|
|
style_struct = NULL;
|
|
|
|
if(style_struct
|
|
&& (prop = STYLESTRUCT_GetString(style_struct, BOTTOMMARGIN_STYLE)) != NULL)
|
|
{
|
|
has_ss_bottom_margin = TRUE;
|
|
XP_FREE(prop);
|
|
}
|
|
|
|
/* pop the previous style tag, since this is an end tag */
|
|
LO_PopStyleTag(context, &state, tag);
|
|
|
|
/* if we have a pointer to the style struct it will
|
|
* be dangling now since we just pop'd and detroyed the
|
|
* top style struct.
|
|
*/
|
|
style_struct = NULL;
|
|
}
|
|
}
|
|
|
|
if(tag->type != P_TEXT && tag->type != P_UNKNOWN)
|
|
{
|
|
XP_ASSERT(state->top_state);
|
|
if(state->top_state)
|
|
state->top_state->tag_count++;
|
|
}
|
|
|
|
LO_LockLayout();
|
|
|
|
if(!tag->is_end && lo_IsEmptyTag(tag->type))
|
|
{
|
|
lo_SetStyleSheetProperties(context, style_struct, tag);
|
|
}
|
|
|
|
switch(tag->type)
|
|
{
|
|
case P_TEXT:
|
|
if(!state->hide_content)
|
|
lo_process_text_tag(context, state, tag);
|
|
break;
|
|
|
|
/*
|
|
* Title text is special, it is not displayed, just
|
|
* captured and sent to the front end.
|
|
*/
|
|
case P_TITLE:
|
|
lo_process_title_tag(context, state, tag);
|
|
break;
|
|
|
|
/*
|
|
* Certificate text is special, it is not displayed,
|
|
* but decoded and saved for later reference.
|
|
*/
|
|
case P_CERTIFICATE:
|
|
#ifdef HTML_CERTIFICATE_SUPPORT
|
|
lo_process_certificate_tag(context, state, tag);
|
|
#endif
|
|
break;
|
|
|
|
/*
|
|
* All these tags just flip the fixed width font
|
|
* bit, and push a new font on the font stack.
|
|
*/
|
|
case P_FIXED:
|
|
case P_CODE:
|
|
case P_SAMPLE:
|
|
case P_KEYBOARD:
|
|
lo_ProcessFontTag( state, tag, LO_FONT_FIXED, 0);
|
|
break;
|
|
|
|
#ifdef EDITOR
|
|
process_server:
|
|
case P_SERVER:
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
LO_TextAttr *old_attr;
|
|
LO_TextAttr *attr;
|
|
|
|
state->preformatted = PRE_TEXT_YES;
|
|
FE_BeginPreSection(context);
|
|
|
|
old_attr = state->font_stack->text_attr;
|
|
lo_CopyTextAttr(old_attr, &tmp_attr);
|
|
tmp_attr.fontmask |= LO_FONT_FIXED;
|
|
if( tag->type == P_SCRIPT ){
|
|
tmp_attr.fg.green = 0;
|
|
tmp_attr.fg.red = 0xff;
|
|
tmp_attr.fg.blue = 0;
|
|
}
|
|
else {
|
|
tmp_attr.fg.green = 0;
|
|
tmp_attr.fg.red = 0;
|
|
tmp_attr.fg.blue = 0xff;
|
|
}
|
|
tmp_attr.size = 3;
|
|
attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
|
|
lo_PushFont(state, tag->type, attr);
|
|
}
|
|
else
|
|
{
|
|
LO_TextAttr *attr;
|
|
|
|
attr = lo_PopFont(state, tag->type);
|
|
state->preformatted = PRE_TEXT_NO;
|
|
FE_EndPreSection(context);
|
|
}
|
|
|
|
break;
|
|
#endif
|
|
|
|
|
|
/*
|
|
* All these tags just flip the bold font
|
|
* bit, and push a new font on the font stack.
|
|
*/
|
|
case P_BOLD:
|
|
case P_STRONG:
|
|
lo_ProcessFontTag( state, tag, LO_FONT_BOLD, 0);
|
|
break;
|
|
|
|
/*
|
|
* All these tags just flip the italic font
|
|
* bit, and push a new font on the font stack.
|
|
*/
|
|
case P_ITALIC:
|
|
case P_EMPHASIZED:
|
|
case P_VARIABLE:
|
|
case P_CITATION:
|
|
lo_ProcessFontTag( state, tag, LO_FONT_ITALIC, 0);
|
|
break;
|
|
|
|
/*
|
|
* All headers force whitespace, then set the bold
|
|
* font bit and unset the fixed and/or italic if set.
|
|
* In addition, they set the size attribute of the
|
|
* new font they will push, like <font size=num>
|
|
* Sizes are:
|
|
* H1 -> 6
|
|
* H2 -> 5
|
|
* H3 -> 4
|
|
* H4 -> 3
|
|
* H5 -> 2
|
|
* H6 -> 1
|
|
*/
|
|
case P_HEADER_1:
|
|
lo_process_header_tag(context, state, tag, 6);
|
|
break;
|
|
case P_HEADER_2:
|
|
lo_process_header_tag(context, state, tag, 5);
|
|
break;
|
|
case P_HEADER_3:
|
|
lo_process_header_tag(context, state, tag, 4);
|
|
break;
|
|
case P_HEADER_4:
|
|
lo_process_header_tag(context, state, tag, 3);
|
|
break;
|
|
case P_HEADER_5:
|
|
lo_process_header_tag(context, state, tag, 2);
|
|
break;
|
|
case P_HEADER_6:
|
|
lo_process_header_tag(context, state, tag, 1);
|
|
break;
|
|
|
|
/*
|
|
* Description titles are just normal,
|
|
* unless value > 1 and then they are outdented
|
|
* the width of one list indention.
|
|
*/
|
|
case P_NSDT:
|
|
/*
|
|
* The start of a list infers the end of
|
|
* the HEAD section of the HTML and starts the BODY
|
|
*/
|
|
state->top_state->in_head = FALSE;
|
|
state->top_state->in_body = TRUE;
|
|
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
/*
|
|
* Undent to the left for a title if necessary.
|
|
*/
|
|
if ((state->list_stack->level != 0)&&
|
|
(state->list_stack->type == P_DESC_LIST)&&
|
|
(state->list_stack->value > 1))
|
|
{
|
|
state->list_stack->old_left_margin -=
|
|
LIST_MARGIN_INC;
|
|
state->list_stack->value = 1;
|
|
}
|
|
|
|
if (state->linefeed_state >= 1)
|
|
{
|
|
lo_FindLineMargins(context, state,FALSE);
|
|
}
|
|
else
|
|
{
|
|
lo_SetLineBreakState(context,state,FALSE,LO_LINEFEED_BREAK_HARD, 1, FALSE);
|
|
}
|
|
state->x = state->left_margin;
|
|
}
|
|
break;
|
|
case P_DESC_TITLE:
|
|
/*
|
|
* The start of a list infers the end of
|
|
* the HEAD section of the HTML and starts the BODY
|
|
*/
|
|
state->top_state->in_head = FALSE;
|
|
state->top_state->in_body = TRUE;
|
|
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
LO_DescTitleStruct *title = (LO_DescTitleStruct*)lo_NewElement(context, state, LO_DESCTITLE, NULL, 0);
|
|
|
|
XP_ASSERT(title);
|
|
if (!title)
|
|
{
|
|
LO_UnlockLayout();
|
|
return;
|
|
}
|
|
|
|
title->lo_any.type = LO_DESCTITLE;
|
|
title->lo_any.ele_id = NEXT_ELEMENT;
|
|
|
|
title->lo_any.x = state->x;
|
|
title->lo_any.y = state->y;
|
|
title->lo_any.x_offset = 0;
|
|
title->lo_any.y_offset = 0;
|
|
title->lo_any.width = 0;
|
|
title->lo_any.height = 0;
|
|
title->lo_any.line_height = 0;
|
|
|
|
lo_AppendToLineList(context, state, (LO_Element*)title, 0);
|
|
|
|
lo_ProcessDescTitleElement(context, state, title, FALSE);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Description text, is just normal text, but since there
|
|
* is no ending <dd> tag, we need to put the
|
|
* linebreak from the previous line in here.
|
|
* Compact lists may not linebreak since they try to
|
|
* put description text on the same line as title
|
|
* text if there is room.
|
|
* If value == 1 then we need to indent for the description
|
|
* text, if it is > 1 then we are already indented.
|
|
*/
|
|
case P_DESC_TEXT:
|
|
/*
|
|
* The start of a list infers the end of
|
|
* the HEAD section of the HTML and starts the BODY
|
|
*/
|
|
state->top_state->in_head = FALSE;
|
|
state->top_state->in_body = TRUE;
|
|
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
LO_DescTextStruct *text = (LO_DescTextStruct*)lo_NewElement(context, state, LO_DESCTEXT, NULL, 0);
|
|
|
|
XP_ASSERT(text);
|
|
if (!text)
|
|
{
|
|
LO_UnlockLayout();
|
|
return;
|
|
}
|
|
|
|
text->lo_any.type = LO_DESCTEXT;
|
|
text->lo_any.ele_id = NEXT_ELEMENT;
|
|
|
|
text->lo_any.x = state->x;
|
|
text->lo_any.y = state->y;
|
|
text->lo_any.x_offset = 0;
|
|
text->lo_any.y_offset = 0;
|
|
text->lo_any.width = 0;
|
|
text->lo_any.height = 0;
|
|
text->lo_any.line_height = 0;
|
|
|
|
lo_AppendToLineList(context, state, (LO_Element*)text, 0);
|
|
|
|
lo_ProcessDescTextElement(context, state, text, FALSE);
|
|
}
|
|
break;
|
|
/*
|
|
* A new list level, but don't move the
|
|
* left margin in until you need to.
|
|
* val is overloaded to tell us if we are
|
|
* not indented (val=1) or indented (val > 1).
|
|
* Push a new element on the list stack.
|
|
*/
|
|
case P_DESC_LIST:
|
|
/*
|
|
* The start of a list infers the end of
|
|
* the HEAD section of the HTML and starts the BODY
|
|
*/
|
|
state->top_state->in_head = FALSE;
|
|
state->top_state->in_body = TRUE;
|
|
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
PA_Block buff;
|
|
char * t_ptr;
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_COMPACT);
|
|
|
|
PA_LOCK(t_ptr, char *, buff);
|
|
|
|
lo_setup_list(state,
|
|
context,
|
|
tag,
|
|
NULL,
|
|
NULL,
|
|
t_ptr);
|
|
|
|
PA_UNLOCK(buff);
|
|
|
|
if (buff) PA_FREE(buff);
|
|
}
|
|
else /* tag->is_end is TRUE */
|
|
{
|
|
lo_TeardownList(context, state, tag);
|
|
}
|
|
|
|
break;
|
|
/*
|
|
* Move the left margin in another indent step.
|
|
* Push a new element on the list stack.
|
|
*/
|
|
case P_NUM_LIST:
|
|
case P_UNUM_LIST:
|
|
case P_MENU:
|
|
case P_DIRECTORY:
|
|
/* case P_MQUOTE: mquote tag disabled, replaced by <blockquote mail> */
|
|
/*
|
|
* The start of a list infers the end of
|
|
* the HEAD section of the HTML and starts the BODY
|
|
*/
|
|
state->top_state->in_head = FALSE;
|
|
state->top_state->in_body = TRUE;
|
|
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
char *bullet_type;
|
|
char *bullet_start;
|
|
char *compact_attr;
|
|
|
|
bullet_type = (char*)lo_FetchParamValue(context,
|
|
tag,
|
|
PARAM_TYPE);
|
|
|
|
bullet_start = (char*)lo_FetchParamValue(context,
|
|
tag,
|
|
PARAM_START);
|
|
|
|
compact_attr = (char*)lo_FetchParamValue(context,
|
|
tag,
|
|
PARAM_COMPACT);
|
|
|
|
lo_setup_list(state,
|
|
context,
|
|
tag,
|
|
bullet_type,
|
|
bullet_start,
|
|
compact_attr);
|
|
|
|
XP_FREEIF(bullet_type);
|
|
XP_FREEIF(bullet_start);
|
|
XP_FREEIF(compact_attr);
|
|
}
|
|
/* tag->is_end is TRUE */
|
|
else {
|
|
lo_TeardownList(context, state, tag);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Complex because we have to place one of many types
|
|
* of bullet here, or one of several types of numbers.
|
|
*/
|
|
case P_LIST_ITEM:
|
|
/*
|
|
* The start of a list infers the end of
|
|
* the HEAD section of the HTML and starts the BODY
|
|
*/
|
|
state->top_state->in_head = FALSE;
|
|
state->top_state->in_body = TRUE;
|
|
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
if (tag->is_end == FALSE && !state->hide_content)
|
|
{
|
|
int type;
|
|
int bullet_type;
|
|
PA_Block buff;
|
|
|
|
/*
|
|
* Reset fake linefeed state used to
|
|
* fake out headers in lists.
|
|
*/
|
|
if (state->at_begin_line == FALSE)
|
|
{
|
|
state->linefeed_state = 0;
|
|
}
|
|
|
|
lo_SetLineBreakState(context, state, FALSE, LO_LINEFEED_BREAK_HARD, 1, FALSE);
|
|
/*
|
|
* Artificially setting state to 2 here
|
|
* allows us to put headers on list items
|
|
* even if they aren't double spaced.
|
|
*/
|
|
state->linefeed_state = 2;
|
|
|
|
type = state->list_stack->bullet_type;
|
|
buff = lo_FetchParamValue(context, tag, PARAM_TYPE);
|
|
if ((buff != NULL)&&
|
|
(state->list_stack->type == P_NUM_LIST))
|
|
{
|
|
char *type_str;
|
|
|
|
PA_LOCK(type_str, char *, buff);
|
|
if (*type_str == 'A')
|
|
{
|
|
type = BULLET_ALPHA_L;
|
|
}
|
|
else if (*type_str == 'a')
|
|
{
|
|
type = BULLET_ALPHA_S;
|
|
}
|
|
else if (*type_str == 'I')
|
|
{
|
|
type = BULLET_NUM_L_ROMAN;
|
|
}
|
|
else if (*type_str == 'i')
|
|
{
|
|
type = BULLET_NUM_S_ROMAN;
|
|
}
|
|
else
|
|
{
|
|
type = BULLET_NUM;
|
|
}
|
|
PA_UNLOCK(buff);
|
|
}
|
|
else if ((buff != NULL)&&
|
|
(state->list_stack->type !=P_DESC_LIST))
|
|
{
|
|
char *type_str;
|
|
|
|
PA_LOCK(type_str, char *, buff);
|
|
if (pa_TagEqual("round", type_str))
|
|
{
|
|
type = BULLET_ROUND;
|
|
}
|
|
else if (pa_TagEqual("circle",type_str))
|
|
{
|
|
type = BULLET_ROUND;
|
|
}
|
|
else if (pa_TagEqual("square",type_str))
|
|
{
|
|
type = BULLET_SQUARE;
|
|
}
|
|
else
|
|
{
|
|
type = BULLET_BASIC;
|
|
}
|
|
PA_UNLOCK(buff);
|
|
}
|
|
state->list_stack->bullet_type = type;
|
|
|
|
if (buff != NULL)
|
|
{
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_VALUE);
|
|
if (buff != NULL)
|
|
{
|
|
int32 val;
|
|
char *val_str;
|
|
|
|
PA_LOCK(val_str, char *, buff);
|
|
val = XP_ATOI(val_str);
|
|
if (val < 1)
|
|
{
|
|
val = 1;
|
|
}
|
|
PA_UNLOCK(buff);
|
|
state->list_stack->value = val;
|
|
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
bullet_type = state->list_stack->bullet_type;
|
|
if ((bullet_type == BULLET_NUM)||
|
|
(bullet_type == BULLET_ALPHA_L)||
|
|
(bullet_type == BULLET_ALPHA_S)||
|
|
(bullet_type == BULLET_NUM_L_ROMAN)||
|
|
(bullet_type == BULLET_NUM_S_ROMAN))
|
|
{
|
|
lo_PlaceBulletStr(context, state);
|
|
state->list_stack->value++;
|
|
}
|
|
else
|
|
{
|
|
lo_PlaceBullet(context, state);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Another font attribute, another font on the font stack
|
|
*/
|
|
case P_BLINK:
|
|
/*
|
|
* Blink is ignored in the editor.
|
|
*/
|
|
if ( EDT_IS_EDITOR(context) ) {
|
|
break;
|
|
}
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
LO_TextAttr *old_attr;
|
|
LO_TextAttr *attr;
|
|
|
|
old_attr = state->font_stack->text_attr;
|
|
lo_CopyTextAttr(old_attr, &tmp_attr);
|
|
tmp_attr.attrmask |= LO_ATTR_BLINK;
|
|
attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
|
|
lo_PushFont(state, tag->type, attr);
|
|
}
|
|
else
|
|
{
|
|
LO_TextAttr *attr;
|
|
|
|
attr = lo_PopFont(state, tag->type);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Another font attribute, another font on the font stack
|
|
* Because of spec mutation, two tags do the same thing.
|
|
* <s> and <strike> are the same.
|
|
*/
|
|
case P_STRIKEOUT:
|
|
case P_STRIKE:
|
|
lo_ProcessFontTag( state, tag, 0, LO_ATTR_STRIKEOUT);
|
|
break;
|
|
|
|
case P_SPELL:
|
|
lo_ProcessFontTag( state, tag, 0, LO_ATTR_SPELL);
|
|
break;
|
|
|
|
|
|
case P_INLINEINPUT:
|
|
lo_ProcessFontTag( state, tag, 0, LO_ATTR_INLINEINPUT);
|
|
break;
|
|
|
|
|
|
case P_INLINEINPUTTHICK:
|
|
lo_ProcessFontTag( state, tag, 0, LO_ATTR_INLINEINPUTTHICK);
|
|
break;
|
|
|
|
|
|
case P_INLINEINPUTDOTTED:
|
|
lo_ProcessFontTag( state, tag, 0, LO_ATTR_INLINEINPUTDOTTED);
|
|
break;
|
|
|
|
/*
|
|
* Another font attribute, another font on the font stack
|
|
*/
|
|
case P_UNDERLINE:
|
|
lo_ProcessFontTag( state, tag, 0, LO_ATTR_UNDERLINE);
|
|
break;
|
|
|
|
/*
|
|
* Center all following lines. Forces a single
|
|
* line break to start and end the new centered lines.
|
|
*/
|
|
case P_CENTER:
|
|
lo_process_center_tag(context, state, tag);
|
|
break;
|
|
|
|
/*
|
|
* For plaintext there is no endtag, so this is
|
|
* really the last tag will we be parsing if this
|
|
* is a plaintext tag.
|
|
*
|
|
* Difference between these three tags are
|
|
* all based on what things the parser allows to
|
|
* be tags while inside them.
|
|
*/
|
|
case P_PREFORMAT:
|
|
case P_PLAIN_PIECE:
|
|
case P_PLAIN_TEXT:
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
LO_TextAttr *old_attr;
|
|
LO_TextAttr *attr;
|
|
PA_Block buff;
|
|
|
|
lo_SetLineBreakState(context, state, FALSE, LO_LINEFEED_BREAK_HARD,
|
|
2, FALSE);
|
|
|
|
if ((tag->type == P_PLAIN_PIECE)||
|
|
(tag->type == P_PLAIN_TEXT))
|
|
{
|
|
state->allow_amp_escapes = FALSE;
|
|
}
|
|
|
|
state->preformatted = PRE_TEXT_YES;
|
|
FE_BeginPreSection(context);
|
|
|
|
/*
|
|
* Special WRAP attribute of the PRE tag
|
|
* to make wrapped pre sections.
|
|
*/
|
|
if (tag->type == P_PREFORMAT)
|
|
{
|
|
buff = lo_FetchParamValue(context, tag,
|
|
PARAM_WRAP);
|
|
if (buff != NULL)
|
|
{
|
|
state->preformatted =
|
|
PRE_TEXT_WRAP;
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
/*
|
|
* Wrap at a certain column.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag,
|
|
PARAM_COLS);
|
|
if (buff != NULL)
|
|
{
|
|
char * str;
|
|
int32 val;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
val = XP_ATOI(str);
|
|
if (val < 0)
|
|
{
|
|
val = 0;
|
|
}
|
|
|
|
state->preformat_cols = val;
|
|
state->preformatted =
|
|
PRE_TEXT_COLS;
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
/*
|
|
* Special TABSTOP attribute of the PRE tag
|
|
* to set the tab stop width.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag,
|
|
PARAM_TABSTOP);
|
|
if (buff != NULL)
|
|
{
|
|
char * str;
|
|
int32 val;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
val = XP_ATOI(str);
|
|
|
|
/*
|
|
* Note that I made a tabwidth of 0 be an
|
|
* illegal value. lo_PreformatedText divides
|
|
* with tab_stop.
|
|
*/
|
|
if ( val <= 0 )
|
|
{
|
|
val = DEF_TAB_WIDTH;
|
|
}
|
|
state->tab_stop = val;
|
|
PA_FREE(buff);
|
|
}
|
|
}
|
|
|
|
old_attr = state->font_stack->text_attr;
|
|
lo_CopyTextAttr(old_attr, &tmp_attr);
|
|
|
|
/*
|
|
* Allow <PRE VARIABLE> to create a variable
|
|
* width font preformatted section. This
|
|
* is only to be used internally for
|
|
* mail and news in Netscape 2.0 and greater.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_VARIABLE);
|
|
if ((tag->type == P_PREFORMAT)&&(buff != NULL))
|
|
{
|
|
tmp_attr.fontmask &= (~(LO_FONT_FIXED));
|
|
}
|
|
else
|
|
{
|
|
tmp_attr.fontmask |= LO_FONT_FIXED;
|
|
}
|
|
/*
|
|
* If VARIABLE attribute was there, free
|
|
* the value buffer.
|
|
*/
|
|
if (buff != NULL)
|
|
{
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
|
|
lo_PushFont(state, tag->type, attr);
|
|
}
|
|
else
|
|
{
|
|
LO_TextAttr *attr;
|
|
|
|
attr = lo_PopFont(state, tag->type);
|
|
|
|
state->preformat_cols = 0;
|
|
state->preformatted = PRE_TEXT_NO;
|
|
state->allow_amp_escapes = TRUE;
|
|
lo_SetLineBreakState(context, state, FALSE, LO_LINEFEED_BREAK_HARD,
|
|
2, FALSE);
|
|
FE_EndPreSection(context);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Historic text type. Paragraph break, font size change
|
|
* and font set to fixed. Was supposed to support
|
|
* lineprinter listings if you can believe that!
|
|
*/
|
|
case P_LISTING_TEXT:
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
LO_TextAttr *old_attr;
|
|
LO_TextAttr *attr;
|
|
|
|
lo_SetSoftLineBreakState(context, state, FALSE, 2);
|
|
|
|
state->preformatted = PRE_TEXT_YES;
|
|
FE_BeginPreSection(context);
|
|
|
|
old_attr = state->font_stack->text_attr;
|
|
lo_CopyTextAttr(old_attr, &tmp_attr);
|
|
tmp_attr.fontmask |= LO_FONT_FIXED;
|
|
tmp_attr.size = DEFAULT_BASE_FONT_SIZE - 1;
|
|
attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
|
|
lo_PushFont(state, tag->type, attr);
|
|
}
|
|
else
|
|
{
|
|
LO_TextAttr *attr;
|
|
|
|
attr = lo_PopFont(state, tag->type);
|
|
|
|
state->preformatted = PRE_TEXT_NO;
|
|
lo_SetSoftLineBreakState(context, state, FALSE, 2);
|
|
FE_EndPreSection(context);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Another historic tag. Break a line, and switch to italic.
|
|
*/
|
|
case P_ADDRESS:
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
LO_TextAttr *old_attr;
|
|
LO_TextAttr *attr;
|
|
|
|
lo_SetSoftLineBreakState(context, state, FALSE, 1);
|
|
|
|
old_attr = state->font_stack->text_attr;
|
|
lo_CopyTextAttr(old_attr, &tmp_attr);
|
|
tmp_attr.fontmask |= LO_FONT_ITALIC;
|
|
attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
|
|
lo_PushFont(state, tag->type, attr);
|
|
}
|
|
else
|
|
{
|
|
LO_TextAttr *attr;
|
|
|
|
attr = lo_PopFont(state, tag->type);
|
|
|
|
lo_SetSoftLineBreakState(context, state, FALSE, 1);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Subscript. Move down so the middle of the ascent
|
|
* of the new text aligns with the baseline of the old.
|
|
*/
|
|
case P_SUB:
|
|
if (state->font_stack != NULL)
|
|
{
|
|
PA_Block buff;
|
|
char *str;
|
|
LO_TextStruct tmp_text;
|
|
LO_TextInfo text_info;
|
|
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
LO_TextAttr *old_attr;
|
|
LO_TextAttr *attr;
|
|
intn new_size;
|
|
|
|
old_attr = state->font_stack->text_attr;
|
|
lo_CopyTextAttr(old_attr, &tmp_attr);
|
|
new_size = LO_ChangeFontSize(tmp_attr.size,
|
|
"-1");
|
|
tmp_attr.size = new_size;
|
|
attr = lo_FetchTextAttr(state,
|
|
&tmp_attr);
|
|
|
|
lo_PushFont(state, tag->type, attr);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
LO_UnlockLayout();
|
|
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);
|
|
|
|
{
|
|
LO_SubStruct *sub = (LO_SubStruct*)lo_NewElement(context, state, LO_SUB, NULL, 0);
|
|
|
|
XP_ASSERT(sub);
|
|
if (!sub)
|
|
{
|
|
LO_UnlockLayout();
|
|
return;
|
|
}
|
|
|
|
sub->lo_any.type = LO_SUB;
|
|
sub->lo_any.ele_id = NEXT_ELEMENT;
|
|
|
|
sub->is_end = tag->is_end;
|
|
sub->baseline_adj = text_info.ascent / 2;
|
|
|
|
lo_AppendToLineList(context, state, (LO_Element*)sub, 0);
|
|
|
|
lo_ProcessSubElement(context, state, sub);
|
|
}
|
|
|
|
if (tag->is_end == TRUE)
|
|
{
|
|
LO_TextAttr *attr = lo_PopFont(state, tag->type);
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Superscript. Move up so the baseline of the new text
|
|
* is raised above the baseline of the old by 1/2 the
|
|
* ascent of the new text.
|
|
*/
|
|
case P_SUPER:
|
|
if (state->font_stack != NULL)
|
|
{
|
|
PA_Block buff;
|
|
char *str;
|
|
LO_TextStruct tmp_text;
|
|
LO_TextInfo text_info;
|
|
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
LO_TextAttr *old_attr;
|
|
LO_TextAttr *attr;
|
|
intn new_size;
|
|
|
|
old_attr = state->font_stack->text_attr;
|
|
lo_CopyTextAttr(old_attr, &tmp_attr);
|
|
new_size = LO_ChangeFontSize(tmp_attr.size,
|
|
"-1");
|
|
tmp_attr.size = new_size;
|
|
attr = lo_FetchTextAttr(state,
|
|
&tmp_attr);
|
|
|
|
lo_PushFont(state, tag->type, attr);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
LO_UnlockLayout();
|
|
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);
|
|
|
|
{
|
|
LO_SuperStruct *super = (LO_SuperStruct*)lo_NewElement(context, state, LO_SUPER, NULL, 0);
|
|
|
|
XP_ASSERT(super);
|
|
if (!super)
|
|
{
|
|
LO_UnlockLayout();
|
|
return;
|
|
}
|
|
|
|
super->lo_any.type = LO_SUPER;
|
|
super->lo_any.ele_id = NEXT_ELEMENT;
|
|
super->is_end = tag->is_end;
|
|
super->baseline_adj = text_info.ascent / 2;
|
|
|
|
lo_AppendToLineList(context, state, (LO_Element*)super, 0);
|
|
|
|
lo_ProcessSuperElement(context, state, super);
|
|
}
|
|
|
|
if (tag->is_end == TRUE)
|
|
{
|
|
LO_TextAttr *attr = lo_PopFont(state, tag->type);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Change the base font size that relative font size changes
|
|
* are figured from.
|
|
*/
|
|
case P_BASEFONT:
|
|
{
|
|
LO_TextAttr *old_attr;
|
|
LO_TextAttr *attr;
|
|
PA_Block buff;
|
|
intn new_size;
|
|
char *size_str;
|
|
lo_FontStack *fptr;
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_SIZE);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(size_str, char *, buff);
|
|
new_size = LO_ChangeFontSize(
|
|
state->base_font_size,
|
|
size_str);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
else
|
|
{
|
|
new_size = DEFAULT_BASE_FONT_SIZE;
|
|
}
|
|
state->base_font_size = new_size;
|
|
|
|
fptr = state->font_stack;
|
|
/*
|
|
* If the current font is the basefont
|
|
* we are changing the current font size.
|
|
*/
|
|
if (fptr->tag_type == P_UNKNOWN)
|
|
{
|
|
/*
|
|
* Flush all current text before
|
|
* changing the font size.
|
|
*/
|
|
lo_FlushTextBlock(context, state);
|
|
}
|
|
|
|
/*
|
|
* Traverse the font stack to the base font
|
|
* entry.
|
|
*/
|
|
while (fptr->tag_type != P_UNKNOWN)
|
|
{
|
|
fptr = fptr->next;
|
|
}
|
|
|
|
/*
|
|
* Copy the old base entry, change its
|
|
* size, and stuff the new one in its place
|
|
*/
|
|
old_attr = fptr->text_attr;
|
|
lo_CopyTextAttr(old_attr, &tmp_attr);
|
|
tmp_attr.size = state->base_font_size;
|
|
attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
if (attr != NULL)
|
|
{
|
|
fptr->text_attr = attr;
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
/*
|
|
* Change font, currently only lets you change size.
|
|
* Now let you change color.
|
|
* Now let you change FACE.
|
|
*/
|
|
case P_FONT:
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
LO_TextAttr *old_attr;
|
|
LO_TextAttr *attr;
|
|
PA_Block buff;
|
|
intn new_size;
|
|
uint16 new_point_size=0;
|
|
uint16 new_font_weight=0;
|
|
char *size_str;
|
|
char *str;
|
|
char *new_face=NULL;
|
|
Bool has_face;
|
|
Bool has_size;
|
|
Bool has_point_size;
|
|
Bool has_font_weight = FALSE;
|
|
Bool new_colors;
|
|
uint8 red, green, blue;
|
|
|
|
new_size = state->base_font_size;
|
|
has_size = FALSE;
|
|
has_point_size = FALSE;
|
|
has_font_weight = FALSE;
|
|
has_face = FALSE;
|
|
buff = lo_FetchParamValue(context, tag, PARAM_SIZE);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(size_str, char *, buff);
|
|
new_size = LO_ChangeFontSize(
|
|
state->base_font_size,
|
|
size_str);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
has_size = TRUE;
|
|
}
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_POINT_SIZE);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(size_str, char *, buff);
|
|
new_point_size = LO_ChangePointSize(
|
|
DEFAULT_BASE_POINT_SIZE,
|
|
size_str);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
has_point_size = TRUE;
|
|
}
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_FONT_WEIGHT);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(size_str, char *, buff);
|
|
new_font_weight = LO_ChangeFontOrPointSize(
|
|
DEFAULT_BASE_FONT_WEIGHT,
|
|
size_str,
|
|
MIN_FONT_WEIGHT,
|
|
MAX_FONT_WEIGHT);
|
|
|
|
/* normalize 100, 200, ... 900 */
|
|
new_font_weight = new_font_weight - (new_font_weight % 100);
|
|
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
has_font_weight = TRUE;
|
|
}
|
|
|
|
/*
|
|
* For backwards compatibility, a font
|
|
* tag with NO attributes will reset the
|
|
* font size to the base size.
|
|
*/
|
|
if (PA_TagHasParams(tag) == FALSE)
|
|
{
|
|
new_size = state->base_font_size;
|
|
has_size = TRUE;
|
|
}
|
|
|
|
new_colors = FALSE;
|
|
buff = lo_FetchParamValue(context, tag, PARAM_COLOR);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff);
|
|
LO_ParseRGB(str, &red, &green, &blue);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
new_colors = TRUE;
|
|
}
|
|
|
|
if (lo_face_attribute())
|
|
{
|
|
buff = lo_FetchParamValue(context, tag,
|
|
PARAM_FACE);
|
|
new_face = NULL;
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff);
|
|
new_face = lo_FetchFontFace(context, state,
|
|
str);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
has_face = TRUE;
|
|
}
|
|
}
|
|
|
|
old_attr = state->font_stack->text_attr;
|
|
lo_CopyTextAttr(old_attr, &tmp_attr);
|
|
|
|
if (has_size != FALSE)
|
|
{
|
|
tmp_attr.size = new_size;
|
|
}
|
|
|
|
if (has_point_size)
|
|
tmp_attr.point_size = (double)new_point_size;
|
|
|
|
if (has_font_weight)
|
|
tmp_attr.font_weight = new_font_weight;
|
|
|
|
if (new_colors != FALSE)
|
|
{
|
|
tmp_attr.fg.red = red;
|
|
tmp_attr.fg.green = green;
|
|
tmp_attr.fg.blue = blue;
|
|
}
|
|
|
|
if (has_face != FALSE)
|
|
{
|
|
tmp_attr.font_face = new_face;
|
|
}
|
|
|
|
attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
|
|
lo_PushFont(state, tag->type, attr);
|
|
}
|
|
else
|
|
{
|
|
LO_TextAttr *attr;
|
|
|
|
attr = lo_PopFont(state, tag->type);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Change font to bigger than current font.
|
|
*/
|
|
case P_BIG:
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
LO_TextAttr *old_attr;
|
|
LO_TextAttr *attr;
|
|
intn new_size;
|
|
|
|
old_attr = state->font_stack->text_attr;
|
|
lo_CopyTextAttr(old_attr, &tmp_attr);
|
|
new_size = LO_ChangeFontSize(tmp_attr.size, "+1");
|
|
tmp_attr.size = new_size;
|
|
attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
|
|
lo_PushFont(state, tag->type, attr);
|
|
}
|
|
else
|
|
{
|
|
LO_TextAttr *attr;
|
|
|
|
attr = lo_PopFont(state, tag->type);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Change font to smaller than current font.
|
|
*/
|
|
case P_SMALL:
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
LO_TextAttr *old_attr;
|
|
LO_TextAttr *attr;
|
|
intn new_size;
|
|
|
|
old_attr = state->font_stack->text_attr;
|
|
lo_CopyTextAttr(old_attr, &tmp_attr);
|
|
new_size = LO_ChangeFontSize(tmp_attr.size, "-1");
|
|
tmp_attr.size = new_size;
|
|
attr = lo_FetchTextAttr(state, &tmp_attr);
|
|
|
|
lo_PushFont(state, tag->type, attr);
|
|
}
|
|
else
|
|
{
|
|
LO_TextAttr *attr;
|
|
|
|
attr = lo_PopFont(state, tag->type);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Change the absolute URL that describes where this
|
|
* docuemtn came from for all following
|
|
* relative URLs.
|
|
*/
|
|
case P_BASE:
|
|
{
|
|
PA_Block buff;
|
|
char *url;
|
|
char *target;
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_HREF);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(url, char *, buff);
|
|
if (url != NULL)
|
|
{
|
|
int32 len;
|
|
|
|
len = lo_StripTextWhitespace(
|
|
url, XP_STRLEN(url));
|
|
}
|
|
lo_SetBaseUrl(state->top_state, url, FALSE);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_TARGET);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(target, char *, buff);
|
|
if (target != NULL)
|
|
{
|
|
int32 len;
|
|
|
|
len = lo_StripTextWhitespace(
|
|
target, XP_STRLEN(target));
|
|
}
|
|
if (lo_IsValidTarget(target) == FALSE)
|
|
{
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
#ifdef XP_OS2_FIX /*trap on double free of buff...*/
|
|
buff = NULL;
|
|
#endif
|
|
target = NULL;
|
|
}
|
|
if (state->top_state->base_target !=
|
|
NULL)
|
|
{
|
|
XP_FREE(
|
|
state->top_state->base_target);
|
|
state->top_state->base_target =NULL;
|
|
}
|
|
if (target != NULL)
|
|
{
|
|
state->top_state->base_target =
|
|
XP_STRDUP(target);
|
|
}
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Begin an anchor. Change font colors if
|
|
* there is an HREF here. Make the HREF absolute
|
|
* based on the base url. Check name in case we
|
|
* are jumping to a named anchor inside this document.
|
|
*/
|
|
case P_ANCHOR:
|
|
lo_process_anchor_tag(context, state, tag);
|
|
if (state->top_state->out_of_memory != FALSE)
|
|
{
|
|
LO_UnlockLayout();
|
|
return;
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Insert a wordbreak. Allows layout to break a line
|
|
* at this location in case it needs to.
|
|
*/
|
|
case P_WORDBREAK:
|
|
lo_InsertWordBreak(context, state);
|
|
break;
|
|
|
|
/*
|
|
* All text inside these tags must be considered one
|
|
* word, and not broken (unless there is a word break
|
|
* in there).
|
|
*/
|
|
case P_NOBREAK:
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
state->breakable = FALSE;
|
|
}
|
|
else
|
|
{
|
|
state->breakable = TRUE;
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* spacer element to move stuff around
|
|
*/
|
|
case P_SPACER:
|
|
if (tag->is_end == FALSE && !state->hide_content)
|
|
{
|
|
lo_ProcessSpacerTag(context, state, tag);
|
|
}
|
|
break;
|
|
|
|
case P_MULTICOLUMN:
|
|
lo_process_multicolumn_tag(context, state, tag);
|
|
break;
|
|
|
|
case P_LAYER:
|
|
case P_ILAYER:
|
|
|
|
if(state->hide_content)
|
|
{
|
|
break; /* skip layer */
|
|
}
|
|
else if (tag->is_end == FALSE)
|
|
{
|
|
/* Can't have a layer inside a form */
|
|
if (state->top_state->in_form != FALSE)
|
|
{
|
|
lo_EndForm(context, state);
|
|
lo_SetSoftLineBreakState(context, state, FALSE, 2);
|
|
}
|
|
|
|
lo_BeginLayerTag(context, state, tag);
|
|
}
|
|
else
|
|
{
|
|
/* Don't close blocks unless they were opened in this scope. */
|
|
if (state->layer_nest_level > 0)
|
|
{
|
|
lo_EndLayerTag(context, state, tag);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Break the current line.
|
|
* Optionally break furthur down to get past floating
|
|
* elements in the margins.
|
|
*/
|
|
case P_LINEBREAK:
|
|
|
|
{
|
|
uint8 clear_type = LO_CLEAR_NONE;
|
|
|
|
if(state->hide_content)
|
|
break; /* skip the whole tag */
|
|
|
|
{
|
|
PA_Block buff;
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_CLEAR);
|
|
if (buff != NULL)
|
|
{
|
|
char *clear_str;
|
|
|
|
PA_LOCK(clear_str, char *, buff);
|
|
if (pa_TagEqual("left", clear_str))
|
|
{
|
|
clear_type = LO_CLEAR_TO_LEFT;
|
|
}
|
|
else if (pa_TagEqual("right", clear_str))
|
|
{
|
|
clear_type = LO_CLEAR_TO_RIGHT;
|
|
}
|
|
else if (pa_TagEqual("all", clear_str))
|
|
{
|
|
clear_type = LO_CLEAR_TO_BOTH;
|
|
}
|
|
else if (pa_TagEqual("both", clear_str))
|
|
{
|
|
clear_type = LO_CLEAR_TO_BOTH;
|
|
}
|
|
else if (pa_TagEqual("none", clear_str))
|
|
{
|
|
clear_type = LO_CLEAR_NONE;
|
|
}
|
|
else
|
|
{
|
|
/* default to clear none */
|
|
clear_type = LO_CLEAR_NONE;
|
|
}
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
/*
|
|
* Reset the margins properly in case
|
|
* we are inside a list.
|
|
*/
|
|
lo_FindLineMargins(context, state, TRUE);
|
|
state->x = state->left_margin;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* <br> tag diabled in preformatted text
|
|
*/
|
|
#ifdef EDITOR
|
|
if (state->preformatted != PRE_TEXT_NO && !EDT_IS_EDITOR(context))
|
|
#else
|
|
if (state->preformatted != PRE_TEXT_NO)
|
|
#endif
|
|
{
|
|
lo_SetSoftLineBreakState(context, state, FALSE, 1);
|
|
break;
|
|
}
|
|
lo_HardLineBreakWithClearType(context, state, clear_type, FALSE);
|
|
#ifdef EDITOR
|
|
/* editor hack. Make breaks always do something. */
|
|
if ( EDT_IS_EDITOR(context) ) {
|
|
state->linefeed_state = 0;
|
|
}
|
|
#endif
|
|
|
|
switch(clear_type)
|
|
{
|
|
case LO_CLEAR_TO_LEFT:
|
|
lo_ClearToLeftMargin(context, state);
|
|
break;
|
|
case LO_CLEAR_TO_RIGHT:
|
|
lo_ClearToRightMargin(context, state);
|
|
break;
|
|
case LO_CLEAR_TO_ALL:
|
|
case LO_CLEAR_TO_BOTH:
|
|
lo_ClearToBothMargins(context, state);
|
|
break;
|
|
case LO_CLEAR_NONE:
|
|
default:
|
|
break;
|
|
}
|
|
/*
|
|
* Reset the margins properly in case
|
|
* we are inside a list.
|
|
*/
|
|
lo_FindLineMargins(context, state, TRUE);
|
|
state->x = state->left_margin;
|
|
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* The head tag. The tag is supposed to be a container
|
|
* for the head of the HTML document.
|
|
* We use it to try and detect when we should
|
|
* suppress the content of unknown head tags.
|
|
*/
|
|
case P_HEAD:
|
|
/*
|
|
* The end HEAD tags defines the end of
|
|
* the HEAD section of the HTML
|
|
*/
|
|
if (tag->is_end != FALSE)
|
|
{
|
|
state->top_state->in_head = FALSE;
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* The body tag. The tag is supposed to be a container
|
|
* for the body of the HTML document.
|
|
* Right now we are just using it to specify
|
|
* the document qualities and colors.
|
|
*/
|
|
case P_BODY:
|
|
/*
|
|
* The start of BODY definitely ends
|
|
* the HEAD section of the HTML and starts the BODY
|
|
* Only process this body tag if we aren't already
|
|
* in a body.
|
|
*/
|
|
if (state->top_state->in_body == FALSE)
|
|
{
|
|
state->top_state->in_head = FALSE;
|
|
state->top_state->in_body = TRUE;
|
|
|
|
lo_ProcessBodyTag(context, state, tag);
|
|
}
|
|
else
|
|
{
|
|
state->top_state->in_head = FALSE;
|
|
state->top_state->in_body = TRUE;
|
|
/*
|
|
* We now process later bodies just to
|
|
* see if they set attributes not set by
|
|
* the first body.
|
|
*/
|
|
lo_ProcessBodyTag(context, state, tag);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* The colormap tag. This is used to specify the colormap
|
|
* to be used by all the images on this page.
|
|
*/
|
|
case P_COLORMAP:
|
|
if ((tag->is_end == FALSE)&&
|
|
(state->end_last_line == NULL))
|
|
{
|
|
PA_Block buff;
|
|
char *str;
|
|
char *image_url;
|
|
|
|
image_url = NULL;
|
|
buff = lo_FetchParamValue(context, tag, PARAM_SRC);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff);
|
|
if (str != NULL)
|
|
{
|
|
int32 len;
|
|
|
|
len = lo_StripTextWhitespace(
|
|
str, XP_STRLEN(str));
|
|
}
|
|
image_url = NET_MakeAbsoluteURL(
|
|
state->top_state->base_url,str);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
#ifndef M12N /* XXXM12N Fix me. Colormap tag has yet to
|
|
implemented. */
|
|
(void)IL_ColormapTag(image_url, context);
|
|
#endif /* M12N */
|
|
if (image_url != NULL)
|
|
{
|
|
XP_FREE(image_url);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* The meta tag. This tag can only appear in the
|
|
* head of the document.
|
|
* It overrides HTTP headers sent by the
|
|
* web server that served this document.
|
|
* If URLStruct somehow became NULL, META must
|
|
* be ignored.
|
|
*/
|
|
case P_META:
|
|
if ((tag->is_end == FALSE)&&
|
|
/* (state->end_last_line == NULL)&& allow in head */
|
|
(state->top_state->nurl != NULL))
|
|
{
|
|
PA_Block buff;
|
|
PA_Block buff2;
|
|
PA_Block buff3;
|
|
char *name;
|
|
char *value;
|
|
char *tptr;
|
|
|
|
buff = lo_FetchParamValue(context, tag, "rel");
|
|
if (buff != NULL)
|
|
{
|
|
char *rel_val = (char*)buff;
|
|
|
|
/* try and fetch the src */
|
|
buff2 = lo_FetchParamValue(context, tag, PARAM_SRC);
|
|
if (buff2 != NULL)
|
|
{
|
|
if(!strcasecomp(rel_val, "SMALL_BOOKMARK_ICON"))
|
|
{
|
|
state->top_state->small_bm_icon =
|
|
NET_MakeAbsoluteURL(state->top_state->base_url,
|
|
rel_val);
|
|
}
|
|
else if(!strcasecomp(rel_val, "LARGE_BOOKMARK_ICON"))
|
|
{
|
|
state->top_state->large_bm_icon =
|
|
NET_MakeAbsoluteURL(state->top_state->base_url,
|
|
rel_val);
|
|
}
|
|
|
|
PA_FREE(buff2);
|
|
}
|
|
|
|
PA_FREE(buff);
|
|
|
|
}
|
|
|
|
buff = lo_FetchParamValue(context, tag,
|
|
PARAM_HTTP_EQUIV);
|
|
if (buff != NULL)
|
|
{
|
|
/*
|
|
* Lou needs a colon on the end of
|
|
* the name.
|
|
*/
|
|
PA_LOCK(tptr, char *, buff);
|
|
buff3 = PA_ALLOC(XP_STRLEN(tptr) + 2);
|
|
if (buff3 != NULL)
|
|
{
|
|
PA_LOCK(name, char *, buff3);
|
|
XP_STRCPY(name, tptr);
|
|
XP_STRCAT(name, ":");
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
|
|
buff2 = lo_FetchParamValue(context, tag,
|
|
PARAM_CONTENT);
|
|
if (buff2 != NULL)
|
|
{
|
|
Bool ok;
|
|
|
|
PA_LOCK(value, char *, buff2);
|
|
ok = NET_ParseMimeHeader(
|
|
FO_PRESENT,
|
|
context,
|
|
state->top_state->nurl,
|
|
name, value, FALSE);
|
|
PA_UNLOCK(buff2);
|
|
PA_FREE(buff2);
|
|
if (INTL_GetCSIRelayoutFlag(c) == METACHARSET_REQUESTRELAYOUT)
|
|
{
|
|
INTL_SetCSIRelayoutFlag(c, METACHARSET_FORCERELAYOUT);
|
|
INTL_Relayout(context);
|
|
}
|
|
|
|
}
|
|
PA_UNLOCK(buff3);
|
|
PA_FREE(buff3);
|
|
}
|
|
else
|
|
{
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tag->is_end == FALSE && state && state->top_state)
|
|
{
|
|
/* Build up meta tags list for consumption by the RDF HT */
|
|
lo_TopState *top_state = state->top_state;
|
|
|
|
if ( top_state->metaTags == NULL )
|
|
{
|
|
top_state->metaTags = (TagList *) XP_NEW_ZAP( TagList );
|
|
top_state->metaTags->tagList = PA_CloneMDLTag( tag );
|
|
top_state->metaTags->lastTag = top_state->metaTags->tagList;
|
|
}
|
|
else
|
|
{
|
|
top_state->metaTags->lastTag->next = PA_CloneMDLTag( tag );
|
|
top_state->metaTags->lastTag = top_state->metaTags->lastTag->next;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* The hype tag is just for fun.
|
|
* It only effects the UNIX version
|
|
* which can affor to have a sound file
|
|
* compiled into the binary.
|
|
*/
|
|
case P_HYPE:
|
|
#if defined(XP_UNIX) || defined(XP_MAC)
|
|
if (tag->is_end == FALSE && !state->hide_content)
|
|
{
|
|
PA_Tag tmp_tag;
|
|
PA_Block buff;
|
|
PA_Block abuff;
|
|
LO_AnchorData *hold_current_anchor;
|
|
LO_AnchorData *hype_anchor;
|
|
char *str;
|
|
|
|
/*
|
|
* Try to allocate the hype anchor as if we
|
|
* were inside:
|
|
* <A HREF="HYPE_ANCHOR">
|
|
* Save the real current anchor to restore later
|
|
*/
|
|
hype_anchor = NULL;
|
|
abuff = PA_ALLOC(XP_STRLEN(HYPE_ANCHOR) + 1);
|
|
if (abuff != NULL)
|
|
{
|
|
hype_anchor = lo_NewAnchor(state, NULL, NULL);
|
|
if (hype_anchor == NULL)
|
|
{
|
|
PA_FREE(abuff);
|
|
abuff = NULL;
|
|
}
|
|
else
|
|
{
|
|
PA_LOCK(str, char *, abuff);
|
|
XP_STRCPY(str, HYPE_ANCHOR);
|
|
PA_UNLOCK(abuff);
|
|
|
|
hype_anchor->anchor = abuff;
|
|
hype_anchor->target = NULL;
|
|
|
|
/*
|
|
* Add this url's block to the list
|
|
* of all allocated urls so we can free
|
|
* it later.
|
|
*/
|
|
lo_AddToUrlList(context, state,
|
|
hype_anchor);
|
|
if (state->top_state->out_of_memory !=FALSE)
|
|
{
|
|
PA_FREE(abuff);
|
|
XP_DELETE(hype_anchor);
|
|
LO_UnlockLayout();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
hold_current_anchor = state->current_anchor;
|
|
state->current_anchor = hype_anchor;
|
|
|
|
/*
|
|
* If we have the memory, create a fake image
|
|
* tag to replace the <HYPE> tag and process it.
|
|
*/
|
|
buff = PA_ALLOC(XP_STRLEN(HYPE_TAG_BECOMES)+1);
|
|
if (buff != NULL)
|
|
{
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
XP_STRCPY(str, HYPE_TAG_BECOMES);
|
|
PA_UNLOCK(buff);
|
|
|
|
tmp_tag.type = P_IMAGE;
|
|
tmp_tag.is_end = FALSE;
|
|
tmp_tag.newline_count = tag->newline_count;
|
|
tmp_tag.data = buff;
|
|
tmp_tag.data_len = XP_STRLEN(HYPE_TAG_BECOMES);
|
|
tmp_tag.true_len = 0;
|
|
tmp_tag.lo_data = NULL;
|
|
tmp_tag.next = NULL;
|
|
lo_FormatImage(context, state, &tmp_tag);
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
/*
|
|
* Restore the proper current_anchor.
|
|
*/
|
|
state->current_anchor = hold_current_anchor;
|
|
}
|
|
#endif /* XP_UNIX */
|
|
break;
|
|
|
|
/*
|
|
* Insert a horizontal rule element.
|
|
* They always take up an entire line.
|
|
*/
|
|
case P_HRULE:
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
|
|
/*
|
|
* HR now does alignment the same as P, H?, and DIV.
|
|
* Ignore </hr>.
|
|
*/
|
|
if (tag->is_end == FALSE && !state->hide_content)
|
|
{
|
|
lo_SetSoftLineBreakState(context, state, FALSE, 1);
|
|
|
|
lo_HorizontalRule(context, state, tag);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Experimental! May be used to implement TABLES
|
|
*/
|
|
case P_CELL:
|
|
break;
|
|
|
|
/*
|
|
* The start of a caption in a table
|
|
*/
|
|
case P_CAPTION:
|
|
if(!state->hide_content)
|
|
lo_process_caption_tag(context, state, tag);
|
|
break;
|
|
|
|
/*
|
|
* The start of a header or data cell
|
|
* in a row in a table
|
|
*/
|
|
case P_TABLE_HEADER:
|
|
case P_TABLE_DATA:
|
|
if(!state->hide_content)
|
|
lo_process_table_cell_tag(context, state, tag);
|
|
break;
|
|
|
|
/*
|
|
* The start of a row in a table
|
|
*/
|
|
case P_TABLE_ROW:
|
|
if(!state->hide_content)
|
|
lo_process_table_row_tag(context, state, tag);
|
|
break;
|
|
|
|
/*
|
|
* The start of a table. Since this can cause the
|
|
* destruction of elements that others may be holding
|
|
* pointers to, make sure they are out of their critical
|
|
* sections before starting to layout the table
|
|
*/
|
|
case P_TABLE:
|
|
if(!state->hide_content)
|
|
lo_process_table_tag(context, state, tag);
|
|
break;
|
|
|
|
/*
|
|
* Experimental! Everything in these tags is a complete
|
|
* separate sub document, it should be formatted, and the
|
|
* resulting entire document placed just like an image.
|
|
*/
|
|
case P_SUBDOC:
|
|
break;
|
|
|
|
/*
|
|
* Begin a grid. MUST be the very first thing inside
|
|
* this HTML document. After a grid is started, all non-grid
|
|
* tags will be ignored!
|
|
* Also, right now the text and postscript FEs can't
|
|
* handle grids, so we block them.
|
|
*/
|
|
case P_GRID:
|
|
if ((context->type != MWContextText)&&
|
|
(context->type != MWContextPostScript)&&
|
|
(EDT_IS_EDITOR(context) == FALSE)&&
|
|
(state->top_state->nothing_displayed != FALSE)&&
|
|
!lo_InsideLayer(state) &&
|
|
(state->top_state->in_body == FALSE)&&
|
|
(state->end_last_line == NULL)&&
|
|
/* (state->line_list == NULL)&& */ /* Can be non-null because we are putting TEXTBLOCK
|
|
elements on there each time we get text tags*/
|
|
(state->float_list == NULL)&&
|
|
(state->top_state->the_grid == NULL))
|
|
{
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
if (state->current_grid == NULL)
|
|
{
|
|
state->top_state->is_grid = TRUE;
|
|
/*
|
|
* We may be restoring a grid from
|
|
* history. If so we recreate it
|
|
* from saved data, and ignore all
|
|
* the rest of the tags in this
|
|
* document. This happens because
|
|
* we will set the_grid and not
|
|
* set current_grid, so all
|
|
* grid tags are ignored, and since
|
|
* we have set is_grid, all non-grid
|
|
* tags will be ignored.
|
|
*
|
|
* Only recreate if main window
|
|
* size has not changed.
|
|
*/
|
|
if (state->top_state->savedData.Grid->the_grid != NULL)
|
|
{
|
|
lo_SavedGridData *savedGridData;
|
|
int32 width, height;
|
|
|
|
width = state->win_width;
|
|
height = state->win_height;
|
|
FE_GetFullWindowSize(context,
|
|
&width, &height);
|
|
|
|
savedGridData =
|
|
state->top_state->savedData.Grid;
|
|
if ((savedGridData->main_width ==
|
|
width)&&
|
|
(savedGridData->main_height ==
|
|
height))
|
|
{
|
|
lo_RecreateGrid(context, state,
|
|
savedGridData->the_grid);
|
|
savedGridData->the_grid =NULL;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If window size has changed
|
|
* on the grid we are restoring
|
|
* we process normally, but
|
|
* hang on to the old data so
|
|
* we can restore the grid cell
|
|
* contents once new sizes are
|
|
* calculated.
|
|
*/
|
|
state->top_state->old_grid =
|
|
savedGridData->the_grid;
|
|
savedGridData->the_grid =NULL;
|
|
|
|
lo_BeginGrid(context, state,
|
|
tag);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lo_BeginGrid(context, state, tag);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lo_BeginSubgrid(context, state, tag);
|
|
}
|
|
}
|
|
else if ((tag->is_end != FALSE)&&
|
|
(state->current_grid != NULL))
|
|
{
|
|
lo_GridRec *grid;
|
|
|
|
grid = state->current_grid;
|
|
if (grid->subgrid == NULL)
|
|
{
|
|
state->current_grid = NULL;
|
|
lo_EndGrid(context, state, grid);
|
|
}
|
|
else
|
|
{
|
|
lo_EndSubgrid(context, state, grid);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* A grid cell can only happen inside a grid
|
|
*/
|
|
case P_GRID_CELL:
|
|
if ((state->top_state->nothing_displayed != FALSE)&&
|
|
!lo_InsideLayer(state) &&
|
|
(state->top_state->in_body == FALSE)&&
|
|
(state->end_last_line == NULL)&&
|
|
/* state->line_list == NULL)&& */ /* Can be non-null because we are putting TEXTBLOCK
|
|
elements on there each time we get text tags*/
|
|
(state->float_list == NULL)&&
|
|
(state->current_grid != NULL))
|
|
{
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
lo_BeginGridCell(context, state, tag);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Begin a map. Maps define area in client-side
|
|
* image maps.
|
|
*/
|
|
case P_MAP:
|
|
if ((tag->is_end == FALSE)&&
|
|
(state->top_state->current_map == NULL))
|
|
{
|
|
lo_BeginMap(context, state, tag);
|
|
}
|
|
else if ((tag->is_end != FALSE)&&
|
|
(state->top_state->current_map != NULL))
|
|
{
|
|
lo_MapRec *map;
|
|
|
|
map = state->top_state->current_map;
|
|
state->top_state->current_map = NULL;
|
|
lo_EndMap(context, state, map);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* An area definition, can only happen inside a MAP
|
|
* definition for a client-side image map.
|
|
*/
|
|
case P_AREA:
|
|
if ((state->top_state->current_map != NULL)&&
|
|
(tag->is_end == FALSE))
|
|
{
|
|
lo_BeginMapArea(context, state, tag);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Shortcut and old historic tag to auto-place
|
|
* a simple single item query form.
|
|
*/
|
|
case P_INDEX:
|
|
/*
|
|
* No forms in the scrolling document
|
|
* hack
|
|
*/
|
|
if (state->top_state->scrolling_doc != FALSE)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ((tag->is_end == FALSE)&&
|
|
(state->top_state->in_form == FALSE))
|
|
{
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
if(!state->hide_content)
|
|
lo_ProcessIsIndexTag(context, state, tag);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Begin a form.
|
|
* Form cannot be nested!
|
|
*/
|
|
case P_FORM:
|
|
#if defined(SingleSignon)
|
|
/* Notify the signon module of the new form */
|
|
SI_StartOfForm();
|
|
#endif
|
|
/*
|
|
* No forms in the scrolling document
|
|
* hack
|
|
*/
|
|
if (state->top_state->scrolling_doc != FALSE)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
/*
|
|
* Sorry, no nested forms
|
|
*/
|
|
if (state->top_state->in_form == FALSE)
|
|
{
|
|
lo_SetLineBreakState(context, state, FALSE,
|
|
LO_LINEFEED_BREAK_HARD, 2, FALSE);
|
|
|
|
lo_BeginForm(context, state, tag);
|
|
}
|
|
}
|
|
else if (state->top_state->in_form != FALSE)
|
|
{
|
|
lo_EndForm(context, state);
|
|
lo_SetLineBreakState(context, state, FALSE,
|
|
LO_LINEFEED_BREAK_HARD, 2, FALSE);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Deceptive simple form element tag. Can only be inside a
|
|
* form. Really multiplies into one of many possible
|
|
* input elements.
|
|
*/
|
|
case P_INPUT:
|
|
/*
|
|
* Input tags only inside an open form.
|
|
*/
|
|
if ((state->top_state->in_form != FALSE)&&
|
|
(tag->is_end == FALSE))
|
|
{
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
if(!state->hide_content)
|
|
lo_ProcessInputTag(context, state, tag);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* A multi-line text input form element. It encloses text
|
|
* that will be its default text.
|
|
*/
|
|
case P_TEXTAREA:
|
|
/*
|
|
* Textarea tags only inside an open form.
|
|
*/
|
|
if (state->top_state->in_form != FALSE
|
|
&& !state->hide_content)
|
|
{
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
lo_FlushTextBlock(context, state);
|
|
|
|
state->line_buf_len = 0;
|
|
state->text_divert = tag->type;
|
|
lo_BeginTextareaTag(context, state, tag);
|
|
}
|
|
else if (state->text_divert == tag->type)
|
|
{
|
|
PA_Block buff;
|
|
int32 len;
|
|
int32 new_len;
|
|
|
|
if (state->line_buf_len != 0)
|
|
{
|
|
PA_LOCK(tptr2, char *,
|
|
state->line_buf);
|
|
/*
|
|
* Don't do this, they were already
|
|
* expanded by the parser.
|
|
(void)pa_ExpandEscapes(tptr2,
|
|
state->line_buf_len, &len, TRUE);
|
|
*/
|
|
len = state->line_buf_len;
|
|
buff = lo_ConvertToFELinebreaks(tptr2, len,
|
|
&new_len);
|
|
PA_UNLOCK(state->line_buf);
|
|
}
|
|
else
|
|
{
|
|
buff = NULL;
|
|
new_len = 0;
|
|
}
|
|
lo_EndTextareaTag(context, state, buff);
|
|
state->line_buf_len = 0;
|
|
state->text_divert = P_UNKNOWN;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Option tag can only occur inside a select inside a form
|
|
*/
|
|
case P_OPTION:
|
|
/*
|
|
* Select tags only inside an open select
|
|
* in an open form.
|
|
*/
|
|
if ((state->top_state->in_form != FALSE)&&
|
|
(tag->is_end == FALSE)&&
|
|
(state->current_ele != NULL)&&
|
|
(!state->hide_content)&&
|
|
(state->current_ele->type == LO_FORM_ELE)&&
|
|
((state->current_ele->lo_form.element_data->type
|
|
== FORM_TYPE_SELECT_ONE)||
|
|
(state->current_ele->lo_form.element_data->type
|
|
== FORM_TYPE_SELECT_MULT)))
|
|
{
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
if (state->text_divert == P_OPTION)
|
|
{
|
|
PA_Block buff;
|
|
int32 len;
|
|
|
|
if (state->line_buf_len != 0)
|
|
{
|
|
PA_LOCK(tptr2, char *,
|
|
state->line_buf);
|
|
/*
|
|
* Don't do this, they were already
|
|
* expanded by the parser.
|
|
(void)pa_ExpandEscapes(tptr2,
|
|
state->line_buf_len, &len, TRUE);
|
|
*/
|
|
len = state->line_buf_len;
|
|
len = lo_CleanTextWhitespace(tptr2, len);
|
|
buff = PA_ALLOC(len + 1);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(tptr, char *, buff);
|
|
XP_BCOPY(tptr2, tptr, len);
|
|
tptr[len] = '\0';
|
|
PA_UNLOCK(buff);
|
|
}
|
|
PA_UNLOCK(state->line_buf);
|
|
}
|
|
else
|
|
{
|
|
buff = NULL;
|
|
len = 0;
|
|
}
|
|
lo_EndOptionTag(context, state, buff);
|
|
}
|
|
state->line_buf_len = 0;
|
|
lo_BeginOptionTag(context, state, tag);
|
|
state->text_divert = tag->type;
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Select tag, either an option menu or a scrolled
|
|
* list inside a form. Lets you chose N of Many.
|
|
*/
|
|
case P_SELECT:
|
|
/*
|
|
* Select tags only inside an open form.
|
|
*/
|
|
if ((state->top_state->in_form != FALSE)&&
|
|
(!state->hide_content))
|
|
{
|
|
/*
|
|
* Because SELECT doesn't automatically flush,
|
|
* we must do it here if this is the start of a
|
|
* select.
|
|
*/
|
|
if ((tag->is_end == FALSE)&&
|
|
(state->current_ele == NULL))
|
|
{
|
|
lo_FlushTextBlock(context, state);
|
|
}
|
|
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
if ((tag->is_end == FALSE)&&
|
|
(state->current_ele == NULL))
|
|
{
|
|
/*
|
|
* Don't allow text between beginning of select
|
|
* and beginning of first option.
|
|
*/
|
|
state->text_divert = tag->type;
|
|
|
|
lo_BeginSelectTag(context, state, tag);
|
|
}
|
|
else if ((state->current_ele != NULL)&&
|
|
(state->current_ele->type == LO_FORM_ELE)&&
|
|
((state->current_ele->lo_form.element_data->type
|
|
== FORM_TYPE_SELECT_ONE)||
|
|
(state->current_ele->lo_form.element_data->type
|
|
== FORM_TYPE_SELECT_MULT)))
|
|
{
|
|
if (state->text_divert == P_OPTION)
|
|
{
|
|
PA_Block buff;
|
|
int32 len;
|
|
|
|
if (state->line_buf_len != 0)
|
|
{
|
|
PA_LOCK(tptr2, char *,
|
|
state->line_buf);
|
|
/*
|
|
* Don't do this, they were already
|
|
* expanded by the parser.
|
|
(void)pa_ExpandEscapes(tptr2,
|
|
state->line_buf_len, &len, TRUE);
|
|
*/
|
|
len = state->line_buf_len;
|
|
len = lo_CleanTextWhitespace(tptr2,len);
|
|
buff = PA_ALLOC(len + 1);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(tptr, char *, buff);
|
|
XP_BCOPY(tptr2, tptr, len);
|
|
tptr[len] = '\0';
|
|
PA_UNLOCK(buff);
|
|
}
|
|
PA_UNLOCK(state->line_buf);
|
|
}
|
|
else
|
|
{
|
|
buff = NULL;
|
|
len = 0;
|
|
}
|
|
lo_EndOptionTag(context, state, buff);
|
|
}
|
|
state->line_buf_len = 0;
|
|
state->text_divert = P_UNKNOWN;
|
|
|
|
lo_EndSelectTag(context, state);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Place an embedded object.
|
|
* Just reserve an area of layout for that object
|
|
* to use as it wishes.
|
|
*/
|
|
case P_EMBED:
|
|
if (tag->is_end == FALSE && !state->hide_content)
|
|
{
|
|
#ifdef ANTHRAX
|
|
PA_Block buff;
|
|
NET_cinfo* fileInfo;
|
|
char* str;
|
|
char* appletName;
|
|
#endif
|
|
/*
|
|
* If we have started loading an EMBED we are
|
|
* out if the HEAD section of the HTML
|
|
* and into the BODY
|
|
*/
|
|
state->top_state->in_head = FALSE;
|
|
state->top_state->in_body = TRUE;
|
|
|
|
#ifdef ANTHRAX
|
|
/* determine the mimetype */
|
|
buff = lo_FetchParamValue(context, tag, PARAM_TYPE);
|
|
if(buff == NULL)
|
|
{
|
|
/* get SRC */
|
|
buff = lo_FetchParamValue(context, tag, PARAM_SRC);
|
|
PA_LOCK(str, char *, buff);
|
|
|
|
/* if we didn't find a type param, look for an association in the SRC */
|
|
fileInfo = NET_cinfo_find_type(str);
|
|
str = fileInfo->type;
|
|
}
|
|
else
|
|
PA_LOCK(str, char *, buff);
|
|
|
|
/* check to see if this mimetype has an applet handler */
|
|
if((appletName = NPL_FindAppletEnabledForMimetype(str)) != NULL)
|
|
{
|
|
PA_UNLOCK(buff);
|
|
if(buff)
|
|
XP_FREE(buff);
|
|
|
|
/*
|
|
//lo_AddParam(tag, "CODE", appletName);
|
|
//lo_RemoveParam(tag, "type");
|
|
//lo_AddParam(tag, "TYPE", "application/x-java-vm");
|
|
//lo_FormatEmbed(context, state, tag);
|
|
*/
|
|
|
|
lo_ProcessObjectTag(context, state, tag, FALSE);
|
|
tag->is_end = TRUE;
|
|
lo_ProcessObjectTag(context, state, tag, FALSE);
|
|
}
|
|
else
|
|
lo_FormatEmbed(context, state, tag);
|
|
#else
|
|
lo_FormatEmbed(context, state, tag);
|
|
#endif /* ANTHRAX */
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Generate a public/private key pair. The user sees
|
|
* a selection of key sizes to choose from; an ascii
|
|
* representation of the public key becomes the form
|
|
* field value.
|
|
*/
|
|
case P_KEYGEN:
|
|
/*
|
|
* Keygen tags only inside an open form.
|
|
*/
|
|
if ((state->top_state->in_form != FALSE)&&
|
|
(tag->is_end == FALSE)&&
|
|
(!state->hide_content))
|
|
{
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
lo_ProcessKeygenTag(context, state, tag);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Process Mocha script source in a <SCRIPT LANGUAGE="Mocha">
|
|
* ...</SCRIPT> container. Drop all unknown language SCRIPT
|
|
* contents on the floor. Process SRC=<url> attributes for
|
|
* known languages.
|
|
*/
|
|
case P_SCRIPT:
|
|
#ifdef EDITOR
|
|
if( EDT_IS_EDITOR( context ) )
|
|
{
|
|
goto process_server;
|
|
}
|
|
#endif
|
|
|
|
if(!state->hide_content)
|
|
lo_ProcessScriptTag(context, state, tag, NULL);
|
|
break;
|
|
|
|
case P_STYLE:
|
|
if(!state->hide_content)
|
|
lo_ProcessStyleTag(context, state, tag);
|
|
break;
|
|
|
|
case P_LINK:
|
|
/* link tag */
|
|
{
|
|
PA_Block buff = lo_FetchParamValue(context, tag, PARAM_REL);
|
|
if (buff != NULL)
|
|
{
|
|
if (strcasestr((char *)buff, "stylesheet"))
|
|
{
|
|
char *media = (char*)lo_FetchParamValue(context, tag, PARAM_MEDIA);
|
|
|
|
/* check for media=screen
|
|
* don't load the style sheet if there
|
|
* is a media not equal to screen
|
|
*/
|
|
if(!media || !strcasecomp(media, "screen"))
|
|
{
|
|
if(LO_StyleSheetsEnabled(context))
|
|
{
|
|
/* set stylesheet tag stack on */
|
|
if(state->top_state && state->top_state->style_stack)
|
|
{
|
|
STYLESTACK_SetSaveOn(state->top_state->style_stack, TRUE);
|
|
}
|
|
|
|
lo_ProcessScriptTag(context, state, tag, NULL);
|
|
/* now end the script since
|
|
* link is an empty tag
|
|
*/
|
|
tag->is_end = TRUE;
|
|
lo_ProcessScriptTag(context, state, tag, NULL);
|
|
tag->is_end = FALSE;
|
|
}
|
|
}
|
|
|
|
XP_FREEIF(media);
|
|
}
|
|
else if (!strcasecomp((char *)buff, "fontdef") &&
|
|
!state->top_state->resize_reload)
|
|
{
|
|
/* Webfonts */
|
|
PA_Block buff2 = lo_FetchParamValue(context, tag, PARAM_SRC);
|
|
char *font_url = NULL;
|
|
char *str = NULL;
|
|
|
|
if (buff2 != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff2);
|
|
if (str != NULL)
|
|
{
|
|
(void) lo_StripTextWhitespace(str,
|
|
XP_STRLEN(str));
|
|
}
|
|
font_url = NET_MakeAbsoluteURL(
|
|
state->top_state->base_url, str);
|
|
PA_UNLOCK(buff2);
|
|
PA_FREE(buff2);
|
|
}
|
|
|
|
if (font_url)
|
|
{
|
|
#ifdef WEBFONTS
|
|
if (WF_fbu)
|
|
{
|
|
/* Download the webfont */
|
|
nffbu_LoadWebfont(WF_fbu, context, font_url,
|
|
FORCE_RELOAD_FLAG(state->top_state), NULL);
|
|
}
|
|
#endif /* WEBFONTS */
|
|
XP_FREE(font_url);
|
|
}
|
|
}
|
|
else if (!strcasecomp((char *)buff, "sitemap"))
|
|
{
|
|
/* RDF sitemaps */
|
|
PA_Block buff2 = lo_FetchParamValue(context, tag, PARAM_SRC);
|
|
PA_Block buff3 = lo_FetchParamValue(context, tag, PARAM_NAME);
|
|
char *rdfURL = NULL;
|
|
char *str = NULL;
|
|
|
|
if (buff2 == NULL) buff2 = lo_FetchParamValue(context, tag, PARAM_HREF);
|
|
|
|
if (buff2 != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff2);
|
|
if (str != NULL)
|
|
{
|
|
(void) lo_StripTextWhitespace(str,
|
|
XP_STRLEN(str));
|
|
}
|
|
rdfURL = NET_MakeAbsoluteURL(
|
|
state->top_state->base_url, str);
|
|
if (rdfURL)
|
|
{
|
|
XP_AddNavCenterSitemap(context, rdfURL, (char *)buff3);
|
|
}
|
|
|
|
PA_UNLOCK(buff2);
|
|
PA_FREE(buff2);
|
|
PA_UNLOCK(buff3);
|
|
PA_FREE(buff3);
|
|
|
|
}
|
|
|
|
}
|
|
else if (!strcasecomp((char *)buff, "prefetch"))
|
|
{
|
|
char *prefetchURL=NULL;
|
|
char *str;
|
|
PA_Block buff2 = lo_FetchParamValue(context, tag, PARAM_SRC);
|
|
|
|
if (buff2)
|
|
{
|
|
PA_LOCK(str, char *, buff2);
|
|
if (str)
|
|
{
|
|
(void) lo_StripTextWhitespace(str, XP_STRLEN(str));
|
|
}
|
|
prefetchURL = NET_MakeAbsoluteURL(
|
|
state->top_state->base_url, str);
|
|
PA_UNLOCK(buff2);
|
|
PA_FREE(buff2);
|
|
}
|
|
if (prefetchURL)
|
|
{
|
|
PRE_AddToList(context, prefetchURL);
|
|
}
|
|
}
|
|
|
|
else if (!strcasecomp((char *)buff, "privacypolicy"))
|
|
{
|
|
char *policyURL=NULL;
|
|
char *str;
|
|
PA_Block buff2 = lo_FetchParamValue(context, tag, PARAM_SRC);
|
|
History_entry *hist;
|
|
|
|
|
|
if (buff2)
|
|
{
|
|
PA_LOCK(str, char *, buff2);
|
|
if (str)
|
|
{
|
|
(void) lo_StripTextWhitespace(str, XP_STRLEN(str));
|
|
}
|
|
policyURL = NET_MakeAbsoluteURL(
|
|
state->top_state->base_url, str);
|
|
PA_UNLOCK(buff2);
|
|
PA_FREE(buff2);
|
|
}
|
|
if (policyURL)
|
|
{
|
|
hist = SHIST_GetCurrent(&context->hist);
|
|
if (hist)
|
|
hist->privacy_policy_url = policyURL;
|
|
}
|
|
}
|
|
|
|
PA_FREE(buff);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* We were blocking on a <script> tag, while being processed
|
|
* it did a document.write("something that blocked us").
|
|
* Now the elements in the above call are no longer blocking
|
|
* us but we need to continue to block on the script tag
|
|
* since it might have more output
|
|
*/
|
|
case P_NSCP_REBLOCK:
|
|
if (state->in_relayout == FALSE)
|
|
{
|
|
state->top_state->layout_blocking_element = tag->lo_data;
|
|
XP_ASSERT(tag->newline_count == state->top_state->input_write_level);
|
|
state->top_state->input_write_level--;
|
|
ET_DocWriteAck(context, 0);
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* This is the NEW HTML-WG approved embedded JAVA
|
|
* application.
|
|
*/
|
|
#if defined(OJI)
|
|
case P_JAVA_APPLET:
|
|
javaPlugin = NPL_FindPluginEnabledForType(JAVA_PLUGIN_MIMETYPE);
|
|
|
|
/* If there is a JVM installed as a plugin, redirect to the object tag */
|
|
if(javaPlugin && !state->hide_content)
|
|
{
|
|
if(tag->is_end == FALSE)
|
|
{
|
|
PA_Block buff;
|
|
char* str;
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_CODE);
|
|
if(buff)
|
|
{
|
|
PA_LOCK(str, char *, buff);
|
|
lo_AddParam(tag, "TYPE", JAVA_PLUGIN_MIMETYPE);
|
|
|
|
PA_UNLOCK(buff);
|
|
XP_FREE(buff);
|
|
|
|
tag->type = P_OBJECT;
|
|
lo_ProcessObjectTag(context, state, tag, FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tag->type = P_OBJECT;
|
|
lo_ProcessObjectTag(context, state, tag, FALSE);
|
|
}
|
|
|
|
XP_FREE(javaPlugin);
|
|
|
|
}
|
|
break;
|
|
|
|
#elif defined(JAVA)
|
|
case P_JAVA_APPLET:
|
|
/*
|
|
* If the user has disabled Java, act like it we don't
|
|
* understand the APPLET tag.
|
|
*/
|
|
if (!state->hide_content && LJ_GetJavaEnabled() != FALSE)
|
|
{
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
/*
|
|
* If we open a new java applet without closing
|
|
* an old one, force a close of the old one
|
|
* first.
|
|
* Now open the new java applet.
|
|
*/
|
|
if (state->current_java != NULL)
|
|
{
|
|
lo_CloseJavaApp(context, state,
|
|
state->current_java);
|
|
state->current_java = NULL;
|
|
|
|
lo_FormatJavaApp(context, state, tag);
|
|
}
|
|
/*
|
|
* Else this is a new java applet, open it.
|
|
*/
|
|
else
|
|
{
|
|
/*
|
|
* If we have started loading an APPLET we are
|
|
* out if the HEAD section of the HTML
|
|
* and into the BODY
|
|
*/
|
|
state->top_state->in_head = FALSE;
|
|
state->top_state->in_body = TRUE;
|
|
|
|
lo_FormatJavaApp(context, state, tag);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Only close a java app if it exists.
|
|
*/
|
|
if (state->current_java != NULL)
|
|
{
|
|
lo_CloseJavaApp(context, state,
|
|
state->current_java);
|
|
state->current_java = NULL;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
#endif /* JAVA */
|
|
|
|
case P_OBJECT:
|
|
if(!state->hide_content)
|
|
lo_ProcessObjectTag(context, state, tag, FALSE);
|
|
break;
|
|
|
|
case P_PARAM:
|
|
if(!state->hide_content)
|
|
lo_ProcessParamTag(context, state, tag, FALSE);
|
|
break;
|
|
|
|
/*
|
|
* Place an image. Special because if layout of all
|
|
* other tags was blocked, image tags are still
|
|
* partially processed at that time.
|
|
*/
|
|
case P_IMAGE:
|
|
case P_NEW_IMAGE:
|
|
if (tag->is_end == FALSE && !state->hide_content)
|
|
{
|
|
/*
|
|
* If we have started loading an IMAGE we are
|
|
* out if the HEAD section of the HTML
|
|
* and into the BODY
|
|
*/
|
|
state->top_state->in_head = FALSE;
|
|
state->top_state->in_body = TRUE;
|
|
|
|
if (tag->lo_data != NULL)
|
|
{
|
|
lo_PartialFormatImage(context, state,tag);
|
|
}
|
|
else
|
|
{
|
|
lo_FormatImage(context, state, tag);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Paragraph break, a single line of whitespace.
|
|
*/
|
|
case P_PARAGRAPH:
|
|
lo_process_paragraph_tag(context, state, tag, style_struct);
|
|
break;
|
|
|
|
/*
|
|
* Division. General alignment text block
|
|
*/
|
|
case P_DIVISION:
|
|
/*
|
|
* The start of a division infers the end of
|
|
* the HEAD section of the HTML and starts the BODY
|
|
*/
|
|
state->top_state->in_head = FALSE;
|
|
state->top_state->in_body = TRUE;
|
|
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
lo_SetLineBreakState(context, state, FALSE,
|
|
LO_LINEFEED_BREAK_HARD, 1, FALSE);
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
PA_Block buff;
|
|
char *str;
|
|
int32 alignment;
|
|
Bool is_multicol;
|
|
|
|
/*
|
|
* By default inherit current alignment
|
|
* for DIV tags that set COLS but not
|
|
* ALIGN.
|
|
*/
|
|
if (state->align_stack != NULL)
|
|
{
|
|
alignment = state->align_stack->alignment;
|
|
}
|
|
else
|
|
{
|
|
alignment = LO_ALIGN_LEFT;
|
|
}
|
|
|
|
buff = lo_FetchParamValue(context, tag, PARAM_ALIGN);
|
|
if (buff != NULL)
|
|
{
|
|
PA_LOCK(str, char *, buff);
|
|
alignment =
|
|
(int32)lo_EvalDivisionAlignParam(str);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
is_multicol = FALSE;
|
|
/*
|
|
* If a DIV tag has a COLS attribute of
|
|
* greater than 1, then treat it as a
|
|
* MULTICOL tag.
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_COLS);
|
|
if (buff != NULL)
|
|
{
|
|
int32 cols;
|
|
|
|
PA_LOCK(str, char *, buff);
|
|
cols = XP_ATOI(str);
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
if (cols > 1)
|
|
{
|
|
is_multicol = TRUE;
|
|
}
|
|
}
|
|
|
|
if (is_multicol == FALSE)
|
|
{
|
|
lo_PushAlignment(state, tag->type, alignment);
|
|
}
|
|
else
|
|
{
|
|
lo_PushAlignment(state, P_MULTICOLUMN, alignment);
|
|
/*
|
|
lo_BeginMulticolumn(context, state, tag);
|
|
*/
|
|
lo_process_multicolumn_tag(context, state, tag);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lo_AlignStack *aptr;
|
|
Bool is_multicol;
|
|
|
|
is_multicol = FALSE;
|
|
aptr = lo_PopAlignment(state);
|
|
if (aptr != NULL)
|
|
{
|
|
if (aptr->type == P_MULTICOLUMN)
|
|
{
|
|
is_multicol = TRUE;
|
|
}
|
|
XP_DELETE(aptr);
|
|
}
|
|
|
|
/*
|
|
* If this closing DIV closes a DIV that
|
|
* was acting like a MULTICOL, then
|
|
* we must close the fake MULTICOL tag
|
|
* here.
|
|
*/
|
|
if ((is_multicol != FALSE)&&
|
|
(state->current_multicol != NULL))
|
|
{
|
|
/*
|
|
lo_EndMulticolumn(context, state, tag,
|
|
state->current_multicol);
|
|
*/
|
|
lo_process_multicolumn_tag(context, state, tag);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* added for stylesheet support
|
|
*
|
|
* a general container tag. The default style is
|
|
* inline. It doesn't do anything without associated
|
|
* stylesheet properties.
|
|
* it's just ignored in normal layout
|
|
*/
|
|
case P_SPAN:
|
|
{
|
|
#ifdef DOM
|
|
lo_process_span_tag(context, state, tag);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Just move the left and right margins in for this paragraph
|
|
* of text.
|
|
*/
|
|
case P_BLOCKQUOTE:
|
|
{
|
|
int8 quote_type = QUOTE_NONE;
|
|
PA_Block buff;
|
|
/*
|
|
* Get the param to see if this is a mailing quote. <BLOCKQUOTE TYPE=CITE>
|
|
*/
|
|
buff = lo_FetchParamValue(context, tag, PARAM_TYPE);
|
|
if (buff != NULL) {
|
|
char *str;
|
|
PA_LOCK(str, char *, buff);
|
|
/*
|
|
* cite used to map to QUOTE_MQUOTE.
|
|
* We now use QUOTE_CITE.
|
|
*/
|
|
if (pa_TagEqual("cite",str)) {
|
|
quote_type = QUOTE_CITE;
|
|
}
|
|
else if (pa_TagEqual("jwz",str)) {
|
|
quote_type = QUOTE_JWZ;
|
|
}
|
|
PA_UNLOCK(buff);
|
|
PA_FREE(buff);
|
|
}
|
|
|
|
/*
|
|
* The start of a blockquote infers the end of
|
|
* the HEAD section of the HTML and starts the BODY
|
|
*/
|
|
state->top_state->in_head = FALSE;
|
|
state->top_state->in_body = TRUE;
|
|
|
|
if (state->in_paragraph != FALSE)
|
|
{
|
|
lo_CloseParagraph(context, &state, tag, 2);
|
|
}
|
|
|
|
{
|
|
LO_BlockQuoteStruct *quote = (LO_BlockQuoteStruct*)lo_NewElement(context, state, LO_BLOCKQUOTE, NULL, 0);
|
|
|
|
XP_ASSERT(quote);
|
|
if (!quote)
|
|
{
|
|
LO_UnlockLayout();
|
|
return;
|
|
}
|
|
|
|
quote->lo_any.type = LO_BLOCKQUOTE;
|
|
quote->lo_any.ele_id = NEXT_ELEMENT;
|
|
|
|
quote->lo_any.x = state->x;
|
|
quote->lo_any.y = state->y;
|
|
quote->lo_any.x_offset = 0;
|
|
quote->lo_any.y_offset = 0;
|
|
quote->lo_any.width = 0;
|
|
quote->lo_any.height = 0;
|
|
quote->lo_any.line_height = 0;
|
|
|
|
quote->is_end = tag->is_end;
|
|
quote->quote_type = quote_type;
|
|
quote->tag = PA_CloneMDLTag(tag);
|
|
|
|
lo_AppendToLineList(context, state, (LO_Element*)quote, 0);
|
|
|
|
lo_ProcessBlockQuoteElement(context, state, quote, FALSE);
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* An internal tag to allow libmocha/lm_doc.c's
|
|
* doc_open_stream to get the new document's top
|
|
* state struct early.
|
|
*/
|
|
case P_NSCP_OPEN:
|
|
break;
|
|
|
|
/*
|
|
* An internal tag to allow us to merge two
|
|
* possibly incomplete HTML docs as neatly
|
|
* as possible.
|
|
*/
|
|
case P_NSCP_CLOSE:
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
LO_CloseAllTags(context);
|
|
/* the previous function call
|
|
* closes tags and free's the top
|
|
* style struct
|
|
*/
|
|
style_struct = NULL;
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* Normally unknown tags are just ignored.
|
|
* But if the unknown tag is in the head of a document, we
|
|
* assume it is a tag that can contain content, and ignore
|
|
* that content.
|
|
* WARNING: In special cases with omitted </head> tags,
|
|
* this can result in the loss of body data!
|
|
*/
|
|
case P_UNKNOWN:
|
|
#ifndef MOCHA
|
|
unknown:
|
|
#endif
|
|
if (state->top_state->in_head != FALSE)
|
|
{
|
|
char *tdata;
|
|
char *tptr;
|
|
char tchar;
|
|
|
|
PA_LOCK(tdata, char *, tag->data);
|
|
|
|
/*
|
|
* Throw out non-tags like <!doctype>
|
|
*/
|
|
if ((tag->data_len < 1)||(tdata[0] == '!'))
|
|
{
|
|
PA_UNLOCK(tag->data);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Remove the unknown tag name from any attributes
|
|
* it might have.
|
|
*/
|
|
tptr = tdata;
|
|
while ((!XP_IS_SPACE(*tptr))&&(*tptr != '>'))
|
|
{
|
|
tptr++;
|
|
}
|
|
tchar = *tptr;
|
|
*tptr = '\0';
|
|
|
|
/*
|
|
* Save the unknown tag name to match to its
|
|
* end tag.
|
|
*/
|
|
if (tag->is_end == FALSE)
|
|
{
|
|
if (state->top_state->unknown_head_tag == NULL)
|
|
{
|
|
state->top_state->unknown_head_tag =
|
|
XP_STRDUP(tdata);
|
|
}
|
|
}
|
|
/*
|
|
* Else if we already have a saved start and this
|
|
* is the matching end. Clear the knowledge
|
|
* that we are in an unknown head tag.
|
|
*/
|
|
else
|
|
{
|
|
if ((state->top_state->unknown_head_tag != NULL)
|
|
&&(XP_STRCASECMP(state->top_state->unknown_head_tag, tdata) == 0))
|
|
{
|
|
XP_FREE(state->top_state->unknown_head_tag);
|
|
state->top_state->unknown_head_tag = NULL;
|
|
}
|
|
}
|
|
|
|
*tptr = tchar;
|
|
PA_UNLOCK(tag->data);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(!tag->is_end)
|
|
{
|
|
if(!lo_IsEmptyTag(tag->type))
|
|
{
|
|
lo_SetStyleSheetProperties(context, style_struct, tag);
|
|
}
|
|
else
|
|
{
|
|
/* get the new current state */
|
|
int32 doc_id = XP_DOCID(context);
|
|
lo_TopState *top_state = lo_FetchTopState(doc_id);
|
|
state = lo_TopSubState(top_state);
|
|
|
|
|
|
/* the tag is not a container so pop it off of
|
|
* the style stack
|
|
*/
|
|
LO_PopStyleTag(context, &state, tag);
|
|
style_struct = NULL;
|
|
}
|
|
}
|
|
|
|
/* the last argument is TRUE if we started in the head
|
|
*/
|
|
lo_PostLayoutTag( context, state, tag, started_in_head);
|
|
|
|
|
|
LO_UnlockLayout();
|
|
}
|
|
|
|
void lo_PreLayoutTag(MWContext * context, lo_DocState *state, PA_Tag *tag)
|
|
{
|
|
if (state->in_relayout == FALSE &&
|
|
lo_IsTagInSourcedLayer(state, tag) == FALSE)
|
|
{
|
|
NET_StreamClass *stream = state->top_state->mocha_write_stream;
|
|
|
|
/* @@@ small bug here
|
|
* if someone uses <script type=text/css>
|
|
* it will break.
|
|
*/
|
|
if ((stream != NULL) &&
|
|
(state->top_state->in_script == SCRIPT_TYPE_NOT
|
|
|| state->top_state->in_script == SCRIPT_TYPE_CSS
|
|
|| state->top_state->in_script == SCRIPT_TYPE_JSSS))
|
|
{
|
|
switch (tag->type)
|
|
{
|
|
case P_TEXT:
|
|
if (tag->data != NULL)
|
|
{
|
|
stream->put_block(stream,
|
|
(char *)tag->data,
|
|
tag->data_len);
|
|
}
|
|
break;
|
|
|
|
case P_UNKNOWN:
|
|
if (tag->data != NULL)
|
|
{
|
|
char *tag_str = PR_smprintf("<%s%s",
|
|
tag->is_end ? "/" : "",
|
|
tag->data);
|
|
|
|
if (tag_str == NULL)
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
}
|
|
else
|
|
{
|
|
stream->put_block(stream, tag_str,
|
|
XP_STRLEN(tag_str));
|
|
XP_FREE(tag_str);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case P_NSCP_OPEN:
|
|
case P_NSCP_CLOSE:
|
|
case P_NSCP_REBLOCK:
|
|
case P_SCRIPT:
|
|
case P_NOSCRIPT:
|
|
break;
|
|
|
|
default:
|
|
{
|
|
const char *tag_name;
|
|
char *tag_str;
|
|
|
|
tag_name = pa_PrintTagToken(tag->type);
|
|
if ((tag->data != NULL) &&
|
|
(*(char *)tag->data != '>'))
|
|
{
|
|
tag_str = PR_smprintf("<%s%s%s",
|
|
tag->is_end ? "/" : "",
|
|
tag_name, tag->data);
|
|
}
|
|
else
|
|
{
|
|
tag_str = PR_smprintf("<%s%s>",
|
|
tag->is_end ? "/" : "",
|
|
tag_name);
|
|
}
|
|
if (tag_str == NULL)
|
|
{
|
|
state->top_state->out_of_memory = TRUE;
|
|
}
|
|
else
|
|
{
|
|
stream->put_block(stream, tag_str,
|
|
XP_STRLEN(tag_str));
|
|
XP_FREE(tag_str);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we get a non-text tag, and we have some previous text
|
|
* elements we have been merging into state, flush those
|
|
* before processing the new tag.
|
|
* Exceptions: P_TITLE does special stuff with title text.
|
|
* Exceptions: P_TEXTAREA does special stuff with text.
|
|
* Exceptions: P_OPTION does special stuff with text.
|
|
* Exceptions: P_SELECT does special stuff with option text on close.
|
|
* Exceptions: P_SCRIPT (and P_STYLE) does special stuff with Mocha script text.
|
|
* Exceptions: P_CERTIFICATE does special stuff with text.
|
|
* Exceptions: P_BASEFONT may not change text flow.
|
|
* Exceptions: P_BASE only affects URLs not text flow.
|
|
*/
|
|
if ((tag->type != P_TEXT)&&(tag->type != P_TITLE)&&
|
|
(tag->type != P_BASEFONT)&&
|
|
(tag->type != P_TEXTAREA)&&
|
|
(tag->type != P_OPTION)&&
|
|
(tag->type != P_SELECT)&&
|
|
(tag->type != P_SCRIPT)&&
|
|
(tag->type != P_STYLE)&&
|
|
(tag->type != P_CERTIFICATE))
|
|
{
|
|
lo_FlushTextBlock(context, state);
|
|
if (state->top_state->out_of_memory)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
#ifdef EDITOR
|
|
/*
|
|
* The editor needs all text elements to be individual text elements.
|
|
* Force the line buffer to be flushed so each individual text item
|
|
* remains such.
|
|
*/
|
|
if (EDT_IS_EDITOR(context))
|
|
{
|
|
lo_FlushTextBlock(context, state);
|
|
if (state->top_state->out_of_memory)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* LTNOTE:
|
|
* This is a kludge. We should allow all Layout elements to have
|
|
* pointers to edit elements. As should also be setting the
|
|
* edit_element at the point we actually parse the tag.
|
|
*/
|
|
|
|
if( !state->edit_force_offset )
|
|
{
|
|
state->edit_current_offset = 0;
|
|
}
|
|
|
|
if( tag->type == P_TEXT
|
|
|| tag->type == P_IMAGE
|
|
|| tag->type == P_HRULE
|
|
|| tag->type == P_LINEBREAK
|
|
)
|
|
{
|
|
state->edit_current_element = tag->edit_element;
|
|
if( tag->type != P_TEXT )
|
|
{
|
|
state->edit_current_offset = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
state->edit_current_element = 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void lo_PostLayoutTag(MWContext * context, lo_DocState *state, PA_Tag *tag, XP_Bool started_in_head)
|
|
{
|
|
|
|
#ifdef EDITOR
|
|
/*
|
|
* The editor needs all text elements to be individual text elements.
|
|
* Force the line buffer to be flushed so each individual text item
|
|
* remains such.
|
|
*/
|
|
if (EDT_IS_EDITOR(context))
|
|
{
|
|
lo_FlushTextBlock(context, state);
|
|
}
|
|
#endif
|
|
|
|
#ifdef PICS_SUPPORT
|
|
if(started_in_head || tag->type == P_META)
|
|
{
|
|
int32 doc_id = XP_DOCID(context);
|
|
lo_TopState *top_state = lo_FetchTopState(doc_id);
|
|
|
|
/* true if we transition from the head to out of the head in
|
|
* the last pass through laytags
|
|
*
|
|
* if started in head is false then we processed a meta tag
|
|
* after the body.
|
|
*/
|
|
if(top_state->in_head != started_in_head
|
|
|| !started_in_head)
|
|
{
|
|
char *fail_url = NULL;
|
|
PICS_PassFailReturnVal status = lo_ProcessPicsLabel(context,
|
|
state,
|
|
&fail_url);
|
|
|
|
if(status == PICS_NO_RATINGS
|
|
&& PICS_AreRatingsRequired())
|
|
{
|
|
/* check to see if the URL is already in an approved tree
|
|
*
|
|
* if it is then we don't have to fail
|
|
*/
|
|
URL_Struct *URL_s = state->top_state->nurl;
|
|
|
|
/* if the URL is not http or https then don't require a
|
|
* rating. This allows mail and news and local files
|
|
* to work. This is per marketings request, I'm not
|
|
* entirely sure it's the right thing to do
|
|
*/
|
|
if(!strncasecomp(URL_s->address, "http", 4)
|
|
&& !PICS_CheckForValidTreeRating(URL_s->address))
|
|
{
|
|
/* if we just left the head and we didn't find
|
|
* any valid ratings and ratings are required
|
|
* then fail
|
|
*/
|
|
status = PICS_RATINGS_FAILED;
|
|
XP_FREEIF(fail_url);
|
|
fail_url = PICS_RStoURL(NULL, URL_s->address);
|
|
}
|
|
}
|
|
|
|
if(status == PICS_RATINGS_FAILED)
|
|
{
|
|
/* emit a fake script */
|
|
PA_Tag tmp_tag;
|
|
|
|
tmp_tag.type = P_IMAGE;
|
|
tmp_tag.is_end = FALSE;
|
|
tmp_tag.newline_count = tag->newline_count;
|
|
tmp_tag.data = NULL;
|
|
tmp_tag.data_len = 0;
|
|
tmp_tag.true_len = 0;
|
|
tmp_tag.lo_data = NULL;
|
|
tmp_tag.next = NULL;
|
|
|
|
lo_ProcessScriptTag(context, state, &tmp_tag, NULL);
|
|
|
|
top_state->in_script = SCRIPT_TYPE_MOCHA;
|
|
tmp_tag.is_end = TRUE;
|
|
|
|
#define JS_PICS_SCRIPT "location.replace('%s');"
|
|
|
|
state->line_buf = (PA_Block)PR_smprintf(JS_PICS_SCRIPT,
|
|
fail_url ? fail_url : "about:pics");
|
|
XP_FREEIF(fail_url);
|
|
|
|
if(state->line_buf)
|
|
{
|
|
state->line_buf_len = XP_STRLEN((char*)state->line_buf);
|
|
|
|
lo_ProcessScriptTag(context, state, &tmp_tag, NULL);
|
|
}
|
|
|
|
state->hide_content = TRUE;
|
|
}
|
|
}
|
|
}
|
|
#endif /* PICS_SUPPORT */
|
|
}
|
|
|
|
#ifdef ANTHRAX
|
|
static void lo_RemoveParam(PA_Tag* tag, char* name)
|
|
{
|
|
char* start, *end;
|
|
char* tagData = (char*)tag->data;
|
|
start = XP_STRCASESTR(tagData, name);
|
|
if(start != NULL)
|
|
{
|
|
end = start;
|
|
|
|
/* advance to the = */
|
|
while(*end != '=')
|
|
++end;
|
|
|
|
/* skip the = */
|
|
++end;
|
|
|
|
/* advance through whitespace */
|
|
while(*end == ' ')
|
|
++end;
|
|
|
|
/* advance until we hit whitespace again */
|
|
while(*end != ' ')
|
|
++end;
|
|
|
|
/* clear it out */
|
|
while(start != end)
|
|
{
|
|
*start = ' ';
|
|
++start;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void lo_AddParam(PA_Tag* tag, char* aName, char* aValue)
|
|
{
|
|
uint32 nameLen, valueLen, oldTagLen, newTagLen;
|
|
char* tagData;
|
|
|
|
nameLen = XP_STRLEN(aName);
|
|
valueLen = XP_STRLEN(aValue);
|
|
oldTagLen = XP_STRLEN((char*)(tag->data));
|
|
newTagLen = oldTagLen + nameLen + valueLen + 2;
|
|
|
|
tag->data = XP_REALLOC(tag->data, newTagLen+1);
|
|
|
|
/* Remove the '>' character */
|
|
tagData = (char*)(tag->data);
|
|
tagData[oldTagLen-1] = 0;
|
|
|
|
/* Add "aName=aValue" */
|
|
XP_STRCAT(tagData, " ");
|
|
XP_STRCAT(tagData, aName);
|
|
XP_STRCAT(tagData, "=");
|
|
XP_STRCAT(tagData, aValue);
|
|
XP_STRCAT(tagData, ">");
|
|
|
|
tag->data_len = newTagLen;
|
|
}
|
|
|