mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3102 lines
91 KiB
3102 lines
91 KiB
/*
|
|
Enhanced NCSA Mosaic from Spyglass
|
|
"Guitar"
|
|
|
|
Copyright 1994 Spyglass, Inc.
|
|
All Rights Reserved
|
|
|
|
Author(s):
|
|
Eric W. Sink [email protected]
|
|
Jim Seidman [email protected]
|
|
Jeff Hostetler [email protected]
|
|
Scott Piette [email protected]
|
|
*/
|
|
|
|
|
|
/* Note that several of the functions prototyped in HText.h are now
|
|
in platform-specific forms code. */
|
|
|
|
/*****************************************************************************
|
|
INCLUDES
|
|
*****************************************************************************/
|
|
#include "all.h"
|
|
#include "chars.h"
|
|
|
|
|
|
#ifdef FEATURE_TABLES
|
|
|
|
#define FEATURE_TABLES_IMPLICIT_TD
|
|
|
|
static int x_append_cell(struct _cellvector * pv, int value)
|
|
{
|
|
/* claim a new cell on the end of the vector */
|
|
|
|
if (!pv->aVector || pv->size==0)
|
|
{
|
|
int newSpace = 10;
|
|
pv->aVector = (int *)GTR_CALLOC(1,newSpace*sizeof(int *));
|
|
if (!pv->aVector)
|
|
return -1;
|
|
pv->size = newSpace;
|
|
pv->next = 0;
|
|
}
|
|
else if (pv->next >= pv->size)
|
|
{
|
|
int * newArray;
|
|
int newSpace;
|
|
|
|
newSpace = pv->size * 2;
|
|
newArray = (int *)GTR_REALLOC(pv->aVector,newSpace*sizeof(int *));
|
|
if (!newArray)
|
|
{
|
|
newSpace = pv->size + 20;
|
|
newArray = (int *)GTR_REALLOC(pv->aVector,newSpace*sizeof(int *));
|
|
if (!newArray)
|
|
return -1;
|
|
}
|
|
|
|
memset(newArray+pv->next,0,(newSpace-pv->next)*sizeof(int *));
|
|
pv->aVector = newArray;
|
|
pv->size = newSpace;
|
|
}
|
|
|
|
pv->aVector[pv->next] = value;
|
|
return pv->next++;
|
|
}
|
|
static void x_remove_end_cell(struct _cellvector * pv)
|
|
{
|
|
pv->next--;
|
|
return;
|
|
}
|
|
static BOOL x_is_empty_vector(struct _cellvector * pv)
|
|
{
|
|
return (pv->next <= 0);
|
|
}
|
|
static void x_empty_vector(struct _cellvector * pv)
|
|
{
|
|
pv->next = 0;
|
|
return;
|
|
}
|
|
static int x_fetch_end_cell(struct _cellvector * pv)
|
|
{
|
|
return pv->aVector[pv->next-1];
|
|
}
|
|
static BOOL x_get_kth_cell(struct _cellvector * pv, int k, int * pvalue)
|
|
{
|
|
if (k >= pv->next)
|
|
return FALSE;
|
|
*pvalue = pv->aVector[k];
|
|
return TRUE;
|
|
}
|
|
static void x_set_kth_cell(struct _cellvector * pv, int k, int value)
|
|
{
|
|
while (k >= pv->next)
|
|
x_append_cell(pv,0);
|
|
pv->aVector[k] = value;
|
|
return;
|
|
}
|
|
|
|
static int x_extend_tabledata(struct _tabledatavector * pv)
|
|
{
|
|
/* claim a new cell on the end of the vector */
|
|
|
|
if (!pv->aVector || pv->size==0)
|
|
{
|
|
int newSpace = 10;
|
|
pv->aVector = (struct _tabledata *)GTR_CALLOC(1,newSpace*sizeof(struct _tabledata));
|
|
if (!pv->aVector)
|
|
return -1;
|
|
pv->size = newSpace;
|
|
pv->next = 0;
|
|
}
|
|
else if (pv->next >= pv->size)
|
|
{
|
|
struct _tabledata * newArray;
|
|
int newSpace;
|
|
|
|
newSpace = pv->size * 2;
|
|
newArray = (struct _tabledata *)GTR_REALLOC(pv->aVector,newSpace*sizeof(struct _tabledata));
|
|
if (!newArray)
|
|
{
|
|
newSpace = pv->size + 20;
|
|
newArray = (struct _tabledata *)GTR_REALLOC(pv->aVector,newSpace*sizeof(struct _tabledata));
|
|
if (!newArray)
|
|
return -1;
|
|
}
|
|
|
|
memset(newArray+pv->next,0,(newSpace-pv->next)*sizeof(struct _tabledata));
|
|
pv->aVector = newArray;
|
|
pv->size = newSpace;
|
|
}
|
|
|
|
return pv->next++;
|
|
}
|
|
|
|
static int x_check_container(HText *text, unsigned char * peType)
|
|
{
|
|
/* return element index of container or -1 on error */
|
|
|
|
int eBegin;
|
|
|
|
*peType = ELE_VOID; /* incase of error */
|
|
|
|
if (x_is_empty_vector(&text->w3doc->cellStack))
|
|
{
|
|
XX_DMsg(DBG_TABLES,("Table: -- not in table\n"));
|
|
return -1;
|
|
}
|
|
|
|
eBegin = x_fetch_end_cell(&text->w3doc->cellStack);
|
|
if ( (eBegin <= 0)
|
|
|| (eBegin >= text->w3doc->elementCount))
|
|
{
|
|
XX_DMsg(DBG_TABLES,("Table: -- cell stack corrupt.\n"));
|
|
return -1;
|
|
}
|
|
|
|
*peType = text->w3doc->aElements[eBegin].type;
|
|
|
|
return eBegin;
|
|
}
|
|
|
|
#ifdef FEATURE_TABLES_IMPLICIT_TD
|
|
static BOOL x_is_in_table_but_not_in_cell(HText * text)
|
|
{
|
|
register struct _cellvector * pv = &text->w3doc->cellStack;
|
|
register int eBegin;
|
|
register unsigned char type;
|
|
|
|
if (x_is_empty_vector(pv))
|
|
return FALSE;
|
|
eBegin = x_fetch_end_cell(pv);
|
|
type = text->w3doc->aElements[eBegin].type;
|
|
|
|
return (type != ELE_BEGINCELL);
|
|
}
|
|
#endif /* FEATURE_TABLES_IMPLICIT_TD */
|
|
|
|
#endif /* FEATURE_TABLES */
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_new2
|
|
*****************************************************************************/
|
|
HText *HText_new2(struct Mwin *tw, HTRequest *req, HTStream * output_stream, struct CharStream *pcsSource)
|
|
{
|
|
HText* text;
|
|
struct _www* w3doc;
|
|
|
|
XX_DMsg(DBG_HTEXT, ("HText_new2 called\n"));
|
|
|
|
text = (HText*) GTR_CALLOC(sizeof(HText), 1);
|
|
if (!text)
|
|
{ /* memory failure! */
|
|
return NULL;
|
|
}
|
|
|
|
/* initialize structure */
|
|
text->tw = tw;
|
|
text->bOpen = FALSE;
|
|
text->bNewPara = TRUE;
|
|
text->bAnchor = FALSE;
|
|
text->bHighlight = FALSE;
|
|
text->hrefOffset = 0;
|
|
text->hrefLen = 0;
|
|
text->iStyle = 0;
|
|
text->fontBits = 0;
|
|
text->bSelect = FALSE;
|
|
text->bOption = FALSE;
|
|
text->bOptionValuePresent = FALSE;
|
|
text->bListSelect = FALSE;
|
|
text->bMultiListSelect = FALSE;
|
|
text->bNextOptionIsSelected = FALSE;
|
|
text->bDD = FALSE;
|
|
text->bTextArea = FALSE;
|
|
text->bStartingListItem = FALSE;
|
|
text->iCurrentSelect = -1;
|
|
text->iParagraphAlign = 0;
|
|
text->bRecord = req->iFlags & HTREQ_RECORD;
|
|
text->szLocal = req->destination->szActualLocal;
|
|
text->next_list = 0;
|
|
|
|
text->basefont_size = BASE_LOGICAL_FONT_SIZE;
|
|
|
|
W3Doc_DisconnectFromWindow(tw->w3doc, tw);
|
|
w3doc = W3Doc_CreateAndInit(tw, req, pcsSource);
|
|
if (!w3doc)
|
|
{ /* memory failure */
|
|
GTR_FREE(text);
|
|
return NULL;
|
|
}
|
|
|
|
text->w3doc = w3doc;
|
|
text->standardAltText_textOffset= POOL_GetOffset (&text->w3doc->pool);
|
|
text->standardAltText_textLen = strlen(GTR_GetString(SID_DLG_MISSING_IMAGE_HOLDER_STRING));
|
|
HText_add_to_pool(text->w3doc, GTR_GetString(SID_DLG_MISSING_IMAGE_HOLDER_STRING), text->standardAltText_textLen, TRUE);
|
|
|
|
W3Doc_ConnectToWindow(w3doc, tw);
|
|
|
|
return text;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_add_to_pool
|
|
|
|
RETURN:
|
|
Length of the text (in characters!) added.
|
|
|
|
NOTE:
|
|
If iNumChars passed in is non-zero, it is simply returned unchanged / checked
|
|
|
|
NOTES:
|
|
This function now combines the functionality of the original HText_add_to_pool
|
|
and HText_add_to_pool_iso functions by using a bUseMapping parameter.
|
|
|
|
bUseMapping = TRUE is used to map iso characters into MAC displayable characters
|
|
This parameter is ignored for other platforms [der: 6/15/95]
|
|
*****************************************************************************/
|
|
int
|
|
HText_add_to_pool
|
|
(struct _www* w3doc,
|
|
const char* pCharsToAdd,
|
|
int iNumChars,
|
|
BOOL bUseMapping)
|
|
{
|
|
return (w3doc->pool.f->AddChars) (&w3doc->pool, (char*) pCharsToAdd, iNumChars, bUseMapping);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_add_element
|
|
*****************************************************************************/
|
|
void HText_add_element(HText * text, int type)
|
|
{
|
|
int len;
|
|
|
|
/*
|
|
If we erroneously got a new element while we were in a TEXTAREA, implicitly
|
|
close it. Otherwise, when we do end the text area, we'll crash when we
|
|
assume that there's a text field in the current element.
|
|
*/
|
|
if (text->bTextArea)
|
|
{
|
|
HText_endTextArea(text);
|
|
}
|
|
|
|
#ifdef UNIX
|
|
/*
|
|
* Needed to add this to make sure that the end select clean up
|
|
* form procedure is called. Causes big problems if it is not.
|
|
* this can happen when the user aborts a document with a select
|
|
* form item in it. Not sure if this is needed on other platforms.
|
|
*/
|
|
if (text->bSelect && type == -1)
|
|
{
|
|
HText_endSelect(text);
|
|
}
|
|
#endif
|
|
|
|
#ifdef FEATURE_TABLES_IMPLICIT_TD
|
|
if (x_is_in_table_but_not_in_cell(text))
|
|
{
|
|
/* there are many tables appearing on the web
|
|
* in which the author fails to include the first
|
|
* <td> or <th> or fails to include a <td> or <th>
|
|
* after a <tr> or </caption>.
|
|
*
|
|
* this is a simple hack to try to
|
|
* catch that and insert an implicit <td>.
|
|
* we only do this if the new element is
|
|
* a displayable item and non-whitespace.
|
|
*/
|
|
|
|
switch (type)
|
|
{
|
|
case ELE_IMAGE:
|
|
case ELE_HR:
|
|
case ELE_LISTITEM:
|
|
case ELE_EDIT:
|
|
case ELE_PASSWORD:
|
|
case ELE_CHECKBOX:
|
|
case ELE_RADIO:
|
|
case ELE_SUBMIT:
|
|
case ELE_RESET:
|
|
case ELE_COMBO:
|
|
case ELE_LIST:
|
|
case ELE_TEXTAREA:
|
|
case ELE_MULTILIST:
|
|
case ELE_OPENLISTITEM:
|
|
case ELE_CLOSELISTITEM:
|
|
case ELE_FORMIMAGE:
|
|
case ELE_BULLET:
|
|
case ELE_BEGINTABLE:
|
|
XX_DMsg(DBG_TABLES,("Implicit <td> inserted after [iElement %d]\n",text->iElement));
|
|
HText_beginTD(text,NULL,NULL,NULL,NULL,NULL,NULL);
|
|
break; /* visible */
|
|
|
|
#ifdef LET_DEFAULT_HANDLE_IT
|
|
case ELE_NOT:
|
|
case ELE_TEXT:
|
|
case ELE_VERTICALTAB:
|
|
case ELE_NEWLINE:
|
|
case ELE_BEGINLIST:
|
|
case ELE_ENDLIST:
|
|
case ELE_INDENT:
|
|
case ELE_OUTDENT:
|
|
case ELE_BEGINFORM:
|
|
case ELE_ENDFORM:
|
|
case ELE_HIDDEN:
|
|
case ELE_TAB:
|
|
case ELE_BEGINCENTER:
|
|
case ELE_ENDCENTER:
|
|
case ELE_BRCLEARLEFT:
|
|
case ELE_BRCLEARRIGHT:
|
|
case ELE_BRCLEARALL:
|
|
case ELE_ENDTABLE:
|
|
case ELE_BEGINCELL:
|
|
case ELE_ENDCELL:
|
|
case ELE_VOID:
|
|
case ELE_BEGINRIGHT:
|
|
case ELE_ENDRIGHT:
|
|
#endif
|
|
default:
|
|
break; /* ignore non-visible */
|
|
}
|
|
}
|
|
#endif /* FEATURE_TABLES_IMPLICIT_TD */
|
|
|
|
#if 0
|
|
if (text->bSelect)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("HText_add_element called with type=%d in middle of SELECT!!\n", type));
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/* check for need to grow element array */
|
|
if (text->w3doc->elementCount >= text->w3doc->elementSpace)
|
|
{
|
|
struct _element* newArray;
|
|
int newSpace;
|
|
|
|
newSpace = ((text->w3doc->elementSpace * 3) / 2);
|
|
newArray = (struct _element *) GTR_REALLOC(text->w3doc->aElements, newSpace * sizeof(struct _element));
|
|
|
|
if (!newArray)
|
|
{ /* See if we can at least get a bit more */
|
|
newSpace = text->w3doc->elementSpace + 100;
|
|
newArray = (struct _element *) GTR_REALLOC(text->w3doc->aElements, newSpace * sizeof(struct _element));
|
|
|
|
if (!newArray)
|
|
{ /* Not much we can do here without error propagation */
|
|
XX_Assert((0), ("Unable to grow element list - realloc failed"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
XX_DMsg(DBG_HTEXT, ("HText_add_element: growing element array from %d to %d elements\n", text->w3doc->elementSpace, newSpace));
|
|
memset(newArray + text->w3doc->elementCount, 0, (newSpace - text->w3doc->elementCount) * sizeof(struct _element));
|
|
text->w3doc->aElements = newArray;
|
|
text->w3doc->elementSpace = newSpace;
|
|
}
|
|
|
|
text->bOpen = FALSE;
|
|
text->iPrevElement = text->iElement;
|
|
text->iElement = text->w3doc->elementCount++;
|
|
memset(&(text->w3doc->aElements[text->iElement]), 0, sizeof(struct _element));
|
|
|
|
if (text->w3doc->elementTail != -1)
|
|
{
|
|
text->w3doc->aElements[text->w3doc->elementTail].next = text->iElement;
|
|
}
|
|
|
|
#ifdef FEATURE_TABLES
|
|
text->w3doc->aElements[text->iElement].prev = text->iPrevElement;
|
|
|
|
XX_DMsg(DBG_TABLES,("Elements: previous [prev %d][i %d][next %d]\n",
|
|
text->w3doc->aElements[text->iPrevElement].prev,
|
|
text->iPrevElement,
|
|
text->w3doc->aElements[text->iPrevElement].next));
|
|
XX_DMsg(DBG_TABLES,("Elements: current [prev %d][i %d][next %d]\n",
|
|
text->w3doc->aElements[text->iElement].prev,
|
|
text->iElement,
|
|
text->w3doc->aElements[text->iElement].next));
|
|
|
|
#endif /* FEATURE_TABLES */
|
|
|
|
text->w3doc->aElements[text->iElement].next = -1;
|
|
text->w3doc->elementTail = text->iElement;
|
|
|
|
text->w3doc->aElements[text->iElement].type = type;
|
|
#if defined(WIN32) || defined(UNIX)
|
|
if (type == ELE_TEXT)
|
|
{
|
|
text->w3doc->aElements[text->iElement].portion.text.cached_font_index = -1;
|
|
}
|
|
#endif
|
|
XX_DMsg(DBG_HTEXT, ("HText_add_element created element %d with type=%d\n", text->iElement, type));
|
|
|
|
if (text->bAnchor)
|
|
{
|
|
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_ANCHOR;
|
|
text->w3doc->aElements[text->iElement].hrefOffset = text->hrefOffset;
|
|
text->w3doc->aElements[text->iElement].hrefLen = (unsigned short) text->hrefLen;
|
|
if (TW_WasVisited(text->w3doc, &(text->w3doc->aElements[text->iElement])))
|
|
{
|
|
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_VISITED;
|
|
}
|
|
}
|
|
|
|
if (text->bHighlight)
|
|
{
|
|
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_HIGHLIGHT;
|
|
}
|
|
|
|
if (text->name[0])
|
|
{
|
|
switch (type)
|
|
{
|
|
case ELE_TEXT:
|
|
case ELE_IMAGE:
|
|
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_NAME;
|
|
text->w3doc->aElements[text->iElement].nameOffset = POOL_GetOffset (&text->w3doc->pool);
|
|
len = HText_add_to_pool(text->w3doc, text->name, 0, TRUE);
|
|
text->w3doc->aElements[text->iElement].nameLen = len;
|
|
text->name[0] = CH_NULL; /* We only need one element with the name */
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (type == ELE_TEXT)
|
|
{
|
|
text->w3doc->aElements[text->iElement].portion.text.font_request.logical_font_size = text->w3doc->pStyles->sty[text->iStyle]->font_request.logical_font_size;
|
|
|
|
text->w3doc->aElements[text->iElement].textOffset = POOL_GetOffset (&text->w3doc->pool);
|
|
text->w3doc->aElements[text->iElement].textLen = 0;
|
|
|
|
if (text->numOpenFontTags > 0)
|
|
{
|
|
if (text->myOpenFontTags[text->numOpenFontTags - 1].flags & HTEXT_OPENFONTTAG_SIZE)
|
|
{
|
|
text->w3doc->aElements[text->iElement].portion.text.font_request.logical_font_size = text->myOpenFontTags[text->numOpenFontTags - 1].mySize;
|
|
}
|
|
if (text->myOpenFontTags[text->numOpenFontTags - 1].flags & HTEXT_OPENFONTTAG_COLOR)
|
|
{
|
|
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_FONT_COLOR;
|
|
text->w3doc->aElements[text->iElement].portion.text.myColor = text->myOpenFontTags[text->numOpenFontTags - 1].myColor;
|
|
}
|
|
}
|
|
|
|
text->w3doc->aElements[text->iElement].portion.text.font_request.flags = text->fontBits;
|
|
}
|
|
|
|
text->w3doc->aElements[text->iElement].iStyle = text->iStyle;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_appendCRLF
|
|
*****************************************************************************/
|
|
void HText_appendCRLF(HText * text)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("appendCRLF\n"));
|
|
HText_add_element(text, ELE_NEWLINE);
|
|
text->bNewPara = TRUE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_appendBR
|
|
*****************************************************************************/
|
|
void HText_appendBR(HText * text, const char *clear)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("appendCRLF\n"));
|
|
HText_add_element(text, ELE_NEWLINE);
|
|
text->bNewPara = TRUE;
|
|
|
|
if (clear)
|
|
{
|
|
if (0 == GTR_strcmpi(clear, "left"))
|
|
{
|
|
HText_add_element(text, ELE_BRCLEARLEFT);
|
|
}
|
|
else if (0 == GTR_strcmpi(clear, "right"))
|
|
{
|
|
HText_add_element(text, ELE_BRCLEARRIGHT);
|
|
}
|
|
else if (0 == GTR_strcmpi(clear, "all"))
|
|
{
|
|
HText_add_element(text, ELE_BRCLEARALL);
|
|
}
|
|
else
|
|
{
|
|
HText_add_element(text, ELE_BRCLEARALL);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_appendText
|
|
*****************************************************************************/
|
|
void HText_appendText(HText * text, CONST char *str)
|
|
{
|
|
char* p;
|
|
|
|
if (!str) return;
|
|
|
|
text->bOpen = FALSE;
|
|
|
|
#ifdef FEATURE_TABLES_IMPLICIT_TD
|
|
if (x_is_in_table_but_not_in_cell(text))
|
|
{
|
|
for (p = (char*) str; (*p); p++)
|
|
if ( ! isspace((unsigned char)*p))
|
|
{
|
|
XX_DMsg(DBG_TABLES,
|
|
("Implicit <td> inserted after [iElement %d] before appendText [%s]\n",
|
|
text->iElement,p));
|
|
HText_beginTD(text,NULL,NULL,NULL,NULL,NULL,NULL);
|
|
break;
|
|
}
|
|
}
|
|
#endif /* FEATURE_TABLES_IMPLICIT_TD */
|
|
|
|
p = (char*) str;
|
|
while (*p)
|
|
{
|
|
HText_appendCharacter(text, *p);
|
|
p++;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
HText_appendCharacter
|
|
*****************************************************************************/
|
|
void HText_appendCharacter(HText * text, char ch)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("HText_appendCharacter: %c(%d)\n", ch, ch));
|
|
|
|
if (text->bOption)
|
|
{
|
|
if (text->lenOption < MAX_NAME_STRING)
|
|
{
|
|
switch (ch)
|
|
{
|
|
case CH_LF:
|
|
case CH_CR:
|
|
break;
|
|
|
|
case CH_SPACE:
|
|
if (text->lenOption)
|
|
{
|
|
text->szOption[text->lenOption++] = ch;
|
|
text->szOption[text->lenOption] = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
text->szOption[text->lenOption++] = ch;
|
|
text->szOption[text->lenOption] = 0;
|
|
break;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (text->bTextArea)
|
|
{
|
|
switch (ch)
|
|
{
|
|
case CH_CR:
|
|
case CH_LF:
|
|
CS_AddChar(text->cs, CH_LF);
|
|
#ifndef MAC
|
|
CS_AddChar(text->cs, CH_CR);
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
CS_AddChar(text->cs, ch);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!text->w3doc->pStyles->sty[text->iStyle]->freeFormat)
|
|
{ /* freeFormat means eat extra whitespace */
|
|
/* preformatted */
|
|
switch (ch)
|
|
{
|
|
case CH_TAB:
|
|
HText_add_element(text, ELE_TAB);
|
|
break;
|
|
|
|
case 12: /* CTRL-L */
|
|
case CH_LF:
|
|
case CH_CR:
|
|
if (!(text->prev_char == CH_LF && ch == CH_CR))
|
|
{
|
|
HText_appendCRLF(text);
|
|
}
|
|
text->prev_char = ch;
|
|
break;
|
|
|
|
default:
|
|
if (!text->bOpen)
|
|
{
|
|
HText_add_element(text, ELE_TEXT);
|
|
text->bOpen = TRUE;
|
|
}
|
|
HText_add_to_pool(text->w3doc, &ch, 1, TRUE);
|
|
text->w3doc->aElements[text->iElement].textLen++;
|
|
text->prev_char = ch;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{ /* isspace doesn't work with non-ascii characters */
|
|
if (ch > 0 && isspace((unsigned char)ch))
|
|
{
|
|
ch = CH_SPACE;
|
|
}
|
|
|
|
switch (ch)
|
|
{
|
|
case CH_SPACE:
|
|
if (text->bNewPara)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (text->prev_char == CH_SPACE)
|
|
{
|
|
if (!text->bOpen)
|
|
{
|
|
HText_add_element(text, ELE_TEXT);
|
|
text->bOpen = TRUE;
|
|
HText_add_to_pool(text->w3doc, &ch, 1, TRUE);
|
|
text->w3doc->aElements[text->iElement].textLen++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!text->bOpen)
|
|
{
|
|
HText_add_element(text, ELE_TEXT);
|
|
text->bOpen = TRUE;
|
|
}
|
|
|
|
HText_add_to_pool(text->w3doc, &ch, 1, TRUE);
|
|
text->w3doc->aElements[text->iElement].textLen++;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (!text->bOpen)
|
|
{
|
|
#ifdef FEATURE_TABLES_IMPLICIT_TD
|
|
if (x_is_in_table_but_not_in_cell(text))
|
|
{
|
|
XX_DMsg(DBG_TABLES,("Implicit <td> inserted after [iElement %d] before text [%c]\n",text->iElement,ch));
|
|
HText_beginTD(text,NULL,NULL,NULL,NULL,NULL,NULL);
|
|
}
|
|
#endif /* FEATURE_TABLES_IMPLICIT_TD */
|
|
HText_add_element(text, ELE_TEXT);
|
|
text->bOpen = TRUE;
|
|
}
|
|
text->bNewPara = FALSE;
|
|
text->bStartingListItem = FALSE;
|
|
HText_add_to_pool(text->w3doc, &ch, 1, TRUE);
|
|
text->w3doc->aElements[text->iElement].textLen++;
|
|
break;
|
|
}
|
|
text->prev_char = ch;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_beginAnchor
|
|
|
|
TODO this can overflow the buffer (FAQ list on commercenet)
|
|
*****************************************************************************/
|
|
void HText_beginAnchor(HText * text, char *href, char *name)
|
|
{
|
|
if (href)
|
|
{
|
|
text->hrefOffset = POOL_GetOffset (&text->w3doc->pool);
|
|
text->hrefLen = HText_add_to_pool (text->w3doc, href, 0, FALSE);
|
|
text->bAnchor = TRUE;
|
|
text->bOpen = FALSE;
|
|
}
|
|
|
|
if (name)
|
|
{
|
|
strncpy(text->name, name, MAX_NAME_STRING);
|
|
text->name[MAX_NAME_STRING] = CH_NULL;
|
|
text->bOpen = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_endAnchor
|
|
*****************************************************************************/
|
|
void HText_endAnchor(HText * text)
|
|
{
|
|
if (text->bAnchor)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("endAnchor\n"));
|
|
text->bAnchor = FALSE;
|
|
text->hrefOffset = 0;
|
|
text->hrefLen = 0;
|
|
text->bOpen = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_beginCenter
|
|
*****************************************************************************/
|
|
void HText_beginCenter(HText * text)
|
|
{
|
|
HText_add_element(text, ELE_BEGINCENTER);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_endCenter
|
|
*****************************************************************************/
|
|
void HText_endCenter(HText * text)
|
|
{
|
|
HText_add_element(text, ELE_ENDCENTER);
|
|
text->bNewPara = TRUE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_beginRight
|
|
*****************************************************************************/
|
|
void HText_beginRight(HText * text)
|
|
{
|
|
HText_add_element(text, ELE_BEGINRIGHT);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_endRight
|
|
*****************************************************************************/
|
|
void HText_endRight(HText * text)
|
|
{
|
|
HText_add_element(text, ELE_ENDRIGHT);
|
|
text->bNewPara = TRUE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_beginHighlight
|
|
*****************************************************************************/
|
|
void HText_beginHighlight(HText * text)
|
|
{
|
|
text->bHighlight = TRUE;
|
|
text->bOpen = FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_endHighlight
|
|
*****************************************************************************/
|
|
void HText_endHighlight(HText * text)
|
|
{
|
|
text->bHighlight = FALSE;
|
|
text->bOpen = FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_appendInlineImage
|
|
*****************************************************************************/
|
|
void HText_appendInlineImage(HText * text, const char *src, const char *width, const char *height, const char *align, BOOL isMap, const char *useMap, const char *alt, const char *border, const char *hspace, const char *vspace, const char *dock)
|
|
{
|
|
int len;
|
|
int nWidth;
|
|
int nHeight;
|
|
|
|
if (!src)
|
|
{
|
|
HText_appendText(text, "<IMAGE>");
|
|
return;
|
|
}
|
|
|
|
HText_add_element(text, ELE_IMAGE);
|
|
|
|
if (border && border[0])
|
|
{
|
|
text->w3doc->aElements[text->iElement].iBorder = atoi(border);
|
|
}
|
|
else
|
|
{
|
|
text->w3doc->aElements[text->iElement].iBorder = text->bAnchor ? 2 : 0;
|
|
}
|
|
|
|
if (hspace && hspace[0])
|
|
{
|
|
text->w3doc->aElements[text->iElement].portion.img.hspace = atoi(hspace);
|
|
}
|
|
else
|
|
{
|
|
text->w3doc->aElements[text->iElement].portion.img.hspace = 2;
|
|
}
|
|
|
|
if (vspace && vspace[0])
|
|
{
|
|
text->w3doc->aElements[text->iElement].portion.img.vspace = atoi(vspace);
|
|
}
|
|
else
|
|
{
|
|
text->w3doc->aElements[text->iElement].portion.img.vspace = 2;
|
|
}
|
|
|
|
if (alt && alt[0])
|
|
{
|
|
text->w3doc->aElements[text->iElement].textOffset = POOL_GetOffset (&text->w3doc->pool);
|
|
len = HText_add_to_pool(text->w3doc, alt, 0, TRUE);
|
|
text->w3doc->aElements[text->iElement].textLen = len;
|
|
}
|
|
else
|
|
{
|
|
text->w3doc->aElements[text->iElement].textOffset = text->standardAltText_textOffset;
|
|
text->w3doc->aElements[text->iElement].textLen = text->standardAltText_textLen;
|
|
}
|
|
|
|
if (isMap)
|
|
{
|
|
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_IMAGEMAP;
|
|
}
|
|
|
|
XX_DMsg(DBG_HTEXT, ("appendInlineImage: src=%s\n", src));
|
|
|
|
if (align && !GTR_strcmpi((char *) align, "top"))
|
|
{
|
|
text->w3doc->aElements[text->iElement].alignment = ALIGN_TOP;
|
|
}
|
|
else if (align && !GTR_strcmpi((char *) align, "middle"))
|
|
{
|
|
text->w3doc->aElements[text->iElement].alignment = ALIGN_MIDDLE;
|
|
}
|
|
else if (align && !GTR_strcmpi((char *) align, "left"))
|
|
{
|
|
text->w3doc->aElements[text->iElement].alignment = ALIGN_LEFT;
|
|
}
|
|
else if (align && !GTR_strcmpi((char *) align, "right"))
|
|
{
|
|
text->w3doc->aElements[text->iElement].alignment = ALIGN_RIGHT;
|
|
}
|
|
#if 0
|
|
else if (align && !GTR_strcmpi((char *) align, "bottom"))
|
|
{
|
|
text->w3doc->aElements[text->iElement].alignment = ALIGN_BOTTOM;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
text->w3doc->aElements[text->iElement].alignment = ALIGN_BOTTOM;
|
|
}
|
|
|
|
if (( text->w3doc->aElements[text->iElement].alignment == ALIGN_LEFT)
|
|
|| (text->w3doc->aElements[text->iElement].alignment == ALIGN_RIGHT))
|
|
{
|
|
struct ImageElementNode *ien;
|
|
|
|
ien = GTR_CALLOC(1, sizeof(*ien));
|
|
if (ien)
|
|
{
|
|
ien->elementIndex = text->iElement;
|
|
ien->next = text->w3doc->image_list;
|
|
text->w3doc->image_list = ien;
|
|
}
|
|
else
|
|
{
|
|
ERR_ReportError(NULL, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
The dock attribute takes precedence over the align attr, so
|
|
we can use the same variable for both.
|
|
*/
|
|
if (dock)
|
|
{
|
|
if (!GTR_strcmpi((char *) dock, "left"))
|
|
{
|
|
text->w3doc->aElements[text->iElement].alignment = DOCK_LEFT;
|
|
}
|
|
else if (!GTR_strcmpi((char *) dock, "top"))
|
|
{
|
|
text->w3doc->aElements[text->iElement].alignment = DOCK_TOP;
|
|
}
|
|
else if (!GTR_strcmpi((char *) dock, "right"))
|
|
{
|
|
text->w3doc->aElements[text->iElement].alignment = DOCK_RIGHT;
|
|
}
|
|
else if (!GTR_strcmpi((char *) dock, "bottom"))
|
|
{
|
|
text->w3doc->aElements[text->iElement].alignment = DOCK_BOTTOM;
|
|
}
|
|
}
|
|
|
|
if (useMap && *useMap)
|
|
{
|
|
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_USEMAP;
|
|
text->w3doc->aElements[text->iElement].portion.img.myMap = Map_CreatePlaceholder(useMap);
|
|
text->w3doc->aElements[text->iElement].portion.img.myMap->refCount++;
|
|
}
|
|
|
|
if (width && height)
|
|
{
|
|
nWidth = atoi(width);
|
|
nHeight = atoi(height);
|
|
|
|
/* ignore any silly stuff */
|
|
if (nWidth <= 0 || nHeight <= 0)
|
|
{
|
|
nWidth = 0;
|
|
nHeight = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nWidth = 0;
|
|
nHeight = 0;
|
|
}
|
|
|
|
text->w3doc->aElements[text->iElement].portion.img.height = nHeight;
|
|
text->w3doc->aElements[text->iElement].portion.img.width = nWidth;
|
|
|
|
text->w3doc->aElements[text->iElement].portion.img.myImage =
|
|
Image_CreatePlaceholder((char *) src, nWidth, nHeight,
|
|
text->w3doc, text->iElement);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_appendHorizontalRule
|
|
*****************************************************************************/
|
|
void HText_appendHorizontalRule(HText * text, CONST char *size, CONST char *align, CONST char *width, BOOL bNoShade)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("appendHorizontalRule\n"));
|
|
|
|
HText_add_element(text, ELE_HR);
|
|
text->bNewPara = TRUE;
|
|
|
|
if (size && size[0])
|
|
{
|
|
text->w3doc->aElements[text->iElement].portion.hr.vsize = atoi(size);
|
|
}
|
|
else
|
|
{
|
|
text->w3doc->aElements[text->iElement].portion.hr.vsize = 2;
|
|
}
|
|
|
|
if (width && width[0])
|
|
{
|
|
text->w3doc->aElements[text->iElement].portion.hr.hsize = atoi(width);
|
|
if (!strchr(width, '%'))
|
|
{
|
|
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_WIDTH_PIXELS;
|
|
}
|
|
else
|
|
{
|
|
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_WIDTH_PERCENT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
text->w3doc->aElements[text->iElement].portion.hr.hsize = 0;
|
|
}
|
|
|
|
if (align && !GTR_strcmpi((char *) align, "left"))
|
|
{
|
|
text->w3doc->aElements[text->iElement].alignment = ALIGN_LEFT;
|
|
}
|
|
else if (align && !GTR_strcmpi((char *) align, "right"))
|
|
{
|
|
text->w3doc->aElements[text->iElement].alignment = ALIGN_RIGHT;
|
|
}
|
|
else
|
|
{
|
|
text->w3doc->aElements[text->iElement].alignment = ALIGN_CENTER;
|
|
}
|
|
|
|
if (bNoShade)
|
|
{
|
|
text->w3doc->aElements[text->iElement].lFlags |= ELEFLAG_HR_NOSHADE;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_beginAppend
|
|
*****************************************************************************/
|
|
void HText_beginAppend(HText * text)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("beginAppend\n"));
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_endAppend
|
|
*****************************************************************************/
|
|
void HText_endAppend(HText * text)
|
|
{
|
|
int nEl;
|
|
|
|
XX_DMsg(DBG_HTEXT, ("endAppend\n"));
|
|
HText_add_element(text, ELE_ENDDOC);
|
|
text->w3doc->bIsComplete = TRUE;
|
|
|
|
if (gPrefs.ReformatHandling > 0)
|
|
{
|
|
if (text->szLocal)
|
|
{ /* See if the local anchor we're jumping to has appeared yet */
|
|
nEl = TW_FindLocalAnchor(text->w3doc, text->szLocal);
|
|
if (nEl >= 0 && nEl != text->w3doc->elementTail)
|
|
{ /* We found it! */
|
|
if (WAIT_GetWaitType(text->tw) >= waitNoInteract)
|
|
{ /* Let the user interact with the new document */
|
|
WAIT_UpdateWaitStack(text->tw, waitPartialInteract, INT_MAX);
|
|
}
|
|
|
|
/* Format any last little bit now that bIsComplete */
|
|
TW_Reformat(text->tw);
|
|
TW_ScrollToElement(text->tw, nEl);
|
|
text->szLocal = NULL; /* Don't free it - it's part of the struct DestInfo */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (WAIT_GetWaitType (text->tw) >= waitNoInteract)
|
|
{ /* Let the user interact with the new document */
|
|
WAIT_UpdateWaitStack (text->tw, waitPartialInteract, INT_MAX);
|
|
}
|
|
|
|
/* Format any last little bit now that bIsComplete */
|
|
TW_Reformat(text->tw);
|
|
}
|
|
}
|
|
|
|
GTR_FREE(text);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_abort
|
|
*****************************************************************************/
|
|
void HText_abort(HText * text)
|
|
{
|
|
HText_add_element(text, ELE_ENDDOC);
|
|
TW_Reformat(text->tw);
|
|
GTR_FREE(text);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_appendVerticalTab
|
|
*****************************************************************************/
|
|
void HText_appendVerticalTab(HText * text, int lines)
|
|
{
|
|
if (lines)
|
|
{
|
|
HText_add_element(text, ELE_VERTICALTAB);
|
|
text->w3doc->aElements[text->iElement].iBlankLines = lines;
|
|
}
|
|
}
|
|
|
|
void HText_endParagraph(HText * text)
|
|
{
|
|
switch (text->iParagraphAlign)
|
|
{
|
|
case ALIGN_CENTER:
|
|
HText_endCenter(text);
|
|
break;
|
|
case ALIGN_RIGHT:
|
|
HText_endRight(text);
|
|
break;
|
|
default:
|
|
/* Do nothing */
|
|
break;
|
|
}
|
|
|
|
HText_appendVerticalTab(text, 1);
|
|
|
|
text->iParagraphAlign = 0;
|
|
text->bOpen = FALSE;
|
|
text->bNewPara = TRUE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
HText_beginParagraph
|
|
*****************************************************************************/
|
|
void HText_beginParagraph(HText * text, CONST char *align)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("appendParagraph\n"));
|
|
|
|
/* Because we can have a <P> inside an <LI>, make sure it looks right. */
|
|
if (text->bStartingListItem)
|
|
{
|
|
text->bStartingListItem = FALSE;
|
|
}
|
|
else
|
|
{
|
|
HText_appendVerticalTab(text, 1);
|
|
}
|
|
text->bNewPara = TRUE;
|
|
|
|
HText_endParagraph(text);
|
|
|
|
if (align && GTR_strcmpi((char *) align, "center") == 0)
|
|
{
|
|
HText_beginCenter(text);
|
|
text->iParagraphAlign = ALIGN_CENTER;
|
|
}
|
|
else if (align && GTR_strcmpi((char *) align, "right") == 0)
|
|
{
|
|
HText_beginRight(text);
|
|
text->iParagraphAlign = ALIGN_RIGHT;
|
|
}
|
|
}
|
|
|
|
void HText_beginHeader(HText *text, int element_number, const char *align)
|
|
{
|
|
if (align && GTR_strcmpi((char *) align, "center") == 0)
|
|
{
|
|
HText_beginCenter(text);
|
|
text->iParagraphAlign = ALIGN_CENTER;
|
|
}
|
|
else if (align && GTR_strcmpi((char *) align, "right") == 0)
|
|
{
|
|
HText_beginRight(text);
|
|
text->iParagraphAlign = ALIGN_RIGHT;
|
|
}
|
|
}
|
|
|
|
void HText_endHeader(HText *text, int element_number)
|
|
{
|
|
switch (text->iParagraphAlign)
|
|
{
|
|
case ALIGN_CENTER:
|
|
HText_endCenter(text);
|
|
break;
|
|
case ALIGN_RIGHT:
|
|
HText_endRight(text);
|
|
break;
|
|
default:
|
|
/* Do nothing */
|
|
break;
|
|
}
|
|
|
|
text->iParagraphAlign = 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
HText_setStyle
|
|
*****************************************************************************/
|
|
void HText_setStyle(HText * text, int style_ndx)
|
|
{
|
|
struct GTRStyle *style;
|
|
int i;
|
|
|
|
style = NULL;
|
|
if (style_ndx >= 0 && style_ndx < COUNT_HTML_STYLES)
|
|
{
|
|
style = text->w3doc->pStyles->sty[style_ndx];
|
|
}
|
|
else
|
|
{
|
|
/** Scott: Added this because it was causing lots of problems **/
|
|
/** when it was set to a illegal value **/
|
|
style_ndx = 0;
|
|
style = text->w3doc->pStyles->sty[style_ndx];
|
|
}
|
|
|
|
if (text->bStartingListItem)
|
|
{
|
|
text->bStartingListItem = FALSE;
|
|
}
|
|
else
|
|
{ /*
|
|
Because of all the bad HTML out there, special case the situation
|
|
where an <LI> is immediately followed by a tag like an <H3>
|
|
*/
|
|
HText_appendCRLF(text);
|
|
HText_appendVerticalTab(text, text->w3doc->pStyles->sty[text->iStyle]->spaceAfter);
|
|
for (i=0; i<text->w3doc->pStyles->sty[text->iStyle]->nLeftIndents; i++)
|
|
{
|
|
HText_add_element(text, ELE_OUTDENT);
|
|
}
|
|
|
|
if (style)
|
|
{
|
|
for (i=0; i<style->nLeftIndents; i++)
|
|
{
|
|
HText_add_element(text, ELE_INDENT);
|
|
}
|
|
HText_appendVerticalTab(text, style->spaceBefore);
|
|
}
|
|
}
|
|
|
|
|
|
text->iStyle = style_ndx;
|
|
text->fontBits = style->font_request.flags;
|
|
text->bOpen = FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_beginStrikeOut
|
|
*****************************************************************************/
|
|
void HText_beginStrikeOut(HText * text)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("beginStrikeOut\n"));
|
|
|
|
text->fontBits |= FONTBIT_STRIKEOUT;
|
|
text->bOpen = FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_endStrikeOut
|
|
*****************************************************************************/
|
|
void HText_endStrikeOut(HText * text)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("endStrikeOut\n"));
|
|
text->fontBits &= (~FONTBIT_STRIKEOUT);
|
|
text->bOpen = FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_beginBold
|
|
*****************************************************************************/
|
|
void HText_beginBold(HText * text)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("beginBold\n"));
|
|
|
|
text->fontBits |= FONTBIT_BOLD;
|
|
text->bOpen = FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_endBold
|
|
*****************************************************************************/
|
|
void HText_endBold(HText * text)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("endBold\n"));
|
|
text->fontBits &= (~FONTBIT_BOLD);
|
|
text->bOpen = FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_beginItalic
|
|
*****************************************************************************/
|
|
void HText_beginItalic(HText * text)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("beginItalic\n"));
|
|
|
|
text->fontBits |= FONTBIT_ITALIC;
|
|
text->bOpen = FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_endItalic
|
|
*****************************************************************************/
|
|
void HText_endItalic(HText * text)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("endItalic\n"));
|
|
text->fontBits &= (~FONTBIT_ITALIC);
|
|
text->bOpen = FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_beginTT
|
|
*****************************************************************************/
|
|
void HText_beginTT(HText * text)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("beginTT\n"));
|
|
|
|
text->fontBits |= FONTBIT_MONOSPACE;
|
|
text->bOpen = FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_endTT
|
|
*****************************************************************************/
|
|
void HText_endTT(HText * text)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("endTT\n"));
|
|
text->fontBits &= (~FONTBIT_MONOSPACE);
|
|
text->bOpen = FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_beginUnderline
|
|
*****************************************************************************/
|
|
void HText_beginUnderline(HText * text)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("beginUnderline\n"));
|
|
|
|
text->fontBits |= FONTBIT_UNDERLINE;
|
|
text->bOpen = FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_endUnderline
|
|
*****************************************************************************/
|
|
void HText_endUnderline(HText * text)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("endUnderline\n"));
|
|
text->fontBits &= (~FONTBIT_UNDERLINE);
|
|
text->bOpen = FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_beginList
|
|
*****************************************************************************/
|
|
void HText_beginList(HText * text, int type)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("beginList\n"));
|
|
if (text->bDD)
|
|
{
|
|
HText_add_element(text, ELE_OUTDENT);
|
|
text->bDD = FALSE;
|
|
}
|
|
HText_appendCRLF(text);
|
|
#ifdef _GIBRALTAR
|
|
HText_appendCRLF(text);
|
|
#endif // _GIBRALTAR
|
|
|
|
HText_add_element(text, ELE_BEGINLIST);
|
|
text->list[text->next_list].type = type;
|
|
text->list[text->next_list].nItems = 0;
|
|
text->next_list++;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_endList
|
|
*****************************************************************************/
|
|
void HText_endList(HText * text, int type)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("endList\n"));
|
|
if (text->next_list > 0)
|
|
{
|
|
if (text->list[text->next_list - 1].nItems)
|
|
{
|
|
HText_add_element(text, ELE_CLOSELISTITEM);
|
|
text->bStartingListItem = FALSE;
|
|
}
|
|
HText_appendCRLF(text);
|
|
HText_add_element(text, ELE_ENDLIST);
|
|
text->next_list--;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_listItem
|
|
*****************************************************************************/
|
|
void HText_listItem(HText * text, int type)
|
|
{
|
|
XX_DMsg(DBG_HTEXT, ("listItem\n"));
|
|
|
|
/*
|
|
The following addition allows an <li> outside of a
|
|
list, by impliciting opening a <ul>.
|
|
*/
|
|
if (text->next_list <= 0)
|
|
{
|
|
HText_beginList(text, HTML_UL);
|
|
}
|
|
|
|
if (text->next_list > 0)
|
|
{
|
|
HText_appendCRLF(text);
|
|
if (text->list[text->next_list - 1].nItems)
|
|
{
|
|
HText_add_element(text, ELE_CLOSELISTITEM);
|
|
}
|
|
text->list[text->next_list - 1].nItems++;
|
|
switch (text->list[text->next_list - 1].type)
|
|
{
|
|
case HTML_DL:
|
|
#ifdef UNIX
|
|
HText_add_element(text, ELE_BULLET);
|
|
#endif
|
|
text->bStartingListItem = TRUE;
|
|
break;
|
|
case HTML_UL:
|
|
#ifdef UNIX
|
|
HText_add_element(text, ELE_BULLET);
|
|
#else
|
|
/* This is an ISO character set bullet. */
|
|
HText_appendCharacter(text, '•');
|
|
#endif
|
|
text->bStartingListItem = TRUE;
|
|
break;
|
|
case HTML_OL:
|
|
{
|
|
char buf[32];
|
|
|
|
#ifdef UNIX
|
|
sprintf(buf, "%4.4ld.", (long) text->list[text->next_list - 1].nItems);
|
|
/* printf("Before conversion >%s< len = %d.\n", buf, strlen(buf)); */
|
|
/** Now strip any leading '0' in the string **/
|
|
{
|
|
int n = 0;
|
|
while (buf[n] && buf[n] == '0')
|
|
{
|
|
buf[n] = CH_SPACE;
|
|
n++;
|
|
}
|
|
}
|
|
/* printf("After conversion >%s< len = %d.\n", buf, strlen(buf)); */
|
|
#else
|
|
sprintf(buf, "%ld", (long) text->list[text->next_list - 1].nItems);
|
|
#endif
|
|
HText_appendText(text, buf);
|
|
}
|
|
text->bStartingListItem = TRUE;
|
|
break;
|
|
default:
|
|
#ifdef UNIX
|
|
HText_add_element(text, ELE_BULLET);
|
|
#endif
|
|
break;
|
|
}
|
|
HText_add_element(text, ELE_OPENLISTITEM);
|
|
|
|
/*
|
|
Treat a list as a new paragraph so we won't get spurious spaces
|
|
before list text.
|
|
*/
|
|
text->bNewPara = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_beginGlossary
|
|
*****************************************************************************/
|
|
void HText_beginGlossary(HText * text)
|
|
{
|
|
HText_beginList(text, HTML_DL);
|
|
text->bDD = FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_endGlossary
|
|
*****************************************************************************/
|
|
void HText_endGlossary(HText * text)
|
|
{
|
|
if (text->bDD)
|
|
{
|
|
HText_add_element(text, ELE_OUTDENT);
|
|
text->bDD = FALSE;
|
|
}
|
|
HText_endList(text, HTML_DL);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_beginDT
|
|
*****************************************************************************/
|
|
void HText_beginDT(HText * text, int type)
|
|
{
|
|
HText_appendCRLF(text);
|
|
if (text->bDD)
|
|
{
|
|
HText_add_element(text, ELE_OUTDENT);
|
|
text->bDD = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_beginDD
|
|
*****************************************************************************/
|
|
void HText_beginDD(HText * text, int type)
|
|
{
|
|
HText_appendCRLF(text);
|
|
if (!text->bDD)
|
|
{
|
|
HText_add_element(text, ELE_INDENT);
|
|
text->bDD = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_setTitle
|
|
*****************************************************************************/
|
|
void HText_setTitle(HText *text, const char *title)
|
|
{
|
|
if (text->w3doc->title)
|
|
{
|
|
GTR_FREE(text->w3doc->title);
|
|
}
|
|
|
|
text->w3doc->title = GTR_strdup(title);
|
|
|
|
#ifdef MAC
|
|
isoMapString (text->w3doc->title);
|
|
#endif
|
|
|
|
if (text->tw)
|
|
{
|
|
TW_SetWindowName(text->tw);
|
|
}
|
|
|
|
if (text->bRecord && !text->bHaveTitle)
|
|
{ /* Update the global history in case we didn't have the title before */
|
|
text->bHaveTitle = TRUE;
|
|
GHist_Add(text->w3doc->szActualURL, text->w3doc->title, time(NULL));
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_update
|
|
*****************************************************************************/
|
|
void HText_update(HText *text)
|
|
{
|
|
/* If the document is already complete, everything should already be formatted. */
|
|
if (text->w3doc->bIsComplete)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (gPrefs.ReformatHandling <=0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (text->szLocal)
|
|
{ /* See if the local anchor we're jumping to has appeared yet */
|
|
int nEl = TW_FindLocalAnchor(text->w3doc, text->szLocal);
|
|
|
|
if (nEl >= 0 && nEl != text->w3doc->elementTail)
|
|
{
|
|
TW_Reformat(text->tw);
|
|
if (TW_ScrollToElement(text->tw, nEl))
|
|
{ /* We found it! */
|
|
|
|
/* Let the user interact with the new document */
|
|
if (WAIT_GetWaitType(text->tw) >= waitNoInteract)
|
|
{
|
|
WAIT_UpdateWaitStack(text->tw, waitPartialInteract, INT_MAX);
|
|
}
|
|
text->szLocal = NULL; /* Don't free it - it's part of the struct DestInfo */
|
|
}
|
|
else
|
|
{ /* Keep the document from displaying */
|
|
text->w3doc->nLastFormattedLine = -1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Let the user interact with the new document */
|
|
if (WAIT_GetWaitType(text->tw) >= waitNoInteract)
|
|
{
|
|
WAIT_UpdateWaitStack(text->tw, waitPartialInteract, INT_MAX);
|
|
}
|
|
|
|
if (text->iElement > 0)
|
|
{
|
|
TW_Reformat(text->tw);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
HText_setIndex
|
|
*****************************************************************************/
|
|
void HText_setIndex(HText * text, const char *action, const char *prompt)
|
|
{
|
|
HText_appendHorizontalRule(text, NULL, NULL, NULL, FALSE);
|
|
if (prompt && (*prompt))
|
|
{
|
|
HText_appendText(text, prompt);
|
|
}
|
|
else
|
|
{
|
|
HText_appendText(text, GTR_GetString(SID_DLG_SEARCHABLE_INDEX_ENTER_KEYWORD));
|
|
}
|
|
|
|
HText_beginForm(text, (char *) action, "GET");
|
|
HText_addInput(text, FALSE, FALSE, NULL, NULL, "isindex", NULL, "text", NULL, NULL, NULL, NULL);
|
|
HText_endForm(text);
|
|
HText_appendHorizontalRule(text, NULL, NULL, NULL, FALSE);
|
|
}
|
|
|
|
BOOL is_hex_digit(char c)
|
|
{
|
|
if (isdigit(c))
|
|
{
|
|
return TRUE;
|
|
}
|
|
if ((c >= 'a') && (c <= 'f'))
|
|
{
|
|
return TRUE;
|
|
}
|
|
if ((c >= 'A') && (c <= 'F'))
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
COLORREF ParseColorRef(const char *s)
|
|
{
|
|
const char *p;
|
|
int r, g, b;
|
|
unsigned long rgb = 0;
|
|
char buf[16];
|
|
int count;
|
|
|
|
p = s;
|
|
count = 0;
|
|
while (*p && (count < 6))
|
|
{
|
|
if (is_hex_digit(*p))
|
|
{
|
|
buf[count++] = *p;
|
|
}
|
|
p++;
|
|
}
|
|
buf[count] = 0;
|
|
|
|
if (buf[0])
|
|
{
|
|
sscanf(buf, "%x", &rgb);
|
|
}
|
|
|
|
r = (rgb >> 16) & 0xff;
|
|
g = (rgb >> 8) & 0xff;
|
|
b = rgb & (0xff);
|
|
|
|
return GTR_MakeCOLORREF(r,g,b);
|
|
}
|
|
|
|
void HText_SetBodyAttributes(HText *text, const char *s_alink, const char *s_background, const char *s_bgcolor, const char *s_link, const char *s_text, const char *s_vlink)
|
|
{
|
|
if (s_background)
|
|
{
|
|
text->w3doc->piiBackground =
|
|
Image_CreatePlaceholder(s_background, 0, 0, (struct _www *)NULL, -1);
|
|
if (text->w3doc->piiBackground)
|
|
{
|
|
text->w3doc->piiBackground->refCount++;
|
|
}
|
|
}
|
|
|
|
if (s_alink)
|
|
{
|
|
text->w3doc->color_alink = ParseColorRef(s_alink);
|
|
text->w3doc->lFlags |= W3DOC_FLAG_COLOR_ALINK;
|
|
}
|
|
if (s_bgcolor)
|
|
{
|
|
text->w3doc->color_bgcolor = ParseColorRef(s_bgcolor);
|
|
text->w3doc->lFlags |= W3DOC_FLAG_COLOR_BGCOLOR;
|
|
}
|
|
if (s_link)
|
|
{
|
|
text->w3doc->color_link = ParseColorRef(s_link);
|
|
text->w3doc->lFlags |= W3DOC_FLAG_COLOR_LINK;
|
|
}
|
|
if (s_text)
|
|
{
|
|
text->w3doc->color_text = ParseColorRef(s_text);
|
|
text->w3doc->lFlags |= W3DOC_FLAG_COLOR_TEXT;
|
|
}
|
|
if (s_vlink)
|
|
{
|
|
text->w3doc->color_vlink = ParseColorRef(s_vlink);
|
|
text->w3doc->lFlags |= W3DOC_FLAG_COLOR_VLINK;
|
|
}
|
|
|
|
#ifdef UNIX
|
|
text->w3doc->wbg_color = text->w3doc->color_bgcolor.pixel;
|
|
/*
|
|
* Now get the shadow colors
|
|
*/
|
|
XmGetColors(theScreen, systemCmap, text->w3doc->wbg_color,
|
|
&text->w3doc->wfg_color, &text->w3doc->wts_color,
|
|
&text->w3doc->wbs_color, &text->w3doc->sel_fg_color);
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
void HText_basefont(HText *text, const char *s_size)
|
|
{
|
|
int mySize = 0;
|
|
|
|
if (s_size)
|
|
{
|
|
mySize = atoi(s_size);
|
|
if (mySize < 1)
|
|
{
|
|
mySize = 1;
|
|
}
|
|
else if (mySize > 7)
|
|
{
|
|
mySize = 7;
|
|
}
|
|
text->basefont_size = mySize;
|
|
text->bOpen = FALSE;
|
|
}
|
|
}
|
|
|
|
void HText_beginFont(HText * text, const char *s_color, const char *s_face, const char *s_size)
|
|
{
|
|
/*
|
|
For now, we ignore the FACE attribute, though we parse it out and bring
|
|
it this far.
|
|
*/
|
|
if (text->numOpenFontTags < MAX_NEST_FONT_TAGS)
|
|
{
|
|
text->myOpenFontTags[text->numOpenFontTags].flags = 0;
|
|
if (s_color)
|
|
{
|
|
text->myOpenFontTags[text->numOpenFontTags].flags |= HTEXT_OPENFONTTAG_COLOR;
|
|
text->myOpenFontTags[text->numOpenFontTags].myColor = ParseColorRef(s_color);
|
|
}
|
|
else if (text->numOpenFontTags > 0)
|
|
{
|
|
if (text->myOpenFontTags[text->numOpenFontTags - 1].flags & HTEXT_OPENFONTTAG_COLOR)
|
|
{
|
|
text->myOpenFontTags[text->numOpenFontTags].flags |= HTEXT_OPENFONTTAG_COLOR;
|
|
text->myOpenFontTags[text->numOpenFontTags].myColor = text->myOpenFontTags[text->numOpenFontTags - 1].myColor;
|
|
}
|
|
}
|
|
if (s_size)
|
|
{
|
|
int mySize;
|
|
|
|
text->myOpenFontTags[text->numOpenFontTags].flags |= HTEXT_OPENFONTTAG_SIZE;
|
|
mySize = atoi(s_size);
|
|
|
|
if (strchr(s_size, '-'))
|
|
{
|
|
/*
|
|
The size is a negative offset
|
|
*/
|
|
|
|
mySize += text->basefont_size;
|
|
}
|
|
else if (strchr(s_size, '+'))
|
|
{
|
|
/*
|
|
The size is a negative offset
|
|
*/
|
|
mySize += text->basefont_size;
|
|
}
|
|
|
|
if (mySize < 1)
|
|
{
|
|
mySize = 1;
|
|
}
|
|
if (mySize > 7)
|
|
{
|
|
mySize = 7;
|
|
}
|
|
text->myOpenFontTags[text->numOpenFontTags].mySize = mySize;
|
|
}
|
|
else if (text->numOpenFontTags > 0)
|
|
{
|
|
if (text->myOpenFontTags[text->numOpenFontTags - 1].flags & HTEXT_OPENFONTTAG_SIZE)
|
|
{
|
|
text->myOpenFontTags[text->numOpenFontTags].flags |= HTEXT_OPENFONTTAG_SIZE;
|
|
text->myOpenFontTags[text->numOpenFontTags].mySize = text->myOpenFontTags[text->numOpenFontTags - 1].mySize;
|
|
}
|
|
}
|
|
text->numOpenFontTags++;
|
|
text->bOpen = FALSE;
|
|
}
|
|
}
|
|
|
|
void HText_endFont(HText * text)
|
|
{
|
|
if (text->numOpenFontTags > 0)
|
|
{
|
|
text->numOpenFontTags--;
|
|
text->bOpen = FALSE;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************
|
|
*****************************************************************
|
|
** HTML (proposed) Table Support
|
|
**
|
|
** Currently there are 4 versions of html tables in the world
|
|
** (and no official standard):
|
|
**
|
|
** the proposed HTML 3.0 draft (Ragget 3/28/95)
|
|
** the HTML Tables IETF draft (Ragget 7/7/95)
|
|
** Netscape's documentation
|
|
** NCSA's documentation
|
|
**
|
|
** We have chosen.... TODO spec this.
|
|
**
|
|
*****************************************************************
|
|
*****************************************************************/
|
|
|
|
#ifdef FEATURE_TABLES
|
|
|
|
static BOOL x_is_edible(struct _www * pdoc, int k)
|
|
{
|
|
/* return TRUE if this element could be nuked */
|
|
|
|
if ( (pdoc->aElements[k].type == ELE_TEXT)
|
|
&& (pdoc->aElements[k].textLen == 1)
|
|
&& ((pdoc->pool.f->IsSpace)(&pdoc->pool,pdoc->aElements[k].textOffset)))
|
|
return TRUE;
|
|
|
|
if (pdoc->aElements[k].type == ELE_NEWLINE)
|
|
return TRUE;
|
|
|
|
if (pdoc->aElements[k].type == ELE_VERTICALTAB)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void x_eat_whitespace(struct _www * pdoc, int iCell, BOOL bBefore, BOOL bAfter)
|
|
{
|
|
/* eat (html token-separating) whitespace immediately around cell. */
|
|
|
|
int k;
|
|
|
|
if (bAfter)
|
|
{
|
|
k = pdoc->aElements[iCell].next;
|
|
while ((k>0) && (x_is_edible(pdoc,k)))
|
|
{
|
|
XX_DMsg(DBG_TABLES,("x_eat_whitespace: eating [i %d][type %d][after %d][next %d]\n",
|
|
k,pdoc->aElements[k].type,iCell,pdoc->aElements[k].next));
|
|
|
|
k = pdoc->aElements[k].next;
|
|
}
|
|
|
|
pdoc->aElements[iCell].next = k;
|
|
pdoc->aElements[k].prev = iCell;
|
|
}
|
|
|
|
if (bBefore)
|
|
{
|
|
k = pdoc->aElements[iCell].prev;
|
|
while ((k>0) && (x_is_edible(pdoc,k)))
|
|
{
|
|
XX_DMsg(DBG_TABLES,("x_eat_whitespace: eating [i %d][type %d][before %d][prev %d]\n",
|
|
k,pdoc->aElements[k].type,iCell,pdoc->aElements[k].prev));
|
|
|
|
k = pdoc->aElements[k].prev;
|
|
}
|
|
|
|
pdoc->aElements[iCell].prev = k;
|
|
pdoc->aElements[k].next = iCell;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void x_end_current_cell(HText *text, int eBegin)
|
|
{
|
|
int eEnd, eBeginTable;
|
|
unsigned char eBeginTableType;
|
|
struct _tabledata * td; /* note see pel warning */
|
|
|
|
XX_DMsg(DBG_TABLES,("Table: implicit end (header, data, or caption) cell\n"));
|
|
|
|
/* end any implicit alignment and font style that we began */
|
|
|
|
text->fontBits = text->w3doc->aElements[eBegin].portion.c.prev_fontBits;
|
|
if (text->w3doc->aElements[eBegin].portion.c.align == ALIGN_CENTER)
|
|
HText_endCenter(text);
|
|
else if (text->w3doc->aElements[eBegin].portion.c.align == ALIGN_RIGHT)
|
|
HText_endRight(text);
|
|
|
|
#ifdef _GIBRALTAR
|
|
//
|
|
// Some HTML pages end a cell while still in an anchor (e.g. gw2k).
|
|
// This is another case where netscape just doesn't complain, even
|
|
// though it's bad HTML
|
|
//
|
|
HText_endAnchor(text);
|
|
#endif
|
|
|
|
/* note: we cannot update the placeholders in k[xy][01] until the first reformat. */
|
|
/* add the end-cell marker and sync up with the begin-cell */
|
|
|
|
HText_add_element(text, ELE_ENDCELL);
|
|
eEnd = text->iElement;
|
|
text->bNewPara = TRUE;
|
|
|
|
text->w3doc->aElements[eBegin].portion.c.endcell_element = eEnd;
|
|
x_remove_end_cell(&text->w3doc->cellStack);
|
|
|
|
/* squeeze out stray white space around the cell */
|
|
|
|
x_eat_whitespace(text->w3doc,eBegin,TRUE,TRUE);
|
|
x_eat_whitespace(text->w3doc,eEnd,TRUE,FALSE);
|
|
|
|
/* get beginning of table element */
|
|
|
|
eBeginTable = x_check_container(text,&eBeginTableType);
|
|
if (eBeginTableType != ELE_BEGINTABLE)
|
|
return;
|
|
td = &text->w3doc->tabledatavector.aVector[text->w3doc->aElements[eBeginTable].portion.t.tabledata_index];
|
|
|
|
/* allow BORDERSTYLE on table to override BORDER on cell */
|
|
|
|
if (text->w3doc->aElements[eBegin].iBorder)
|
|
{
|
|
switch (td->borderstyle)
|
|
{
|
|
case BORDERSTYLE_NONEMPTY:
|
|
/* specified, but empty cells do not get a border when BORDERSTYLE==NONEMPTY */
|
|
|
|
if (text->w3doc->aElements[eBegin].next == eEnd)
|
|
text->w3doc->aElements[eBegin].iBorder = 0;
|
|
break;
|
|
|
|
case BORDERSTYLE_FRAME:
|
|
case BORDERSTYLE_NONE:
|
|
text->w3doc->aElements[eBegin].iBorder = 0;
|
|
break;
|
|
|
|
case BORDERSTYLE_ALL:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* see if we were in a caption cell and need to do
|
|
* some additional housekeeping. the end-caption is
|
|
* an implicit end-of-row marker.
|
|
*/
|
|
|
|
if (eBegin != text->w3doc->aElements[eBeginTable].portion.t.begincaption_element)
|
|
return;
|
|
|
|
HText_beginTR(text,NULL,NULL,NULL);
|
|
td->end_of_row_status = EOR_STATUS_IMPLICIT;
|
|
|
|
return;
|
|
}
|
|
|
|
static int x_begin_cell(HText * text, int eBeginTable)
|
|
{
|
|
int eCell;
|
|
struct _tabledata * td;
|
|
|
|
/* first <tr> in a doc is often omitted (and Raggett's paper suggests we should allow for it) */
|
|
|
|
td = &text->w3doc->tabledatavector.aVector[text->w3doc->aElements[eBeginTable].portion.t.tabledata_index];
|
|
if ( (td->end_of_row_status == EOR_STATUS_NONE_SEEN)
|
|
|| (td->end_of_row_status == EOR_STATUS_IMPLICIT))
|
|
HText_beginTR(text,NULL,NULL,NULL);
|
|
|
|
HText_add_element(text, ELE_BEGINCELL);
|
|
eCell = text->iElement;
|
|
text->bNewPara = TRUE;
|
|
x_append_cell(&text->w3doc->cellStack,eCell);
|
|
text->w3doc->aElements[eCell].portion.c.begintable_element = eBeginTable;
|
|
|
|
return eCell;
|
|
}
|
|
|
|
static unsigned char x_convert_valign_keyword(const char * valign,
|
|
unsigned char row_default,
|
|
unsigned char cell_default)
|
|
{
|
|
if (valign)
|
|
{
|
|
if (GTR_strcmpi((char *) valign, "top") == 0)
|
|
return ALIGN_TOP;
|
|
|
|
if (GTR_strcmpi((char *) valign, "middle") == 0)
|
|
return ALIGN_MIDDLE;
|
|
|
|
if (GTR_strcmpi((char *) valign, "center") == 0) /* non-standard, but a frequent mistake */
|
|
return ALIGN_MIDDLE;
|
|
|
|
if (GTR_strcmpi((char *) valign, "bottom") == 0)
|
|
return ALIGN_BOTTOM;
|
|
}
|
|
|
|
if (row_default != ALIGN_UNSPECIFIED)
|
|
return row_default;
|
|
|
|
return cell_default;
|
|
}
|
|
|
|
static unsigned char x_convert_align_keyword(const char * align,
|
|
unsigned char row_default,
|
|
unsigned char cell_default)
|
|
{
|
|
if (align)
|
|
{
|
|
if (GTR_strcmpi((char *) align, "left") == 0)
|
|
return ALIGN_LEFT;
|
|
|
|
if (GTR_strcmpi((char *) align, "right") == 0)
|
|
return ALIGN_RIGHT;
|
|
|
|
if (GTR_strcmpi((char *) align, "center") == 0)
|
|
return ALIGN_CENTER;
|
|
|
|
if (GTR_strcmpi((char *) align, "middle") == 0) /* non-standard, but a frequent mistake */
|
|
return ALIGN_CENTER;
|
|
}
|
|
|
|
if (row_default != ALIGN_UNSPECIFIED)
|
|
return row_default;
|
|
|
|
return cell_default;
|
|
}
|
|
|
|
static BOOL x_IsANumber(const char * p)
|
|
{
|
|
while (*p)
|
|
{
|
|
if ((*p<'0')||(*p>'9'))
|
|
return FALSE;
|
|
p++;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef FEATURE_TABLE_WIDTH
|
|
static int x_ParseWidthAttribute(const char * szWidth)
|
|
{
|
|
/* parse the value given with the WIDTH attribute
|
|
* on TABLE, TD, and TH tags.
|
|
*
|
|
* WIDTH=10
|
|
* WIDTH=10%
|
|
*
|
|
* We return:
|
|
* >0 when pixel width given
|
|
* =0 on zero or bogus input (caller will ignore width specifier)
|
|
* <0 when percentage given
|
|
*
|
|
*/
|
|
|
|
char buf[20];
|
|
char * p;
|
|
int k;
|
|
|
|
if (!szWidth || !*szWidth)
|
|
return 0;
|
|
|
|
memset(buf,0,20);
|
|
GTR_strncpy(buf,szWidth,19);
|
|
|
|
p = strchr(buf,'%');
|
|
if (p)
|
|
*p = 0;
|
|
|
|
if (!x_IsANumber(buf))
|
|
return 0;
|
|
|
|
k = atol(buf);
|
|
if (p)
|
|
k = -k;
|
|
|
|
return k;
|
|
}
|
|
#endif /* FEATURE_TABLE_WIDTH */
|
|
|
|
#endif /* FEATURE_TABLES */
|
|
|
|
|
|
void HText_beginTable(HText *text,
|
|
const char *align,
|
|
const char *border,
|
|
const char *borderstyle,
|
|
const char *cellpadding,
|
|
const char *cellspacing,
|
|
const char *nowrap,
|
|
const char *width)
|
|
{
|
|
#ifdef FEATURE_TABLES
|
|
|
|
int eBegin;
|
|
struct _tabledata * td;
|
|
|
|
/* Put a begin-table marker in the display list and remember its
|
|
* position so we can sync up the end-table marker.
|
|
*/
|
|
|
|
HText_add_element(text, ELE_BEGINTABLE);
|
|
eBegin = text->iElement;
|
|
text->bNewPara = TRUE;
|
|
x_append_cell(&text->w3doc->cellStack,eBegin);
|
|
|
|
XX_DMsg(DBG_TABLES,
|
|
("Table: begin [i %d][align=%s][border=%s][borderstyle=%s][cellpadding=%s][cellspacing=%s][nowrap=%s][width=%s]\n",
|
|
eBegin,
|
|
((align)?align:""),((border)?border:""),((borderstyle)?borderstyle:""),
|
|
((cellpadding)?cellpadding:""),
|
|
((cellspacing)?cellspacing:""),((nowrap)?nowrap:""),((width)?width:"")));
|
|
|
|
|
|
/* create some permanent storage for the table */
|
|
|
|
text->w3doc->aElements[eBegin].portion.t.tabledata_index = x_extend_tabledata(&text->w3doc->tabledatavector);
|
|
|
|
td = &text->w3doc->tabledatavector.aVector[text->w3doc->aElements[eBegin].portion.t.tabledata_index];
|
|
td->current_y = -1;
|
|
|
|
td->align = ALIGN_UNSPECIFIED;
|
|
td->valign = ALIGN_UNSPECIFIED;
|
|
|
|
/* remember whether border was set. current standards conflict
|
|
* regarding possible value for BORDER attribute. for now, if
|
|
* present (and regardless of the presence of any optional value)
|
|
* we enable borders for this table. we do support the popular
|
|
* special case of BORDER=0, which indicates no border.
|
|
*
|
|
* CURRENTLY WE USE pel->iBorder AS A BOOLEAN INDICATING WHETHER
|
|
* OR NOT A 1 PIXEL BORDER SHOULD BE DRAWN. IN A FUTURE VERSION
|
|
* THIS USAGE MAY CHANGE.
|
|
*/
|
|
|
|
if (border)
|
|
if (*border && x_IsANumber(border) && (atoi(border)==0))
|
|
text->w3doc->aElements[eBegin].iBorder = 0;
|
|
else
|
|
text->w3doc->aElements[eBegin].iBorder = 1;
|
|
else
|
|
text->w3doc->aElements[eBegin].iBorder = 0;
|
|
|
|
/* BORDERSTYLE is our little enhancement and is in anticipation of
|
|
* the next version of the HTML table spec. it describes the type
|
|
* of borders desired without conflicting with the BORDER=thickness
|
|
* usage. we support the following values:
|
|
* ALL -- all cells (even empty ones).
|
|
* NONEMPTY -- cell borders on non-empty cells only. this is the
|
|
* default and current practice.
|
|
* FRAME -- box the table, but none of the cells.
|
|
* NONE -- no borders. equivalent to not specifying BORDER at all.
|
|
* may in the future affect formatting (if a border thickness is
|
|
* specified).
|
|
*/
|
|
td->borderstyle = BORDERSTYLE_NONEMPTY;
|
|
if (borderstyle)
|
|
{
|
|
if (GTR_strcmpi(borderstyle,"all")==0)
|
|
td->borderstyle = BORDERSTYLE_ALL;
|
|
else if (GTR_strcmpi(borderstyle,"frame")==0)
|
|
td->borderstyle = BORDERSTYLE_FRAME;
|
|
else if (GTR_strcmpi(borderstyle,"none")==0)
|
|
{
|
|
td->borderstyle = BORDERSTYLE_NONE;
|
|
text->w3doc->aElements[eBegin].iBorder = 0; /* cancel border flag */
|
|
}
|
|
/* else BORDERSTYLE_NONEMPTY is the default */
|
|
}
|
|
|
|
#ifdef FEATURE_TABLE_WIDTH
|
|
td->given_table_width = x_ParseWidthAttribute(width);
|
|
#endif /* FEATURE_TABLE_WIDTH */
|
|
|
|
#endif /* FEATURE_TABLES */
|
|
}
|
|
|
|
void HText_beginCaption(HText *text,
|
|
const char *align)
|
|
{
|
|
#ifdef FEATURE_TABLES
|
|
|
|
int eBegin;
|
|
int eCell;
|
|
unsigned char eBeginType;
|
|
struct _cellvector * pv;
|
|
struct _tabledata * td;
|
|
BOOL bBottom;
|
|
BOOL bAtBeginningOfTableBody;
|
|
|
|
XX_DMsg(DBG_TABLES,
|
|
("Table: caption [align=%s]\n",
|
|
((align)?align:"")));
|
|
|
|
/* check current open item */
|
|
|
|
eBegin = x_check_container(text,&eBeginType);
|
|
switch (eBeginType)
|
|
{
|
|
case ELE_BEGINTABLE:
|
|
break; /* ok */
|
|
|
|
case ELE_BEGINCELL:
|
|
XX_DMsg(DBG_TABLES,("Tables: Caption not first in table.\n"));
|
|
x_end_current_cell(text,eBegin);
|
|
eBegin = x_check_container(text,&eBeginType);
|
|
XX_Assert((eBeginType==ELE_BEGINTABLE),("Tables: improper cell nesting."));
|
|
break;
|
|
|
|
default: /* not in a table or corrupt stack */
|
|
return;
|
|
}
|
|
|
|
/* a caption can be treated as a special 'row' that
|
|
* has some fixed attributes:
|
|
*
|
|
* [1] it does not have a border.
|
|
* [2] it is horizontally centered.
|
|
* [3] it wraps to fit the table.
|
|
* [5] it is a single column that spans the whole table.
|
|
* [6] it is a special (first or last) row.
|
|
*/
|
|
|
|
td = &text->w3doc->tabledatavector.aVector[text->w3doc->aElements[eBegin].portion.t.tabledata_index];
|
|
|
|
/* the html spec states that the caption, if present, must be
|
|
* the first form within the table-body -- prior to any rows.
|
|
* if they failed to put the caption at the beginning, we
|
|
* force bottom alignment, because we already have data
|
|
* structures in the construction process and we can slip
|
|
* it in. in the future we may want to relax this restriction
|
|
* and go back and adjust down all of our table cells.
|
|
*/
|
|
bAtBeginningOfTableBody = (td->current_y == -1);
|
|
bBottom = ( (align && !GTR_strcmpi((char *) align, "bottom"))
|
|
|| ( ! bAtBeginningOfTableBody));
|
|
|
|
/* we need to force a row-break here. for a bottom caption, we
|
|
* don't want allocate a space in the table yet.
|
|
*/
|
|
|
|
if (bBottom)
|
|
td->end_of_row_status = EOR_STATUS_IMPLICIT;
|
|
|
|
HText_beginTR(text,"center",NULL,NULL); /* [6],[2] */
|
|
|
|
eCell = x_begin_cell(text,eBegin);
|
|
|
|
text->w3doc->aElements[eCell].iBorder = 0; /* [1] */
|
|
|
|
/* [5] set column references to span whole table. use -1 as
|
|
* flag in kx1 which will be fixed-up later when we know
|
|
* how many columns are in the table. when a top-caption,
|
|
* set a single -1 in row_spans.
|
|
*/
|
|
if (!bBottom)
|
|
{
|
|
td = &text->w3doc->tabledatavector.aVector[text->w3doc->aElements[eBegin].portion.t.tabledata_index];
|
|
pv = &td->row_spans;
|
|
x_empty_vector(pv);
|
|
x_append_cell(pv,-1);
|
|
}
|
|
|
|
if (text->w3doc->aElements[eBegin].portion.t.begincaption_element)
|
|
{
|
|
/* multiple captions -- this is not allowed in the html spec
|
|
* convert to a regular TD and go on.
|
|
*/
|
|
text->w3doc->aElements[eCell].portion.c.kx0 = 0;
|
|
text->w3doc->aElements[eCell].portion.c.kx1 = 1;
|
|
text->w3doc->aElements[eCell].portion.c.ky0 = td->current_y;
|
|
text->w3doc->aElements[eCell].portion.c.ky1 = td->current_y+1;
|
|
}
|
|
else
|
|
{
|
|
text->w3doc->aElements[eCell].portion.c.kx0 = 0; /* [5] */
|
|
text->w3doc->aElements[eCell].portion.c.kx1 = -1; /* [5] */
|
|
|
|
text->w3doc->aElements[eBegin].portion.t.begincaption_element = eCell;
|
|
|
|
if (bBottom) /* align=bottom */
|
|
{
|
|
text->w3doc->aElements[eCell].portion.c.ky0 = -1; /* [6] -1 is placeholder for last row */
|
|
text->w3doc->aElements[eCell].portion.c.ky1 = -2; /* [6] -2 is placeholder for last row + 1 */
|
|
}
|
|
else /* default is "top" */
|
|
{
|
|
text->w3doc->aElements[eCell].portion.c.ky0 = td->current_y; /* [6] */
|
|
text->w3doc->aElements[eCell].portion.c.ky1 = td->current_y+1; /* [6] */
|
|
}
|
|
|
|
/* by default a CAPTION is horizontally CENTERED. font style
|
|
* of CAPTION is not yet in specs, so we set it to bold.
|
|
*
|
|
* we save the current font style before we change it
|
|
* (and restore it on end-cell) -- this allows our implicit
|
|
* font change (and any font changes within the cell) to be
|
|
* transparent to the rest of the document.
|
|
*/
|
|
|
|
text->w3doc->aElements[eCell].portion.c.prev_fontBits = text->fontBits;
|
|
text->fontBits = FONTBIT_BOLD;
|
|
|
|
text->w3doc->aElements[eCell].portion.c.valign = ALIGN_UNSPECIFIED;
|
|
text->w3doc->aElements[eCell].portion.c.align =
|
|
x_convert_align_keyword(align,td->align,ALIGN_CENTER);
|
|
|
|
if (text->w3doc->aElements[eCell].portion.c.align == ALIGN_CENTER)
|
|
HText_beginCenter(text);
|
|
else if (text->w3doc->aElements[eCell].portion.c.align == ALIGN_RIGHT)
|
|
HText_beginRight(text);
|
|
}
|
|
|
|
/* [3] TODO see if html.c should handle nowrap */
|
|
|
|
#endif /* FEATURE_TABLES */
|
|
}
|
|
|
|
void HText_beginTR(HText *text,
|
|
const char *align,
|
|
const char *nowrap,
|
|
const char *valign)
|
|
{
|
|
#ifdef FEATURE_TABLES
|
|
|
|
int eBegin;
|
|
unsigned char eBeginType;
|
|
struct _cellvector * pv;
|
|
struct _tabledata * td;
|
|
int k;
|
|
|
|
XX_DMsg(DBG_TABLES,
|
|
("Table: begin row [align=%s][nowrap=%s][valign=%s]\n",
|
|
((align)?align:""),((nowrap)?nowrap:""),((valign)?valign:"")));
|
|
|
|
/* check current open item */
|
|
|
|
eBegin = x_check_container(text,&eBeginType);
|
|
switch (eBeginType)
|
|
{
|
|
case ELE_BEGINTABLE:
|
|
break; /* ok */
|
|
|
|
case ELE_BEGINCELL: /* implicit end of cell */
|
|
x_end_current_cell(text,eBegin);
|
|
eBegin = x_check_container(text,&eBeginType);
|
|
XX_Assert((eBeginType==ELE_BEGINTABLE),("Tables: improper cell nesting."));
|
|
break;
|
|
|
|
default: /* not in a table or corrupt stack */
|
|
return;
|
|
}
|
|
|
|
td = &text->w3doc->tabledatavector.aVector[text->w3doc->aElements[eBegin].portion.t.tabledata_index];
|
|
|
|
td->current_x = 0;
|
|
|
|
if (td->end_of_row_status == EOR_STATUS_IMPLICIT)
|
|
{
|
|
/* last row-break was implicitly generated.
|
|
* we eat this row-break and assign the
|
|
* given attributes to the implicit one.
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
/* add an actual row-break */
|
|
|
|
td->current_y++;
|
|
|
|
pv = &td->row_spans;
|
|
if ( (td->current_y > 0) /* if there was a previous row */
|
|
&& (!x_is_empty_vector(pv)) /* and it had columns */
|
|
&& (pv->aVector[0] != -1)) /* and it didn't span entire width of table, */
|
|
{ /* then decrement row_span counters on each column. */
|
|
for (k=0; k<pv->next; k++)
|
|
if (pv->aVector[k] > 0)
|
|
pv->aVector[k]--;
|
|
}
|
|
}
|
|
td->end_of_row_status = EOR_STATUS_DEFAULT;
|
|
|
|
td->valign = x_convert_valign_keyword(valign,ALIGN_UNSPECIFIED,ALIGN_UNSPECIFIED);
|
|
td->align = x_convert_align_keyword(align,ALIGN_UNSPECIFIED,ALIGN_UNSPECIFIED);
|
|
|
|
#endif /* FEATURE_TABLES */
|
|
}
|
|
|
|
#ifdef FEATURE_TABLES
|
|
static void x_handle_span(struct _tabledata * td,
|
|
const char * colspan, const char * rowspan,
|
|
struct _cell_element_data * ced)
|
|
{
|
|
int r,c,x,v,j;
|
|
struct _cellvector * pv;
|
|
|
|
pv = &td->row_spans;
|
|
r = ((rowspan && *rowspan) ? atoi(rowspan) : 1);
|
|
if (r < 1)
|
|
r = 1;
|
|
c = ((colspan && *colspan) ? atoi(colspan) : 1);
|
|
if (c < 1)
|
|
c = 1;
|
|
|
|
/* for each column in the span we need to claim a space for it.
|
|
* but first, we need to identify its starting point.
|
|
*/
|
|
|
|
x = td->current_x;
|
|
while ((x_get_kth_cell(pv,x,&v)) && (v > 0))
|
|
x++;
|
|
|
|
if (!x_get_kth_cell(pv,x,&v))
|
|
{
|
|
/* if this column is not in the row-span vector, then we can begin
|
|
* the column here and claim the requested col-span.
|
|
*/
|
|
for (j=x; (j<x+c); j++)
|
|
x_set_kth_cell(pv,j,r);
|
|
}
|
|
else
|
|
{
|
|
/* the current column is defined in the row-span vector, but is not
|
|
* being spanned from above. we can start the cell in the here.
|
|
* as we claim additional columns we must check for overlap.
|
|
* overlapping cells are invalid html (in the current draft spec),
|
|
* so if we do detect an overlap, we are allowed to do what we
|
|
* please -- i've chosen to truncate the col-span on this item.
|
|
*/
|
|
|
|
x_set_kth_cell(pv,x,r);
|
|
for (j=x+1; (j<x+c); j++)
|
|
{
|
|
if ((x_get_kth_cell(pv,j,&v)) && (v > 0))
|
|
{
|
|
XX_DMsg(DBG_TABLES,("Table: -- Cell overlap\n"));
|
|
break;
|
|
}
|
|
x_set_kth_cell(pv,j,r);
|
|
}
|
|
}
|
|
|
|
td->current_x = j;
|
|
|
|
ced->kx0 = x;
|
|
ced->kx1 = j;
|
|
|
|
ced->ky0 = td->current_y;
|
|
ced->ky1 = td->current_y + r;
|
|
|
|
return;
|
|
}
|
|
#endif /* FEATURE_TABLES */
|
|
|
|
void HText_beginTH(HText *text,
|
|
const char *align,
|
|
const char *colspan,
|
|
const char *nowrap,
|
|
const char *rowspan,
|
|
const char *valign,
|
|
const char *width)
|
|
{
|
|
#ifdef FEATURE_TABLES
|
|
|
|
int eBegin;
|
|
int eCell;
|
|
unsigned char eBeginType;
|
|
struct _tabledata * td;
|
|
|
|
XX_DMsg(DBG_TABLES,
|
|
("Table: begin header [align=%s][colspan=%s][nowrap=%s][rowspan=%s][valign=%s][width=%s]\n",
|
|
((align)?align:""),((colspan)?colspan:""),((nowrap)?nowrap:""),
|
|
((rowspan)?rowspan:""),((valign)?valign:""),((width)?width:"")));
|
|
|
|
/* check current open item */
|
|
|
|
eBegin = x_check_container(text,&eBeginType);
|
|
switch (eBeginType)
|
|
{
|
|
case ELE_BEGINTABLE:
|
|
break; /* ok */
|
|
|
|
case ELE_BEGINCELL: /* implicit end of cell */
|
|
x_end_current_cell(text,eBegin);
|
|
eBegin = x_check_container(text,&eBeginType);
|
|
XX_Assert((eBeginType==ELE_BEGINTABLE),("Tables: improper cell nesting."));
|
|
break;
|
|
|
|
default: /* not in a table or corrupt stack */
|
|
return;
|
|
}
|
|
|
|
/* push a new cell */
|
|
|
|
eCell = x_begin_cell(text,eBegin);
|
|
|
|
/* copy down border attribute from table */
|
|
|
|
text->w3doc->aElements[eCell].iBorder = text->w3doc->aElements[eBegin].iBorder;
|
|
|
|
/* record row- and column-spanning information and cell coordinates */
|
|
|
|
td = &text->w3doc->tabledatavector.aVector[text->w3doc->aElements[eBegin].portion.t.tabledata_index];
|
|
|
|
x_handle_span(td,
|
|
colspan,rowspan,
|
|
&text->w3doc->aElements[eCell].portion.c);
|
|
|
|
XX_DMsg(DBG_TABLES,
|
|
("Cell: TH [i %d][kx %d %d][ky %d %d]\n",
|
|
eCell,
|
|
text->w3doc->aElements[eCell].portion.c.kx0,
|
|
text->w3doc->aElements[eCell].portion.c.kx1,
|
|
text->w3doc->aElements[eCell].portion.c.ky0,
|
|
text->w3doc->aElements[eCell].portion.c.ky1));
|
|
|
|
#ifdef FEATURE_TABLE_WIDTH
|
|
/* a width attribute was given with this cell. we update
|
|
* the width-info for the column we are starting in. we
|
|
* ignore the fact that we may be spanning multiple columns
|
|
* (how should we divi it up). we ignore the fact that
|
|
* earlier cells may have also set a width for this column
|
|
* (which is right).
|
|
*/
|
|
if (width)
|
|
{
|
|
int xWidthRequested = x_ParseWidthAttribute(width);
|
|
#if 1 /* TODO */
|
|
if (xWidthRequested < 0)
|
|
xWidthRequested = 0;
|
|
#endif /* TODO */
|
|
x_set_kth_cell(&td->x_given_widths,
|
|
text->w3doc->aElements[eCell].portion.c.kx0,
|
|
xWidthRequested);
|
|
}
|
|
#endif /* FEATURE_TABLE_WIDTH */
|
|
|
|
/* by default TH cells are BOLD, horizontally CENTERED and
|
|
* vertically at the TOP. we save the current font style
|
|
* before we force BOLD (and restore it on end-cell) -- this
|
|
* allows our implicit font change (and any font changes
|
|
* within the cell) to be transparent to the rest of the
|
|
* document.
|
|
*/
|
|
|
|
text->w3doc->aElements[eCell].portion.c.prev_fontBits = text->fontBits;
|
|
text->fontBits = FONTBIT_BOLD;
|
|
|
|
text->w3doc->aElements[eCell].portion.c.valign =
|
|
x_convert_valign_keyword(valign,td->valign,ALIGN_MIDDLE);
|
|
text->w3doc->aElements[eCell].portion.c.align =
|
|
x_convert_align_keyword(align,td->align,ALIGN_CENTER);
|
|
|
|
if (text->w3doc->aElements[eCell].portion.c.align == ALIGN_CENTER)
|
|
HText_beginCenter(text);
|
|
else if (text->w3doc->aElements[eCell].portion.c.align == ALIGN_RIGHT)
|
|
HText_beginRight(text);
|
|
|
|
/* TODO handle nowrap */
|
|
|
|
#endif /* FEATURE_TABLES */
|
|
}
|
|
|
|
void HText_beginTD(HText *text,
|
|
const char *align,
|
|
const char *colspan,
|
|
const char *nowrap,
|
|
const char *rowspan,
|
|
const char *valign,
|
|
const char *width)
|
|
{
|
|
#ifdef FEATURE_TABLES
|
|
|
|
int eBegin;
|
|
int eCell;
|
|
unsigned char eBeginType;
|
|
struct _tabledata * td;
|
|
|
|
XX_DMsg(DBG_TABLES,
|
|
("Table: begin data [align=%s][colspan=%s][nowrap=%s][rowspan=%s][valign=%s][width=%s]\n",
|
|
((align)?align:""),((colspan)?colspan:""),((nowrap)?nowrap:""),
|
|
((rowspan)?rowspan:""),((valign)?valign:""),((width)?width:"")));
|
|
|
|
/* check current open item */
|
|
|
|
eBegin = x_check_container(text,&eBeginType);
|
|
switch (eBeginType)
|
|
{
|
|
case ELE_BEGINTABLE:
|
|
break; /* ok */
|
|
|
|
case ELE_BEGINCELL: /* implicit end of cell */
|
|
x_end_current_cell(text,eBegin);
|
|
eBegin = x_check_container(text,&eBeginType);
|
|
XX_Assert((eBeginType==ELE_BEGINTABLE),("Tables: improper cell nesting."));
|
|
break;
|
|
|
|
default: /* not in a table or corrupt stack */
|
|
return;
|
|
}
|
|
|
|
/* push a new cell */
|
|
|
|
eCell = x_begin_cell(text,eBegin);
|
|
|
|
/* copy down border attribute from table */
|
|
|
|
text->w3doc->aElements[eCell].iBorder = text->w3doc->aElements[eBegin].iBorder;
|
|
|
|
/* record row- and column-spanning information and cell coordinates */
|
|
|
|
td = &text->w3doc->tabledatavector.aVector[text->w3doc->aElements[eBegin].portion.t.tabledata_index];
|
|
|
|
x_handle_span(td,
|
|
colspan,rowspan,
|
|
&text->w3doc->aElements[eCell].portion.c);
|
|
|
|
XX_DMsg(DBG_TABLES,
|
|
("Cell: TD [i %d][kx %d %d][ky %d %d]\n",
|
|
eCell,
|
|
text->w3doc->aElements[eCell].portion.c.kx0,
|
|
text->w3doc->aElements[eCell].portion.c.kx1,
|
|
text->w3doc->aElements[eCell].portion.c.ky0,
|
|
text->w3doc->aElements[eCell].portion.c.ky1));
|
|
|
|
#ifdef FEATURE_TABLE_WIDTH
|
|
/* a width attribute was given with this cell. we update
|
|
* the width-info for the column we are starting in. we
|
|
* ignore the fact that we may be spanning multiple columns
|
|
* (how should we divi it up). we ignore the fact that
|
|
* earlier cells may have also set a width for this column
|
|
* (which is right).
|
|
*/
|
|
if (width)
|
|
{
|
|
int xWidthRequested = x_ParseWidthAttribute(width);
|
|
#if 1 /* TODO */
|
|
if (xWidthRequested < 0)
|
|
xWidthRequested = 0;
|
|
#endif /* TODO */
|
|
x_set_kth_cell(&td->x_given_widths,
|
|
text->w3doc->aElements[eCell].portion.c.kx0,
|
|
xWidthRequested);
|
|
}
|
|
#endif /* FEATURE_TABLE_WIDTH */
|
|
|
|
/* by default TD cells are REGULAR, horizontally LEFT and
|
|
* vertically at the TOP. we save the current font style
|
|
* before we force it REGULAR (and restore it on end-cell) -- this
|
|
* allows our implicit font change (and any font changes
|
|
* within the cell) to be transparent to the rest of the
|
|
* document.
|
|
*/
|
|
|
|
text->w3doc->aElements[eCell].portion.c.prev_fontBits = text->fontBits;
|
|
text->fontBits = FONTBIT_REGULAR;
|
|
|
|
text->w3doc->aElements[eCell].portion.c.valign =
|
|
x_convert_valign_keyword(valign,td->valign,ALIGN_MIDDLE);
|
|
text->w3doc->aElements[eCell].portion.c.align =
|
|
x_convert_align_keyword(align,td->align,ALIGN_LEFT);
|
|
|
|
if (text->w3doc->aElements[eCell].portion.c.align == ALIGN_CENTER)
|
|
HText_beginCenter(text);
|
|
else if (text->w3doc->aElements[eCell].portion.c.align == ALIGN_RIGHT)
|
|
HText_beginRight(text);
|
|
|
|
#endif /* FEATURE_TABLES */
|
|
}
|
|
|
|
void HText_endTR(HText *text)
|
|
{
|
|
#ifdef FEATURE_TABLES
|
|
/* The </tr> tag is completely ignored -- we have no need for it.
|
|
*
|
|
* However, some authors insert it and than forget to insert
|
|
* a <tr> to start the next row, rather than just starting
|
|
* each row correctly.
|
|
*
|
|
* The following is an attempt to DWIM -- if we see an end-row,
|
|
* set up for an implicit end-of-row; if the next thing we see
|
|
* is a begin-row, we're already ready for it. sigh.
|
|
*
|
|
*/
|
|
|
|
int eBegin;
|
|
unsigned char eBeginType;
|
|
struct _tabledata * td; /* note see pel warning */
|
|
|
|
XX_DMsg(DBG_TABLES,("Table: end row\n"));
|
|
|
|
/* check current open item */
|
|
|
|
eBegin = x_check_container(text,&eBeginType);
|
|
switch (eBeginType)
|
|
{
|
|
case ELE_BEGINTABLE:
|
|
break; /* ok */
|
|
|
|
case ELE_BEGINCELL: /* implicit end of cell */
|
|
x_end_current_cell(text,eBegin);
|
|
eBegin = x_check_container(text,&eBeginType);
|
|
XX_Assert((eBeginType==ELE_BEGINTABLE),("Tables: improper cell nesting."));
|
|
break;
|
|
|
|
default: /* not in a table or corrupt stack */
|
|
return;
|
|
}
|
|
|
|
td = &text->w3doc->tabledatavector.aVector[text->w3doc->aElements[eBegin].portion.t.tabledata_index];
|
|
HText_beginTR(text,NULL,NULL,NULL);
|
|
td->end_of_row_status = EOR_STATUS_IMPLICIT;
|
|
|
|
return;
|
|
|
|
#endif /* FEATURE_TABLES */
|
|
}
|
|
|
|
void HText_endTable(HText *text)
|
|
{
|
|
#ifdef FEATURE_TABLES
|
|
|
|
int eBegin, eCaption, e;
|
|
unsigned char eBeginType;
|
|
struct _table_element_data * ted; /* because of REALLOC on aElements[], only briefly valid */
|
|
struct _cell_element_data * ced; /* because of REALLOC on aElements[], only briefly valid */
|
|
struct _tabledata * td;
|
|
|
|
XX_DMsg(DBG_TABLES,("Table: end\n"));
|
|
|
|
/* check current open item */
|
|
|
|
eBegin = x_check_container(text,&eBeginType);
|
|
switch (eBeginType)
|
|
{
|
|
case ELE_BEGINTABLE:
|
|
break; /* ok */
|
|
|
|
case ELE_BEGINCELL: /* implicit end of cell */
|
|
x_end_current_cell(text,eBegin);
|
|
eBegin = x_check_container(text,&eBeginType);
|
|
XX_Assert((eBeginType==ELE_BEGINTABLE),("Tables: improper cell nesting."));
|
|
break;
|
|
|
|
default: /* not in a table or corrupt stack */
|
|
return;
|
|
}
|
|
|
|
#ifdef _GIBRALTAR
|
|
//
|
|
// Some HTML pages end a cell while still in an anchor (e.g. gw2k).
|
|
// This is another case where netscape just doesn't complain, even
|
|
// though it's bad HTML
|
|
//
|
|
HText_endAnchor(text);
|
|
#endif
|
|
|
|
/* add the end-table marker and sync up with the begin-table */
|
|
|
|
HText_add_element(text, ELE_ENDTABLE);
|
|
ted = &text->w3doc->aElements[eBegin].portion.t;
|
|
ted->endtable_element = text->iElement;
|
|
text->w3doc->aElements[text->iElement].portion.et.begintable_element = eBegin;
|
|
x_remove_end_cell(&text->w3doc->cellStack);
|
|
text->bNewPara = FALSE;
|
|
|
|
/* clean up any stray whitespace around table */
|
|
|
|
x_eat_whitespace(text->w3doc,eBegin,FALSE,TRUE);
|
|
x_eat_whitespace(text->w3doc,text->iElement,TRUE,FALSE);
|
|
|
|
/* fix size of X,Y coordinate vectors now that we know how many
|
|
* columns and rows there are. also fix-up k[xy][01] on caption.
|
|
*/
|
|
td = &text->w3doc->tabledatavector.aVector[ted->tabledata_index];
|
|
td->total_x = td->row_spans.next;
|
|
x_set_kth_cell(&td->XCellCoords,td->total_x,0);
|
|
x_set_kth_cell(&td->x_smallest_max,td->total_x,0);
|
|
x_set_kth_cell(&td->x_widest_max,td->total_x,0);
|
|
#ifdef FEATURE_TABLE_WIDTH
|
|
x_set_kth_cell(&td->x_given_widths,td->total_x,0);
|
|
x_set_kth_cell(&td->x_constraints,td->total_x,0);
|
|
#endif /* FEATURE_TABLE_WIDTH */
|
|
|
|
/* if the last end-of-row marker was implicitly generated
|
|
* then we didn't actually see the beginning of the next
|
|
* row. therefore, un-account for it.
|
|
*/
|
|
|
|
if (td->end_of_row_status != EOR_STATUS_IMPLICIT)
|
|
td->current_y++;
|
|
|
|
td->total_y = td->current_y;
|
|
|
|
/* the number of rows in the table is given by the number of <tr>'s.
|
|
* clip any over-aggressive row-spans. only clip cells belonging to
|
|
* this table (don't alter cells in nested tables).
|
|
*/
|
|
for (e=eBegin; ((e) && (e!=text->iElement)); e=text->w3doc->aElements[e].next)
|
|
{
|
|
register struct _element * pelE = &text->w3doc->aElements[e];
|
|
if ( (pelE->type==ELE_BEGINCELL)
|
|
&& (pelE->portion.c.begintable_element==eBegin)
|
|
&& (pelE->portion.c.ky1 > td->total_y))
|
|
pelE->portion.c.ky1 = td->total_y;
|
|
}
|
|
|
|
eCaption = ted->begincaption_element;
|
|
if (eCaption)
|
|
{
|
|
ced = &text->w3doc->aElements[eCaption].portion.c;
|
|
ced->kx1 = td->total_x;
|
|
if (ced->ky0 == -1) /* caption had align=bottom */
|
|
{
|
|
ced->ky0 = td->total_y++;
|
|
ced->ky1 = td->total_y;
|
|
|
|
/* TODO: by construction, a bottom aligned caption appears first
|
|
* in the display list but appears below the table on screen.
|
|
* this breaks our selection code which assumes that things
|
|
* display top-left to bottom-right in display list order.
|
|
*/
|
|
|
|
}
|
|
}
|
|
|
|
x_set_kth_cell(&td->YCellCoords,td->total_y,0);
|
|
x_set_kth_cell(&td->y_smallest_max,td->total_y,0);
|
|
|
|
/* upon seeing an ENDTABLE, see if we interrupted a progressive
|
|
* reformat at a BEGINTABLE because the table had not been
|
|
* completely seen. if so, issue a reformat to let the table
|
|
* be progressively drawn. since tables can nest, we only do
|
|
* this when a top-level table is ended (completing an inner
|
|
* table is of no value, since the outer table is not complete).
|
|
*/
|
|
|
|
if ( (text->w3doc->bProgressiveStoppedAtTable)
|
|
&& (x_is_empty_vector(&text->w3doc->cellStack)))
|
|
{
|
|
/* by construction, we are the last element in the document.
|
|
* because of the HACK in TW_Reformat(), it always 'fixes up'
|
|
* the last element to be an ENDDOC and so our reformat would
|
|
* be confused. therefore, we append a VOID element as a
|
|
* place holder.
|
|
*/
|
|
HText_add_element(text,ELE_VOID);
|
|
TW_Reformat(text->tw);
|
|
}
|
|
|
|
#endif /* FEATURE_TABLES */
|
|
}
|
|
|
|
void HText_endCaption(HText *text)
|
|
{
|
|
#ifdef FEATURE_TABLES
|
|
|
|
int eBegin;
|
|
unsigned char eBeginType;
|
|
|
|
XX_DMsg(DBG_TABLES,("Table: end caption\n"));
|
|
|
|
/* verify that we saw a corresponding begin-caption (cell) */
|
|
|
|
eBegin = x_check_container(text,&eBeginType);
|
|
switch (eBeginType)
|
|
{
|
|
case ELE_BEGINTABLE:
|
|
XX_DMsg(DBG_TABLES,("Tables: end-caption token when not in caption.\n"));
|
|
return; /* ignore token */
|
|
|
|
case ELE_BEGINCELL:
|
|
break; /* ok */
|
|
|
|
default: /* not in a table or corrupt stack */
|
|
return;
|
|
}
|
|
|
|
x_end_current_cell(text,eBegin);
|
|
|
|
#endif /* FEATURE_TABLES */
|
|
}
|
|
|
|
void HText_beginTBODY(HText *text)
|
|
{
|
|
#ifdef FEATURE_TABLES
|
|
#endif /* FEATURE_TABLES */
|
|
}
|
|
|
|
void HText_beginTFOOT(HText *text)
|
|
{
|
|
#ifdef FEATURE_TABLES
|
|
#endif /* FEATURE_TABLES */
|
|
}
|
|
|
|
void HText_beginTHEAD(HText *text)
|
|
{
|
|
#ifdef FEATURE_TABLES
|
|
#endif /* FEATURE_TABLES */
|
|
}
|
|
|
|
#ifdef FEATURE_TABLES
|
|
|
|
static BOOL x_clone_cellvector(struct _cellvector * pcvdest, struct _cellvector * pcvsrc)
|
|
{
|
|
int k;
|
|
|
|
pcvdest->size = 0;
|
|
pcvdest->next = 0;
|
|
pcvdest->aVector = NULL;
|
|
|
|
for (k=0; k<pcvsrc->next; k++)
|
|
x_set_kth_cell(pcvdest,k,pcvsrc->aVector[k]);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL x_clone_tabledata(struct _tabledata * ptdest, struct _tabledata * ptsrc)
|
|
{
|
|
/* copy _tabledata structure */
|
|
|
|
ptdest->total_x = ptsrc->total_x;
|
|
ptdest->total_y = ptsrc->total_y;
|
|
ptdest->constraint_x = ptsrc->constraint_x;
|
|
ptdest->new_origin_x = ptsrc->new_origin_x;
|
|
ptdest->new_origin_y = ptsrc->new_origin_y;
|
|
ptdest->tfs = ptsrc->tfs;
|
|
ptdest->current_x = ptsrc->current_x;
|
|
ptdest->current_y = ptsrc->current_y;
|
|
|
|
if ( ! x_clone_cellvector(&ptdest->XCellCoords,&ptsrc->XCellCoords))
|
|
return FALSE;
|
|
if ( ! x_clone_cellvector(&ptdest->YCellCoords,&ptsrc->YCellCoords))
|
|
return FALSE;
|
|
if ( ! x_clone_cellvector(&ptdest->x_smallest_max,&ptsrc->x_smallest_max))
|
|
return FALSE;
|
|
if ( ! x_clone_cellvector(&ptdest->x_widest_max,&ptsrc->x_widest_max))
|
|
return FALSE;
|
|
if ( ! x_clone_cellvector(&ptdest->y_smallest_max,&ptsrc->y_smallest_max))
|
|
return FALSE;
|
|
if ( ! x_clone_cellvector(&ptdest->row_spans,&ptsrc->row_spans))
|
|
return FALSE;
|
|
|
|
#ifdef FEATURE_TABLE_WIDTH
|
|
ptdest->given_table_width = ptsrc->given_table_width;
|
|
if ( ! x_clone_cellvector(&ptdest->x_given_widths,&ptsrc->x_given_widths))
|
|
return FALSE;
|
|
if ( ! x_clone_cellvector(&ptdest->x_constraints,&ptsrc->x_constraints))
|
|
return FALSE;
|
|
#endif /* FEATURE_TABLE_WIDTH */
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL x_clone_tabledatavector(struct _tabledatavector * ptvdest, struct _tabledatavector * ptvsrc)
|
|
{
|
|
int k;
|
|
|
|
ptvdest->size = 0;
|
|
ptvdest->next = 0;
|
|
ptvdest->aVector = NULL;
|
|
|
|
if (ptvsrc->next)
|
|
{
|
|
k = x_extend_tabledata(ptvdest);
|
|
while ((k != -1) && (k != ptvsrc->next))
|
|
k = x_extend_tabledata(ptvdest);
|
|
}
|
|
|
|
if (k == -1)
|
|
return FALSE;
|
|
|
|
for (k=0; k<ptvsrc->next; k++)
|
|
if ( ! x_clone_tabledata(&ptvdest->aVector[k],&ptvsrc->aVector[k]))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL TW_CloneW3docTableInfo(struct _www * pdest, struct _www * psrc)
|
|
{
|
|
/* copy table descriptor fields in psrc into pdest */
|
|
|
|
if ( ! x_clone_cellvector(&pdest->cellStack,&psrc->cellStack))
|
|
return FALSE;
|
|
|
|
if ( ! x_clone_tabledatavector(&pdest->tabledatavector,&psrc->tabledatavector))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void x_free_cellvector(struct _cellvector * pcv)
|
|
{
|
|
if (pcv->aVector)
|
|
GTR_FREE(pcv->aVector);
|
|
|
|
return;
|
|
}
|
|
|
|
static void x_free_tabledata(struct _tabledata * ptd)
|
|
{
|
|
x_free_cellvector(&ptd->XCellCoords);
|
|
x_free_cellvector(&ptd->YCellCoords);
|
|
x_free_cellvector(&ptd->x_smallest_max);
|
|
x_free_cellvector(&ptd->x_widest_max);
|
|
x_free_cellvector(&ptd->y_smallest_max);
|
|
x_free_cellvector(&ptd->row_spans);
|
|
#ifdef FEATURE_TABLE_WIDTH
|
|
x_free_cellvector(&ptd->x_given_widths);
|
|
x_free_cellvector(&ptd->x_constraints);
|
|
#endif /* FEATURE_TABLE_WIDTH */
|
|
|
|
return;
|
|
}
|
|
|
|
static void x_free_tabledatavector(struct _tabledatavector * ptdv)
|
|
{
|
|
int k;
|
|
|
|
if (ptdv->aVector)
|
|
{
|
|
for (k=0; k<ptdv->next; k++)
|
|
x_free_tabledata(&ptdv->aVector[k]);
|
|
|
|
GTR_FREE(ptdv->aVector);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void TW_W3DocTableCleanup(struct _www * pdoc)
|
|
{
|
|
/* free all table related data structures in pdoc */
|
|
|
|
x_free_cellvector(&pdoc->cellStack);
|
|
x_free_tabledatavector(&pdoc->tabledatavector);
|
|
|
|
return;
|
|
}
|
|
|
|
#endif /* FEATURE_TABLES */
|