home *** CD-ROM | disk | FTP | other *** search
- /* -*- 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_tags.h"
- #include "layout.h"
- #include "laylayer.h"
- #include "layers.h"
- #include "libi18n.h"
- #include "edt.h"
- #include "laystyle.h"
- #include "laytrav.h"
-
- /*
- * Turn this define on to get the new multibyte parsing code
- #define FAST_MULTI
- */
-
- #define FAST_EDITOR
-
- #ifdef TEST_16BIT
- #define XP_WIN16
- #endif /* TEST_16BIT */
-
- #ifdef XP_WIN16
- #define SIZE_LIMIT 32000
- #endif /* XP_WIN16 */
-
- #ifdef XP_UNIX
- #define TEXT_CHUNK_LIMIT 500
- #else
- #define TEXT_CHUNK_LIMIT 1600
- #endif
-
- #ifdef XP_MAC
- #define NON_BREAKING_SPACE 0x07
- #else
- #define NON_BREAKING_SPACE 160
- #endif
-
- #ifdef PROFILE
- #pragma profile on
- #endif
-
- int32 lo_correct_text_element_width(LO_TextInfo *);
-
- static void lo_insert_quote_characters(MWContext *context, lo_DocState *state);
- static LO_TextStruct * lo_new_text_element(MWContext *context, lo_DocState *state, ED_Element *edit_element,
- intn edit_offset );
-
- void lo_LayoutFormattedText(MWContext *context, lo_DocState *state, LO_TextBlock * block);
- void lo_LayoutPreformattedText(MWContext *context, lo_DocState *state, LO_TextBlock * block);
- LO_TextBlock * lo_NewTextBlock ( MWContext * context, lo_DocState * state, char * text, uint16 formatMode );
- int32 lo_compute_text_basline_inc ( lo_DocState * state, LO_TextBlock * block, LO_TextStruct * text_data );
-
- uint32 lo_FindBlockOffset ( LO_TextBlock * block, LO_TextStruct * fromElement );
- void lo_RelayoutTextElements ( MWContext * context, lo_DocState * state, LO_TextBlock * block, LO_TextStruct * fromElement );
- Bool lo_CanUseBreakTable ( lo_DocState * state );
- Bool lo_UseBreakTable ( LO_TextBlock * block );
-
- void lo_AppendTextToBlock ( MWContext *context, lo_DocState *state, LO_TextBlock * block, char *text );
- void lo_LayoutTextBlock ( MWContext * context, lo_DocState * state, Bool flushLastLine );
- static void lo_FlushText ( MWContext * context, lo_DocState * state );
- static void lo_SetupBreakState ( LO_TextBlock * block );
-
- Bool lo_GrowTextBlock ( LO_TextBlock * block, uint32 length );
- static void lo_GetTextParseAtributes ( lo_DocState * state, Bool * multiByte );
-
-
- #ifdef XP_MAC
- #define kStaticMeasureTextBufferSize 512
- static uint16 gMeasureTextBuffer[ kStaticMeasureTextBufferSize ];
-
- /* This needs to go in fe_proto.h - will move it there once we're out of the branch */
- #define FE_MeasureText(context, text, charLocs) \
- (*context->funcs->MeasureText)(context, text, charLocs)
- #endif
-
- #ifdef XP_MAC
- Bool gCallNewText = TRUE;
- #endif
-
- LO_TextBlock * lo_NewTextBlock ( MWContext * context, lo_DocState * state, char * text, uint16 formatMode )
- {
- LO_TextBlock * block;
- uint32 length;
-
- length = strlen ( text );
-
- block = (LO_TextBlock *)lo_NewElement ( context, state, LO_TEXTBLOCK, NULL, 0 );
- if ( block == NULL )
- {
- state->top_state->out_of_memory = TRUE;
- return NULL;
- }
-
- block->type = LO_TEXTBLOCK;
- block->x_offset = 0;
- block->ele_id = NEXT_ELEMENT;
- block->x = state->x;
- block->y = state->y;
- block->y_offset = 0;
- block->width = 0;
- block->height = 0;
- block->line_height = 0;
- block->next = NULL;
- block->prev = NULL;
- block->text_attr = NULL;
- block->anchor_href = NULL;
- block->ele_attrmask = 0;
- block->format_mode = 0;
-
- block->startTextElement = NULL;
- block->endTextElement = NULL;
-
- block->text_buffer = NULL;
- block->buffer_length = 0;
- block->buffer_write_index = 0;
- block->last_buffer_write_index = 0;
- block->buffer_read_index = 0;
- block->last_line_break = 0;
-
- block->break_table = NULL;
- block->break_length = 0;
- block->break_write_index = 0;
- block->break_read_index = 0;
- block->last_break_offset = 0;
-
- block->multibyte_index = 0;
- block->multibyte_length = 0;
-
- block->old_break = NULL;
- block->old_break_pos = 0;
- block->old_break_width = 0;
-
- block->totalWidth = 0;
- block->totalChars = 0;
- block->break_pending = 0;
- block->last_char_is_whitespace = 0;
-
- block->ascent = 0;
- block->descent = 0;
-
- block->text_buffer = XP_ALLOC ( length + 1 );
- if ( block->text_buffer == NULL )
- {
- XP_FREE ( block );
- state->top_state->out_of_memory = TRUE;
- return NULL;
- }
-
- block->buffer_length = length + 1;
- XP_BCOPY ( text, block->text_buffer, length );
- block->text_buffer[ length ] = '\0';
- block->buffer_write_index = length;
-
- /* default text does not have a break table */
- block->break_table = NULL;
- block->break_length = 0;
-
- block->format_mode = formatMode;
-
- /*
- * Since we're creating a new text block, grab some of the text state out of the
- * layout state.
- */
- block->anchor_href = state->current_anchor;
-
- if ( state->font_stack != NULL )
- {
- block->text_attr = state->font_stack->text_attr;
- }
-
- if (state->breakable != FALSE)
- {
- block->ele_attrmask |= LO_ELE_BREAKABLE;
- }
-
- state->cur_text_block = block;
-
- lo_AppendToLineList ( context, state, (LO_Element *) block, 0 );
-
- return block;
- }
-
- void
- lo_SetDefaultFontAttr(lo_DocState *state, LO_TextAttr *tptr,
- MWContext *context)
- {
- tptr->size = DEFAULT_BASE_FONT_SIZE;
- tptr->fontmask = 0;
- tptr->no_background = TRUE;
- tptr->attrmask = 0;
- tptr->fg.red = STATE_DEFAULT_FG_RED(state);
- tptr->fg.green = STATE_DEFAULT_FG_GREEN(state);
- tptr->fg.blue = STATE_DEFAULT_FG_BLUE(state);
- tptr->bg.red = STATE_DEFAULT_BG_RED(state);
- tptr->bg.green = STATE_DEFAULT_BG_GREEN(state);
- tptr->bg.blue = STATE_DEFAULT_BG_BLUE(state);
- tptr->charset = INTL_DefaultTextAttributeCharSetID(context);
- tptr->font_face = NULL;
- tptr->FE_Data = NULL;
- tptr->point_size = 0;
- tptr->font_weight = 0;
- }
-
-
- /*************************************
- * Function: lo_DefaultFont
- *
- * Description: This function sets up the text attribute
- * structure for the default text drawing font.
- * This is the font that sits at the bottom of the font
- * stack, and can never be popped off.
- *
- * Params: Document state
- *
- * Returns: A pointer to a lo_FontStack structure.
- * Returns a NULL on error (such as out of memory);
- *************************************/
- lo_FontStack *
- lo_DefaultFont(lo_DocState *state, MWContext *context)
- {
- LO_TextAttr tmp_attr;
- lo_FontStack *fptr;
- LO_TextAttr *tptr;
-
- /*
- * Fill in default font information.
- */
- lo_SetDefaultFontAttr(state, &tmp_attr, context);
- tptr = lo_FetchTextAttr(state, &tmp_attr);
-
- fptr = XP_NEW(lo_FontStack);
- if (fptr == NULL)
- {
- return(NULL);
- }
- fptr->tag_type = P_UNKNOWN;
- fptr->text_attr = tptr;
- fptr->next = NULL;
-
- return(fptr);
- }
-
-
- void
- lo_PushAlignment(lo_DocState *state, intn tag_type, int32 alignment)
- {
- lo_AlignStack *aptr;
-
- aptr = XP_NEW(lo_AlignStack);
- if (aptr == NULL)
- {
- state->top_state->out_of_memory = TRUE;
- return;
- }
-
- aptr->type = tag_type;
- aptr->alignment = alignment;
-
- aptr->next = state->align_stack;
- state->align_stack = aptr;
- }
-
-
- lo_AlignStack *
- lo_PopAlignment(lo_DocState *state)
- {
- lo_AlignStack *aptr;
-
- aptr = state->align_stack;
- if (aptr != NULL)
- {
- state->align_stack = aptr->next;
- aptr->next = NULL;
- }
- return(aptr);
- }
-
- void
- lo_PushLineHeight(lo_DocState *state, int32 height)
- {
- lo_LineHeightStack *aptr;
-
- aptr = XP_NEW(lo_LineHeightStack);
- if (aptr == NULL)
- {
- state->top_state->out_of_memory = TRUE;
- return;
- }
-
- aptr->height = height;
-
- aptr->next = state->line_height_stack;
- state->line_height_stack = aptr;
- }
-
- lo_LineHeightStack *
- lo_PopLineHeight(lo_DocState *state)
- {
- lo_LineHeightStack *aptr;
-
- aptr = state->line_height_stack;
- if (aptr != NULL)
- {
- state->line_height_stack = aptr->next;
- aptr->next = NULL;
- }
- return(aptr);
- }
-
- /*************************************
- * Function: lo_FreshText
- *
- * Description: This function clears out the information from the
- * document state that refers to text in the midst of being
- * laid out. It is called to clear the layout state before
- * Laying out new text potentially in a new font.
- *
- * Params: Document state structure.
- *
- * Returns: Nothing
- *************************************/
- void
- lo_FreshText(lo_DocState *state)
- {
- state->text_info.max_width = 0;
- state->text_info.ascent = 0;
- state->text_info.descent = 0;
- state->text_info.lbearing = 0;
- state->text_info.rbearing = 0;
- state->line_buf_len = 0;
- state->break_pos = -1;
- state->break_width = 0;
- state->last_char_CR = FALSE;
- #ifdef EDITOR
- if( !state->edit_force_offset )
- {
- state->edit_current_offset = 0;
- }
- #endif
- }
-
-
- /*************************************
- * Function: lo_new_text_element
- *
- * Description: Create a new text element structure based
- * on the current text information stored in the document
- * state.
- *
- * Params: Document state
- *
- * Returns: A pointer to a LO_TextStruct structure.
- * Returns a NULL on error (such as out of memory);
- *************************************/
- static LO_TextStruct *
- lo_new_text_element(MWContext *context, lo_DocState *state, ED_Element *edit_element,
- intn edit_offset )
- {
- LO_TextStruct *text_ele = NULL;
- #ifdef DEBUG
- assert (state);
- #endif
-
- if (state == NULL)
- {
- return(NULL);
- }
-
- text_ele = (LO_TextStruct *)lo_NewElement(context, state, LO_TEXT,
- edit_element, edit_offset);
- if (text_ele == NULL)
- {
- #ifdef DEBUG
- assert (state->top_state->out_of_memory);
- #endif
- return(NULL);
- }
-
- text_ele->type = LO_TEXT;
- text_ele->ele_id = NEXT_ELEMENT;
- text_ele->x = state->x;
- text_ele->x_offset = 0;
- text_ele->y = state->y;
- text_ele->y_offset = 0;
- text_ele->width = 0;
- text_ele->height = 0;
- text_ele->next = NULL;
- text_ele->prev = NULL;
-
- if (state->line_buf_len > 0)
- {
- text_ele->text = PA_ALLOC((state->line_buf_len + 1) *
- sizeof(char));
- if (text_ele->text != NULL)
- {
- char *text_buf;
- char *line;
-
- PA_LOCK(line, char *, state->line_buf);
- PA_LOCK(text_buf, char *, text_ele->text);
- XP_BCOPY(line, text_buf, state->line_buf_len);
- text_buf[state->line_buf_len] = '\0';
- PA_UNLOCK(text_ele->text);
- PA_UNLOCK(state->line_buf);
- text_ele->text_len = (int16)state->line_buf_len;
- }
- else
- {
- state->top_state->out_of_memory = TRUE;
- /*
- * free text element && return; it's no different
- * than if we had run out of memory allocating
- * the text element
- */
- lo_FreeElement(context, (LO_Element *)text_ele, FALSE);
- return(NULL);
- }
- }
- else
- {
- text_ele->text = NULL;
- text_ele->text_len = 0;
- }
-
- text_ele->anchor_href = state->current_anchor;
-
- if (state->font_stack == NULL)
- {
- text_ele->text_attr = NULL;
- }
- else
- {
- text_ele->text_attr = state->cur_text_block->text_attr;
- }
-
- text_ele->ele_attrmask = 0;
- if (state->breakable != FALSE)
- {
- text_ele->ele_attrmask |= LO_ELE_BREAKABLE;
- }
-
- text_ele->sel_start = -1;
- text_ele->sel_end = -1;
-
- return(text_ele);
- }
-
-
- /*
- * A bogus an probably too expensive function to fill in the
- * textinfo for whatever font is now on the font stack.
- */
- MODULE_PRIVATE void
- lo_fillin_text_info(MWContext *context, lo_DocState *state)
- {
- LO_TextStruct tmp_text;
- PA_Block buff;
- char *str;
-
- memset (&tmp_text, 0, sizeof (tmp_text));
- buff = PA_ALLOC(1);
- if (buff == NULL)
- {
- return;
- }
- PA_LOCK(str, char *, buff);
- str[0] = ' ';
- PA_UNLOCK(buff);
- tmp_text.text = buff;
- tmp_text.text_len = 1;
-
- /* if we have a text block, use it's font info, otherwise get it from the state */
- if ( state->cur_text_block == NULL )
- {
- tmp_text.text_attr = state->font_stack->text_attr;
- }
- else
- {
- tmp_text.text_attr = state->cur_text_block->text_attr;
- }
-
- FE_GetTextInfo(context, &tmp_text, &(state->text_info));
- PA_FREE(buff);
- }
-
-
- /*************************************
- * Function: lo_NewLinefeed
- *
- * Description: Create a new linefeed element structure based
- * on the current information stored in the document
- * state.
- *
- * Params: Document state, linefeed type
- *
- * Returns: A pointer to a LO_LinefeedStruct structure.
- * Returns a NULL on error (such as out of memory);
- *************************************/
- LO_LinefeedStruct *
- lo_NewLinefeed(lo_DocState *state, MWContext * context, uint32 break_type, uint32 clear_type)
- {
- LO_LinefeedStruct *linefeed = NULL;
-
- if (state == NULL)
- {
- return(NULL);
- }
-
- linefeed = (LO_LinefeedStruct *)lo_NewElement(context, state, LO_LINEFEED, NULL, 0);
- #ifdef DEBUG
- assert (state);
- #endif
- if (linefeed == NULL)
- {
- #ifdef DEBUG
- assert (state->top_state->out_of_memory);
- #endif
- state->top_state->out_of_memory = TRUE;
- return(NULL);
- }
-
- lo_FillInLineFeed( context, state, break_type, clear_type, linefeed );
-
- /*
- linefeed->type = LO_LINEFEED;
- linefeed->ele_id = NEXT_ELEMENT;
- linefeed->x = state->x;
- linefeed->x_offset = 0;
- linefeed->y = state->y;
- linefeed->y_offset = 0;
- */
- /*
- * If we're laying out a block, we want the contents of the block
- * to determine the size of the block. The right margin is nothing
- * more than a hint for where to wrap the contents. We don't want
- * the linefeed to extend out to the right margin, because it
- * unnecessarily extends the block contents.
- */
- /*
- if (state->layer_nest_level > 0) {
- linefeed->width = 0;
- }
- else
- */
- /*
- linefeed->width = state->right_margin - state->x;
- if (linefeed->width < 0)
- {
- linefeed->width = 0;
- }
- linefeed->height = state->line_height;
- */
- /*
- * if this linefeed has a zero height, make it the height
- * of the current font.
- */
- /*
- if (linefeed->height == 0)
- {
- linefeed->height = state->text_info.ascent +
- state->text_info.descent;
- if ((linefeed->height <= 0)&&(state->font_stack != NULL)&&
- (state->font_stack->text_attr != NULL))
- {
- lo_fillin_text_info(context, state);
- linefeed->height = state->text_info.ascent +
- state->text_info.descent;
- }
- */
- /*
- * This should never happen, but we have it
- * covered just in case it does :-)
- */
- /*
- if (linefeed->height <= 0)
- {
- linefeed->height = state->default_line_height;
- }
- }
- linefeed->line_height = linefeed->height;
-
- linefeed->FE_Data = NULL;
- linefeed->anchor_href = state->current_anchor;
-
- if (state->font_stack == NULL)
- {
- LO_TextAttr tmp_attr;
- LO_TextAttr *tptr;
- */
- /*
- * Fill in default font information.
- */
- /*
- lo_SetDefaultFontAttr(state, &tmp_attr, context);
- tptr = lo_FetchTextAttr(state, &tmp_attr);
- linefeed->text_attr = tptr;
- }
- else
- {
- linefeed->text_attr = state->font_stack->text_attr;
- }
-
- linefeed->baseline = state->baseline;
-
- linefeed->ele_attrmask = 0;
-
- linefeed->sel_start = -1;
- linefeed->sel_end = -1;
-
- linefeed->next = NULL;
- linefeed->prev = NULL;
- linefeed->break_type = (uint8) break_type;
- */
-
- return(linefeed);
- }
-
-
-
- /*************************************
- * Function: lo_InsertLineBreak
- *
- * Description: This function forces a linebreak at this time.
- * Forcing the current text insetion point down one line
- * and back to the left margin.
- * It also maintains the linefeed state:
- * 0 - middle of a line.
- * 1 - at left margin below a line of text.
- * 2 - at left margin below a blank line.
- *
- * Params: Window context, Document state, break type and a boolean
- * That describes whether this is a breaking linefeed or not.
- * (Breaking linefeed is one inserted just to break a formatted
- * line to the current document width.)
- *
- * Returns: Nothing
- *************************************/
- void
- lo_InsertLineBreak(MWContext *context, lo_DocState *state, uint32 break_type, uint32 clear_type, Bool breaking)
- {
- /* int32 line_width; */
- Bool scroll_at_bottom;
-
- scroll_at_bottom = FALSE;
- /*
- * If this is an auto-scrolling doc, we will be scrolling
- * later if we are at the bottom now.
- */
- if ((state->is_a_subdoc == SUBDOC_NOT)&&
- (state->display_blocked == FALSE)&&
- (state->top_state->auto_scroll > 0))
- {
- int32 doc_x, doc_y;
-
- FE_GetDocPosition(context, FE_VIEW, &doc_x, &doc_y);
- if ((doc_y + state->win_height + state->win_bottom) >= state->y)
- {
- scroll_at_bottom = TRUE;
- }
- }
-
- /*
- * This line is done, flush it into the line table and
- * display it to the front end.
- */
- lo_FlushLineList(context, state, break_type, clear_type, breaking);
- if (state->top_state->out_of_memory != FALSE)
- {
- return;
- }
-
- lo_UpdateStateAfterLineBreak( context, state, TRUE );
-
- /*
- * if this linefeed has a zero height, make it the height
- * of the current font.
- */
- /*
- if (state->line_height == 0)
- {
- state->line_height = state->text_info.ascent +
- state->text_info.descent;
- if ((state->line_height <= 0)&&(state->font_stack != NULL)&&
- (state->font_stack->text_attr != NULL))
- {
- lo_fillin_text_info(context, state);
- state->line_height = state->text_info.ascent +
- state->text_info.descent;
- }
- */
- /*
- * This should never happen, but we have it
- * covered just in case it does :-)
- */
- /*
- if (state->line_height <= 0)
- {
- state->line_height = state->default_line_height;
- }
- }
-
- if (state->end_last_line != NULL)
- {
- line_width = state->end_last_line->lo_any.x + state->win_right;
- }
- else
- {
- line_width = state->x + state->win_right;
- }
-
- if (line_width > state->max_width)
- {
- state->max_width = line_width;
- }
- */
-
- /* if LineHeightStack exists use it to offset the new Y value */
- /*
- if(state->cur_ele_type != LO_SUBDOC && state->line_height_stack)
- {
- state->y += state->line_height_stack->height;
- }
- else
- {
- state->y = state->y + state->line_height;
- }
-
- state->x = state->left_margin;
- state->width = 0;
- state->at_begin_line = TRUE;
- state->trailing_space = FALSE;
- state->line_height = 0;
- state->break_holder = state->x;
-
- state->linefeed_state++;
- if (state->linefeed_state > 2)
- {
- state->linefeed_state = 2;
- }
-
- */
-
- lo_UpdateFEProgressBar(context, state);
-
- /*
- if (state->is_a_subdoc == SUBDOC_NOT)
- {
- int32 percent;
-
- if (state->top_state->total_bytes < 1)
- {
- percent = -1;
- }
- else
- {
- percent = (100 * state->top_state->layout_bytes) /
- state->top_state->total_bytes;
- if (percent > 100)
- {
- percent = 100;
- }
- }
- if ((percent == 100)||(percent < 0)||
- (percent > (state->top_state->layout_percent + 1)))
- {
- if(!state->top_state->is_binary)
- FE_SetProgressBarPercent(context, percent);
- state->top_state->layout_percent = (intn)percent;
- }
- }
- */
-
- lo_UpdateFEDocSize( context, state );
-
- /*
- * Tell the front end how big the document is right now.
- * Only do this for the top level document.
- */
- /*
- if ((state->is_a_subdoc == SUBDOC_NOT)
- &&(state->display_blocked == FALSE)
- #ifdef EDITOR
- &&(!state->edit_relayout_display_blocked)
- #endif
- )
- {
- */
-
- /*
- * Don't resize the layer if we're laying out a block. This
- * will be done when the line is added to the block.
- */
- /*
- if (!lo_InsideLayer(state))
- {
- LO_SetDocumentDimensions(context, state->max_width, state->y);
- }
- }
- */
-
- if ((state->display_blocked != FALSE)&&
- #ifdef EDITOR
- (!state->edit_relayout_display_blocked)&&
- #endif
- (state->display_blocking_element_y > 0)&&
- (state->y > (state->display_blocking_element_y + state->win_height)))
- {
- int32 y;
-
- state->display_blocked = FALSE;
- y = state->display_blocking_element_y;
- state->display_blocking_element_y = 0;
- if (!lo_InsideLayer(state))
- {
- LO_SetDocumentDimensions(context, state->max_width, state->y);
- }
- FE_SetDocPosition(context, FE_VIEW, 0, y);
-
- if (context->compositor)
- {
- XP_Rect rect;
-
- rect.left = 0;
- rect.top = y;
- rect.right = state->win_width;
- rect.bottom = y + state->win_height;
- CL_UpdateDocumentRect(context->compositor,
- &rect, (PRBool)FALSE);
- }
- }
-
- /*
- * Reset the left and right margins
- */
- /*
- lo_FindLineMargins(context, state, TRUE);
- state->x = state->left_margin;
- */
- if ((state->is_a_subdoc == SUBDOC_NOT)&&
- (state->top_state->auto_scroll > 0)&&
- ((state->line_num - 1) > state->top_state->auto_scroll))
- {
- Bool redraw;
- int32 old_y, dy, new_y;
- int32 doc_x, doc_y;
-
- old_y = state->y;
- FE_GetDocPosition(context, FE_VIEW, &doc_x, &doc_y);
- lo_ClipLines(context, state, 1);
- /*
- * Calculate how much of the top was clipped
- * off, and if any of that area was in the users
- * window we need to redraw their window.
- */
- redraw = FALSE;
- dy = old_y - state->y;
- if (dy >= doc_y)
- {
- redraw = TRUE;
- }
- new_y = doc_y - dy;
- if (new_y < 0)
- {
- new_y = 0;
- }
-
- FE_SetDocPosition(context, FE_VIEW, doc_x, new_y);
- if (!lo_InsideLayer(state))
- {
- LO_SetDocumentDimensions(context, state->max_width, state->y);
- }
-
- if (redraw != FALSE)
- {
- FE_ClearView(context, FE_VIEW );
- if (context->compositor)
- {
- XP_Rect rect;
-
- rect.left = 0;
- rect.top = new_y;
- rect.right = state->win_width;
- rect.bottom = new_y + state->win_height;
- CL_UpdateDocumentRect(context->compositor,
- &rect, (PRBool)FALSE);
- }
- }
- }
-
- if ((state->is_a_subdoc == SUBDOC_NOT)&&
- (state->display_blocked == FALSE)&&
- (state->top_state->auto_scroll > 0))
- {
- int32 doc_x, doc_y;
-
- FE_GetDocPosition(context, FE_VIEW, &doc_x, &doc_y);
- if (((doc_y + state->win_height) < state->y)&&
- (scroll_at_bottom != FALSE))
- {
- int32 y;
-
- y = state->y - state->win_height + state->win_bottom;
- if (y < 0)
- {
- y = 0;
- }
- else
- {
- FE_SetDocPosition(context, FE_VIEW, 0, y);
- }
- }
- }
-
- lo_insert_quote_characters(context, state);
- }
-
- /*************************************
- * Function: lo_HardLineBreak
- *
- * Description: This function forces a linebreak at this time.
- * Forcing the current text insetion point down one line
- * and back to the left margin.
- *
- * Params: Window context, Document state and a boolean
- * That describes whether this is a breaking linefeed or not.
- * (Breaking linefeed is one inserted just to break a formatted
- * line to the current document width.)
- *
- * Returns: Nothing
- *************************************/
- void
- lo_HardLineBreak(MWContext *context, lo_DocState *state, Bool breaking)
- {
- lo_InsertLineBreak ( context, state, LO_LINEFEED_BREAK_HARD, LO_CLEAR_NONE, breaking );
- }
-
- /*************************************
- * Function: lo_HardLineBreakWithBreak
- *
- * Returns: Nothing
- *************************************/
- void
- lo_HardLineBreakWithClearType(MWContext *context, lo_DocState *state, uint32 clear_type, Bool breaking)
- {
- lo_InsertLineBreak ( context, state, LO_LINEFEED_BREAK_HARD, clear_type, breaking );
- }
-
- /*************************************
- * Function: lo_SoftLineBreak
- *
- * Description: This function adds a single soft line break.
- *
- * Params: Window context, Document state and a boolean that
- * describes if this is a breaking linefeed.
- *
- * Returns: Nothing
- *************************************/
- void
- lo_SoftLineBreak(MWContext *context, lo_DocState *state, Bool breaking)
- {
- lo_InsertLineBreak ( context, state, LO_LINEFEED_BREAK_SOFT, LO_CLEAR_NONE, breaking);
- }
-
- /*************************************
- * Function: lo_SetSoftLineBreakState
- *
- * Description: Add soft linebreaks to bring the linefeed state to the
- * specified level.
- * Linefeed states:
- * 0 - middle of a line.
- * 1 - at left margin below a line of text.
- * 2 - at left margin below a blank line.
- *
- * Params: Window context, Document state, a boolean that
- * describes if this is a breaking linefeed, and the desired
- * linefeed state.
- *
- * Returns: Nothing
- *************************************/
- void
- lo_SetSoftLineBreakState(MWContext *context, lo_DocState *state, Bool breaking,
- intn linefeed_state)
- {
- lo_SetLineBreakState ( context, state, breaking, LO_LINEFEED_BREAK_SOFT,
- linefeed_state, FALSE);
- }
-
- /*************************************
- * Function: lo_SetLineBreakState
- *
- * Description: Add linebreaks to bring the linefeed state to the
- * specified level.
- * Linefeed states:
- * 0 - middle of a line.
- * 1 - at left margin below a line of text.
- * 2 - at left margin below a blank line.
- *
- * Params: Window context, Document state, a boolean that
- * describes if this is a breaking linefeed, and the desired
- * linefeed state.
- *
- * Returns: Nothing
- *************************************/
- void
- lo_SetLineBreakState(MWContext *context, lo_DocState *state, Bool breaking,
- uint32 break_type, intn linefeed_state, Bool relayout)
- {
- /*
- * Linefeeds are partially disabled if we are placing
- * preformatted text.
- */
- if (state->preformatted != PRE_TEXT_NO)
- {
- if ((breaking == FALSE)&&(state->linefeed_state < 1)&&
- (state->top_state->out_of_memory == FALSE))
- {
- if (relayout == FALSE)
- {
- lo_InsertLineBreak(context, state, break_type, LO_CLEAR_NONE, breaking);
- }
- else
- {
- lo_rl_AddBreakAndFlushLine( context, state, break_type, LO_CLEAR_NONE, breaking);
- }
- }
- return;
- }
-
- /* check for the style sheet display: inline property.
- * ignore newlines if it is set
- */
- if(state->top_state && state->top_state->style_stack)
- {
- StyleStruct *style = STYLESTACK_GetStyleByIndex(
- state->top_state->style_stack,
- 0);
- if(style)
- {
- char * property = STYLESTRUCT_GetString(style, DISPLAY_STYLE);
- if(property && !strcasecomp(property, INLINE_STYLE))
- {
- XP_FREE(property);
- return;
- }
- XP_FREEIF(property);
- }
- }
-
- if (linefeed_state > 2)
- {
- linefeed_state = 2;
- }
-
- while ((state->linefeed_state < linefeed_state)&&
- (state->top_state->out_of_memory == FALSE))
- {
- if (relayout == FALSE)
- {
- lo_InsertLineBreak(context, state, break_type, LO_CLEAR_NONE, breaking);
- }
- else
- {
- lo_rl_AddBreakAndFlushLine( context, state, break_type, LO_CLEAR_NONE, breaking );
- }
- }
- }
-
- /*************************************
- * Function: lo_InsertWordBreak
- *
- * Description: This insert a word break into the laid out
- * element chain for the current line. A word break is
- * basically an empty text element structure. All
- * word break elements should probably be removed
- * from the layout chain once the line list for this
- * line is flushed.
- *
- * Params: Window context and document state.
- *
- * Returns: Nothing
- *************************************/
- void
- lo_InsertWordBreak(MWContext *context, lo_DocState *state)
- {
- LO_TextStruct *text_ele = NULL;
-
- if (state == NULL)
- {
- return;
- }
-
- text_ele = (LO_TextStruct *)lo_NewElement(context, state, LO_TEXT, NULL, 0);
- if (text_ele == NULL)
- {
- #ifdef DEBUG
- assert (state->top_state->out_of_memory);
- #endif
- return;
- }
-
- text_ele->type = LO_TEXT;
- text_ele->ele_id = NEXT_ELEMENT;
- text_ele->x = state->x;
- text_ele->x_offset = 0;
- text_ele->y = state->y;
- text_ele->y_offset = 0;
- text_ele->width = 0;
- text_ele->height = 0;
- text_ele->next = NULL;
- text_ele->prev = NULL;
-
- text_ele->block_offset = 0;
- text_ele->doc_width = 0;
-
- #if 0
- text_ele->text = PA_ALLOC(sizeof(char));
- if (text_ele->text != NULL)
- {
- PA_LOCK(text_buf, char *, text_ele->text);
- text_buf[0] = '\0';
- PA_UNLOCK(text_ele->text);
- }
- else
- {
- state->top_state->out_of_memory = TRUE;
- }
- #else
- text_ele->text = NULL;
- #endif
- text_ele->text_len = 0;
- text_ele->anchor_href = state->current_anchor;
-
- if (state->font_stack == NULL)
- {
- text_ele->text_attr = NULL;
- }
- else
- {
- text_ele->text_attr = state->font_stack->text_attr;
- }
-
- text_ele->ele_attrmask = 0;
- if (state->breakable != FALSE)
- {
- text_ele->ele_attrmask |= LO_ELE_BREAKABLE;
- }
-
- text_ele->sel_start = -1;
- text_ele->sel_end = -1;
- text_ele->bullet_type = WORDBREAK;
-
- lo_AppendToLineList(context, state, (LO_Element *)text_ele, 0);
-
- state->old_break = text_ele;
- state->old_break_block = state->cur_text_block;
- state->old_break_pos = 0;
- state->old_break_width = 0;
- }
-
-
- int32
- lo_baseline_adjust(MWContext *context, lo_DocState * state, LO_Element *ele_list,
- int32 old_baseline, int32 old_line_height)
- {
- LO_Element *eptr;
- int32 baseline;
- int32 new_height;
- int32 baseline_adjust;
- LO_TextInfo text_info;
-
- baseline = 0;
- new_height = 0;
- eptr = ele_list;
- while (eptr != NULL)
- {
- switch (eptr->type)
- {
- case LO_TEXT:
- FE_GetTextInfo(context, (LO_TextStruct *)eptr,
- &text_info);
- if (text_info.ascent > baseline)
- {
- baseline = text_info.ascent;
- }
- if ((text_info.ascent + text_info.descent) >
- new_height)
- {
- new_height = text_info.ascent +
- text_info.descent;
- }
- break;
- case LO_FORM_ELE:
- if (eptr->lo_form.baseline > baseline)
- {
- baseline = eptr->lo_form.baseline;
- }
- if (eptr->lo_any.height > new_height)
- {
- new_height = eptr->lo_any.height;
- }
- break;
- case LO_IMAGE:
- if ((old_baseline - eptr->lo_any.y_offset) >
- baseline)
- {
- baseline = old_baseline -
- eptr->lo_any.y_offset;
- }
- if ((eptr->lo_image.height +
- (2 * eptr->lo_image.border_width) +
- (2 * eptr->lo_image.border_vert_space))
- > new_height)
- {
- new_height = eptr->lo_image.height +
- (2 * eptr->lo_image.border_width) +
- (2 * eptr->lo_image.border_vert_space);
- }
- break;
- case LO_HRULE:
- case LO_BULLET:
- if ((old_baseline - eptr->lo_any.y_offset) >
- baseline)
- {
- baseline = old_baseline -
- eptr->lo_any.y_offset;
- }
- if (eptr->lo_any.height > new_height)
- {
- new_height = eptr->lo_any.height;
- }
- break;
- default:
- break;
- }
- eptr = eptr->lo_any.next;
- }
- baseline_adjust = old_baseline - baseline;
- return(baseline_adjust);
- }
-
-
- /*************************************
- * Function: lo_BreakOldElement
- *
- * Description: This function goes back to a previous
- * element in the element chain, that is still on the
- * same line, and breaks the line there, putting the
- * remaining elements (if any) on the next line.
- *
- * Params: Window context and document state.
- *
- * Returns: Nothing
- *************************************/
- void
- lo_BreakOldElement(MWContext *context, lo_DocState *state)
- {
- char *text_buf;
- char *break_ptr;
- char *word_ptr;
- char *new_buf;
- PA_Block new_block;
- intn word_len;
- int32 save_width;
- int32 old_baseline;
- int32 old_line_height;
- int32 adjust;
- LO_TextStruct *text_data;
- LO_TextStruct *new_text_data;
- LO_Element *tptr;
- int16 charset;
- int multi_byte;
- LO_TextBlock * block;
- #ifdef LOCAL_DEBUG
- XP_TRACE(("lo_BreakOldElement, flush text.\n"));
- #endif /* LOCAL_DEBUG */
-
- if (state == NULL)
- {
- return;
- }
-
- /* BRAIN DAMAGE: Make sure the correct element is at the end of the list for this block */
- block = state->old_break_block;
-
- if ( block == NULL )
- {
- return;
- }
-
- /*
- * If this text block is using the new breaktable algorithm, call that code
- */
- if ( lo_UseBreakTable ( block ) )
- {
- lo_BreakOldTextBlockElement ( context, state );
- return;
- }
-
- charset = block->text_attr->charset;
- multi_byte = (INTL_CharSetType(charset) != SINGLEBYTE);
-
- /*
- * Move to the element we will break
- */
- text_data = state->old_break;
-
- /*
- * If there is no text there to break
- * it is an error.
- */
- if ( text_data == NULL )
- {
- return;
- }
-
- /*
- * Later operations will trash the width field.
- * So save it now to restore later.
- */
- save_width = state->width;
-
- /*
- * If this buffer is just an empty string, then we are
- * breaking at a previously inserted word break element.
- * Knowing that, the breaking is easier, so it is special
- * cased here.
- */
- if (text_data->text == NULL)
- {
- LO_Element *line_ptr;
-
- /*
- * Back the state up to this element's
- * location, break off the rest of the elements
- * and save them for later.
- * Flush this line, and insert a linebreak.
- */
- state->x = text_data->x;
- state->y = text_data->y;
- tptr = text_data->next;
- text_data->next = NULL;
- PA_UNLOCK(text_data->text);
- state->width = text_data->width;
- state->x += state->width;
- lo_SoftLineBreak(context, state, TRUE);
-
- /*
- * The remaining elements go on the next line.
- * The nextline may already have special mail
- * bullets on it.
- */
- 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)
- {
- state->line_list = tptr;
- if (tptr != NULL)
- {
- tptr->lo_any.prev = NULL;
- }
- }
- else
- {
- line_ptr->lo_any.next = tptr;
- if (tptr != NULL)
- {
- tptr->lo_any.prev = line_ptr;
- }
- }
-
- /*
- * If there are no elements to place on the next
- * line, and we have new text buffered,
- * remove any spaces at the start of that new
- * text since new lines are not allowed to begin
- * with whitespace.
- */
- if ((tptr == NULL)&&(state->line_buf_len != 0))
- {
- char *cptr;
- int32 wlen;
-
- PA_LOCK(text_buf, char *, state->line_buf);
- cptr = text_buf;
- wlen = 0;
- while ((XP_IS_SPACE(*cptr))&&(*cptr != '\0'))
- {
- cptr++;
- wlen++;
- }
- if (wlen)
- {
- LO_TextStruct tmp_text;
-
- memset (&tmp_text, 0, sizeof (tmp_text));
-
- /*
- * We removed space, move the string up
- * and recalculate its current width.
- */
- XP_BCOPY(cptr, text_buf,
- (state->line_buf_len - wlen + 1));
- state->line_buf_len -= (intn) wlen;
- PA_UNLOCK(state->line_buf);
- tmp_text.text = state->line_buf;
- tmp_text.text_len = (int16)state->line_buf_len;
- tmp_text.text_attr =
- block->text_attr;
- FE_GetTextInfo(context, &tmp_text,
- &(state->text_info));
-
- /*
- * Override the saved width since we did want
- * to change the real width in this case.
- */
- save_width = lo_correct_text_element_width(
- &(state->text_info));
- }
- else
- {
- PA_UNLOCK(state->line_buf);
- }
- }
- }
- /*
- * We are breaking somewhere in the middle of an old
- * text element, this will mean splitting it into 2 text
- * elements, and putting a line break between them.
- */
- else
- {
- LO_TextInfo text_info;
- int32 base_change;
-
- PA_LOCK(text_buf, char *, text_data->text);
-
- /*
- * Locate our break location, and a pointer to
- * the remaining word (with its preceeding space removed).
- */
- break_ptr = (char *)(text_buf + state->old_break_pos);
- /*
- * On multibyte, we often break on character which is
- * not space.
- */
- if (multi_byte == FALSE || XP_IS_SPACE(*break_ptr))
- {
- *break_ptr = '\0';
- word_ptr = (char *)(break_ptr + 1);
- }
- else
- {
- word_ptr = (char *)break_ptr;
- }
-
- /*
- * Copy the remaining word into its own block.
- */
- word_len = XP_STRLEN(word_ptr);
- new_block = PA_ALLOC((word_len + 1));
- if (new_block == NULL)
- {
- state->top_state->out_of_memory = TRUE;
- return;
- }
- PA_LOCK(new_buf, char *, new_block);
- XP_BCOPY(word_ptr, new_buf, (word_len + 1));
- new_buf[word_len] = '\0';
-
- /*
- * Back the state up to this element's
- * location, break off the rest of the elements
- * and save them for later.
- * Flush this line, and insert a linebreak.
- */
- state->x = text_data->x;
- state->y = text_data->y;
- tptr = text_data->next;
- text_data->next = NULL;
- if (word_ptr != break_ptr)
- text_data->text_len = text_data->text_len - word_len - 1;
- else
- text_data->text_len = text_data->text_len - word_len;
- FE_GetTextInfo(context, text_data, &text_info);
- state->width = lo_correct_text_element_width(&text_info);
- PA_UNLOCK(text_data->text);
- state->x += state->width;
-
- /*
- * Make the split element know its new width.
- */
- text_data->width = state->width;
-
- /*
- * If the element that caused this break has a different
- * baseline than the element we are breaking, we need to
- * preserve that difference after the break.
- */
- base_change = state->baseline -
- (text_data->y_offset + text_info.ascent);
-
- old_baseline = state->baseline;
- old_line_height = state->line_height;
-
- /*
- * Reset element_id so they are still sequencial.
- */
- state->top_state->element_id = text_data->ele_id + 1;
-
- /*
- * If we are breaking an anchor, we need to make sure the
- * linefeed gets its anchor href set properly.
- */
- if (text_data->anchor_href != NULL)
- {
- LO_AnchorData *tmp_anchor;
-
- tmp_anchor = state->current_anchor;
- state->current_anchor = text_data->anchor_href;
- lo_SoftLineBreak(context, state, TRUE);
- state->current_anchor = tmp_anchor;
- }
- else
- {
- lo_SoftLineBreak(context, state, TRUE);
- }
-
- adjust = lo_baseline_adjust(context, state, tptr,
- old_baseline, old_line_height);
- state->baseline = old_baseline - adjust;
- state->line_height = (intn) old_line_height - adjust;
-
- /*
- * If there was really no remaining word, free the
- * unneeded buffer.
- */
- if (word_len == 0)
- {
- LO_Element *eptr;
- LO_Element *line_ptr;
-
- PA_UNLOCK(new_block);
- PA_FREE(new_block);
-
- 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)
- {
- state->line_list = tptr;
- if (tptr != NULL)
- {
- tptr->lo_any.prev = NULL;
- }
- }
- else
- {
- line_ptr->lo_any.next = tptr;
- if (tptr != NULL)
- {
- tptr->lo_any.prev = line_ptr;
- }
- }
-
- state->width = 0;
-
- eptr = tptr;
- while (eptr != NULL)
- {
- eptr->lo_any.ele_id = NEXT_ELEMENT;
- eptr->lo_any.y_offset -= adjust;
- eptr = eptr->lo_any.next;
- }
- }
- /*
- * Else create a new text element for the remaining word.
- * and stick it in the begining of the next line of
- * text elements.
- */
- else
- {
- LO_Element *line_ptr;
- LO_Element *eptr;
- int32 baseline_inc;
- LO_TextInfo text_info;
- baseline_inc = -1 * adjust;
- new_text_data = lo_new_text_element(context, state,
- text_data->edit_element,
- text_data->edit_offset+text_data->text_len+1 );
- if (new_text_data == NULL)
- {
- return;
- }
- if (new_text_data->text != NULL)
- {
- PA_FREE(new_text_data->text);
- new_text_data->text = NULL;
- new_text_data->text_len = 0;
- }
- new_text_data->anchor_href = text_data->anchor_href;
- new_text_data->text_attr = text_data->text_attr;
- new_text_data->x = state->x;
- new_text_data->y = state->y;
-
- #ifdef LOCAL_DEBUG
- XP_TRACE(("lo_BreakOldElement, left over word (%s)\n", new_buf));
- #endif /* LOCAL_DEBUG */
-
- PA_UNLOCK(new_block);
- new_text_data->text = new_block;
- new_text_data->text_len = word_len;
- FE_GetTextInfo(context, new_text_data, &text_info);
- new_text_data->width =
- lo_correct_text_element_width(&text_info);
-
- /*
- * Some fonts (particulatly italic ones with curly
- * tails on letters like 'f') have a left bearing
- * that extends back into the previous character.
- * Since in this case the previous character is
- * probably not in the same font, we move forward
- * to avoid overlap.
- */
- if (text_info.lbearing < 0)
- {
- new_text_data->x_offset =
- text_info.lbearing * -1;
- }
-
- /*
- * The baseline of the text element just inserted in
- * the line may be less than or greater than the
- * baseline of the rest of the line due to font
- * changes. If the baseline is less, this is easy,
- * we just increase y_offest to move the text down
- * so the baselines line up. For greater baselines,
- * we can't move the text up to line up the baselines
- * because we will overlay the previous line, so we
- * have to move all rest of the elements in this line
- * down.
- *
- * If the baseline is zero, we are the first element
- * on the line, and we get to set the baseline.
- */
- if (state->baseline == 0)
- {
- state->baseline = text_info.ascent;
- }
- else if (text_info.ascent < state->baseline)
- {
- new_text_data->y_offset = state->baseline -
- text_info.ascent;
- }
- else
- {
- baseline_inc = baseline_inc +
- (text_info.ascent - state->baseline);
- state->baseline =
- text_info.ascent;
- }
-
- /*
- * Now that we have broken, and added the new
- * element, we need to move it down to restore the
- * baseline difference that previously existed.
- */
- new_text_data->y_offset -= base_change;
-
- /*
- * Calculate the height of this new
- * text element.
- */
- new_text_data->height = text_info.ascent +
- text_info.descent;
- state->x += new_text_data->width;
-
- /*
- * Stick this new text element at the beginning
- * of the remaining line elements
- * There may be some special mail bullets already
- * on the line that we have to insert after.
- */
- 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)
- {
- state->line_list = (LO_Element *)new_text_data;
- new_text_data->prev = NULL;
- }
- else
- {
- line_ptr->lo_any.next =
- (LO_Element *)new_text_data;
- new_text_data->prev = line_ptr;
- }
- new_text_data->next = tptr;
- if (tptr != NULL)
- {
- tptr->lo_any.prev = (LO_Element *)new_text_data;
- }
-
- eptr = tptr;
-
- while (eptr != NULL)
- {
- eptr->lo_any.ele_id = NEXT_ELEMENT;
- eptr->lo_any.y_offset += baseline_inc;
- eptr = eptr->lo_any.next;
- }
-
- if ((new_text_data->y_offset + new_text_data->height) >
- state->line_height)
- {
- state->line_height = (intn) new_text_data->y_offset +
- new_text_data->height;
- }
-
- state->at_begin_line = FALSE;
- }
- }
-
- /*
- * If we are at the beginning of a line, and there is
- * remaining text to place here, remove leading space
- * which is not allowed at the start of lines.
- * ERIC, make a test case for this, I suspect right now
- * this code is never being executed and may contain an error.
- */
- if ((state->at_begin_line != FALSE)&&(tptr != NULL)&&
- (tptr->type == LO_TEXT))
- {
- char *cptr;
- int32 wlen;
- LO_TextStruct *tmp_text;
- LO_TextInfo text_info;
-
- tmp_text = (LO_TextStruct *)tptr;
-
- PA_LOCK(text_buf, char *, tmp_text->text);
- cptr = text_buf;
- wlen = 0;
- while ((XP_IS_SPACE(*cptr))&&(*cptr != '\0'))
- {
- cptr++;
- wlen++;
- }
- if (wlen)
- {
- XP_BCOPY(cptr, text_buf,
- (tmp_text->text_len - wlen + 1));
- tmp_text->text_len -= (intn) wlen;
-
- PA_UNLOCK(tmp_text->text);
- FE_GetTextInfo(context, tmp_text, &text_info);
- tmp_text->width = lo_correct_text_element_width(
- &text_info);
- }
- else
- {
- PA_UNLOCK(tmp_text->text);
- }
- }
-
- /*
- * Upgrade forward the x and y text positions in the document
- * state.
- */
- while (tptr != NULL)
- {
- lo_UpdateElementPosition ( state, tptr );
- tptr = tptr->lo_any.next;
- }
-
- state->at_begin_line = FALSE;
- state->width = save_width;
- }
-
-
- void lo_UpdateElementPosition ( lo_DocState * state, LO_Element * element )
- {
- element->lo_any.x = state->x;
- element->lo_any.y = state->y;
- state->x = state->x + element->lo_any.width;
-
- /* move any element specific items */
- switch ( element->lo_any.type )
- {
- case LO_IMAGE:
- CL_MoveLayer(element->lo_image.layer, element->lo_any.x, element->lo_any.y);
- break;
-
- case LO_EMBED:
- CL_MoveLayer(element->lo_embed.layer, element->lo_any.x, element->lo_any.y);
- break;
- }
- }
-
-
- /*************************************
- * Function: lo_correct_text_element_width
- *
- * Description: Calculate the correct width of this text element
- * if it is a complete element surrounded by elements of potentially
- * different fonts, so we have to take care not to truncate
- * any slanted characters at either end of the element.
- *
- * Params: LO_TextInfo structure for this text element's text string.
- *
- * Returns: The width this element would have if it stood alone.
- *************************************/
- int32
- lo_correct_text_element_width(LO_TextInfo *text_info)
- {
- int32 x_offset;
- int32 width;
-
- width = text_info->max_width;
- x_offset = 0;
-
- /*
- * For text that leans into the previous character.
- */
- if (text_info->lbearing < 0)
- {
- x_offset = text_info->lbearing * -1;
- width += x_offset;
- }
-
- /*
- * For text that leans right into the following characters.
- */
- if (text_info->rbearing > text_info->max_width)
- {
- width += (text_info->rbearing - text_info->max_width);
- }
-
- return(width);
- }
-
-
- PRIVATE
- int32
- lo_characters_in_line(lo_DocState *state)
- {
- int32 cnt;
- LO_Element *eptr;
-
- cnt = 0;
- eptr = state->line_list;
- while (eptr != NULL)
- {
- if (eptr->type == LO_TEXT)
- {
- cnt += eptr->lo_text.text_len;
- }
- eptr = eptr->lo_any.next;
- }
- return(cnt);
- }
-
- void
- lo_PreformatedText(MWContext *context, lo_DocState *state, char *text)
- {
- LO_TextBlock * block;
-
- block = lo_NewTextBlock ( context, state, text, state->preformatted );
- if ( !state->top_state->out_of_memory && ( block != NULL ) )
- {
- block->buffer_read_index = 0;
- lo_LayoutPreformattedText ( context, state, block );
- }
- }
-
-
- void
- lo_LayoutPreformattedText(MWContext *context, lo_DocState *state, LO_TextBlock * block)
- {
- char *tptr;
- char *w_start;
- char *w_end;
- char *text_buf;
- char tchar1;
- Bool have_CR;
- Bool line_break;
- Bool white_space;
- LO_TextStruct text_data;
- char *tmp_buf;
- PA_Block tmp_block;
- int32 tab_count, ignore_cnt, line_length;
- int16 charset;
- int multi_byte;
- int kinsoku_class, last_kinsoku_class;
- int i;
- int bytestocopy;
- char * text;
- Bool lineBufMeasured;
-
- /* start at the current text position in this text block */
- text = (char *) &block->text_buffer[ block->buffer_read_index ];
-
- kinsoku_class = PROHIBIT_NOWHERE;
-
- lineBufMeasured = FALSE;
-
- /*
- * Initialize the structures to 0 (mark)
- */
- memset (&text_data, 0, sizeof (LO_TextStruct));
-
- /*
- * Error conditions
- */
- if ((state == NULL)||(state->cur_ele_type != LO_TEXT)||(text == NULL))
- {
- return;
- }
-
- charset = block->text_attr->charset;
- multi_byte = (INTL_CharSetType(charset) != SINGLEBYTE);
-
- /*
- * Move through this text fragment, expand tabs, honor LF/CR.
- */
- have_CR = state->last_char_CR;
- tptr = text;
- while ((*tptr != '\0')&&(state->top_state->out_of_memory == FALSE))
- {
- Bool has_nbsp;
- Bool in_word;
- Bool wrap_break;
- Bool pre_wrap_break;
-
- /*
- * white_space is a tag to tell us if the current word
- * ends in whitespace.
- */
- white_space = FALSE;
- line_break = FALSE;
- has_nbsp = FALSE;
- if (multi_byte)
- has_nbsp = TRUE;
-
- /*
- * Find the end of the line, counting tabs.
- */
- tab_count = 0;
- ignore_cnt = 0;
- line_length = 0;
- w_start = tptr;
-
- /*
- * If the last character processed was a CR, and the next
- * char is a LF, ignore it. Otherwise we know we
- * can break the line on the first CR or LF found.
- */
- if ((have_CR != FALSE)&&(*tptr == LF))
- {
- ignore_cnt++;
- tptr++;
- }
- have_CR = FALSE;
-
- in_word = FALSE;
- wrap_break = FALSE;
- pre_wrap_break = FALSE;
-
- #ifdef XP_WIN16
- while ((*tptr != CR)&&(*tptr != LF)&&(*tptr != '\0')&&
- #ifdef TEXT_CHUNK_LIMIT
- ((line_length + (tab_count * state->tab_stop)) < TEXT_CHUNK_LIMIT)&&
- #endif /* TEXT_CHUNK_LIMIT */
- (((state->line_buf_len + line_length + (tab_count * state->tab_stop))) < SIZE_LIMIT))
- #else
- while ((*tptr != CR)&&(*tptr != LF)&&(*tptr != '\0')
- #ifdef TEXT_CHUNK_LIMIT
- &&((line_length + (tab_count * state->tab_stop)) < TEXT_CHUNK_LIMIT)
- #endif /* TEXT_CHUNK_LIMIT */
- )
- #endif /* XP_WIN16 */
- {
- /*
- * In the special wrapping preformatted text
- * we need to chunk by word instead of by
- * line.
- */
- if ((state->preformatted == PRE_TEXT_WRAP)||
- (state->preformatted == PRE_TEXT_COLS))
- {
- if(multi_byte && (! (INTL_CharSetType(charset) & CS_SPACE)))
- {
- last_kinsoku_class = kinsoku_class;
- kinsoku_class = INTL_KinsokuClass(charset, (unsigned char *)tptr);
- /* We need to conser PROHIBIT_WORD_BREAK for UTF8 case */
- if(( PROHIBIT_WORD_BREAK == kinsoku_class) || (0x00 == (*tptr & 0x80)))
- {
- if ((in_word == FALSE)&&(!XP_IS_SPACE(*tptr)) )
- {
- in_word = TRUE;
- }
- else if ((in_word != FALSE)&&
- (XP_IS_SPACE(*tptr)))
- {
- wrap_break = TRUE;
- break;
- }
- }
- else
- {
- if( (line_length != 0) &&
- (PROHIBIT_END_OF_LINE != last_kinsoku_class) &&
- (PROHIBIT_BEGIN_OF_LINE != kinsoku_class)
- )
- {
- wrap_break = TRUE;
- break;
- }
- }
- }
- else
- {
- if ((in_word == FALSE)&&(!XP_IS_SPACE(*tptr)))
- {
- in_word = TRUE;
- }
- else if ((in_word != FALSE)&&
- (XP_IS_SPACE(*tptr)))
- {
- wrap_break = TRUE;
- break;
- }
- }
- }
-
- if ((*tptr == FF)||(*tptr == VTAB))
- {
- /*
- * Ignore the form feeds
- * thrown in by some platforms.
- * Ignore vertical tabs since we don't know
- * what else to do with them.
- */
- ignore_cnt++;
- }
- else if (*tptr == TAB)
- {
- tab_count++;
- }
- else if (!multi_byte && (unsigned char)*tptr == NON_BREAKING_SPACE)
- {
- /* *tptr = ' '; Replace this later */
- has_nbsp = TRUE;
- line_length++;
- }
- else
- {
- if(multi_byte)
- line_length += INTL_CharLen(charset, (unsigned char*)tptr);
- else
- line_length++;
- }
-
- if(multi_byte)
- tptr = INTL_NextChar(charset, tptr);
- else
- tptr++;
- }
- line_length = line_length + (state->tab_stop * tab_count);
-
- #ifdef TEXT_CHUNK_LIMIT
- if ((state->line_buf_len + line_length) > TEXT_CHUNK_LIMIT)
- {
- lo_FlushLineBuffer(context, state);
- if (state->cur_ele_type != LO_TEXT)
- {
- lo_FreshText(state);
- state->cur_ele_type = LO_TEXT;
- }
- }
- #endif /* TEXT_CHUNK_LIMIT */
-
- #ifdef XP_WIN16
- if ((state->line_buf_len + line_length) >= SIZE_LIMIT)
- {
- line_break = TRUE;
- }
- #endif /* XP_WIN16 */
-
- /*
- * Terminate the word, saving the char we replaced
- * with the terminator so it can be restored later.
- */
- w_end = tptr;
- tchar1 = *w_end;
- *w_end = '\0';
-
- tmp_block = PA_ALLOC(line_length + 1);
- if (tmp_block == NULL)
- {
- *w_end = tchar1;
- state->top_state->out_of_memory = TRUE;
- break;
- }
- PA_LOCK(tmp_buf, char *, tmp_block);
-
- if ((tab_count)||(ignore_cnt))
- {
- char *cptr;
- char *text_ptr;
- int32 cnt;
-
- text_ptr = tmp_buf;
- cptr = w_start;
- cnt = lo_characters_in_line(state);
- cnt += state->line_buf_len;
- while (*cptr != '\0')
- {
- if ((*cptr == LF)||(*cptr == FF)||
- (*cptr == VTAB))
- {
- /*
- * Ignore any linefeeds that must have
- * been after CR, and form feeds.
- * Ignore vertical tabs since we
- * don't know what else to do with them.
- */
- cptr++;
- }
- else if (*cptr == TAB)
- {
- int32 i, tab_pos;
-
- tab_pos = ((cnt / state->tab_stop) +
- 1) * state->tab_stop;
- for (i=0; i<(tab_pos - cnt); i++)
- {
- *text_ptr++ = ' ';
- }
- cnt = tab_pos;
- cptr++;
- }
- else
- {
- /*
- * Bug #77467
- * If multibyte, character != char by default, so copy
- * the CHARACTER, not the char
- */
- if(multi_byte)
- {
- bytestocopy = INTL_CharLen(charset, (unsigned char*)cptr);
- for (i=0; i<bytestocopy; i++)
- {
- *text_ptr++ = *cptr++;
- cnt++;
- }
- }
- else
- {
- *text_ptr++ = *cptr++;
- cnt++;
- }
- }
- }
- *text_ptr = *cptr;
- }
- else
- {
- XP_BCOPY(w_start, tmp_buf, line_length + 1);
- }
-
- /*
- * Now we catch those nasty non-breaking space special
- * characters and make them spaces.
- */
- if (has_nbsp != FALSE)
- {
- char *tmp_ptr;
-
- tmp_ptr = tmp_buf;
- while (*tmp_ptr != '\0')
- {
- if (((unsigned char)*tmp_ptr == NON_BREAKING_SPACE)
- && (CS_USER_DEFINED_ENCODING != charset))
- {
- *tmp_ptr = ' ';
- }
- if(multi_byte)
- tmp_ptr = INTL_NextChar(charset, tmp_ptr);
- else
- tmp_ptr++;
- }
- }
-
- /* don't need this any more since we're converting
- * elsewhere -- erik
- tmp_buf = FE_TranslateISOText(context, charset, tmp_buf);
- */
- line_length = XP_STRLEN(tmp_buf);
-
- if ((line_length > 0)&&(XP_IS_SPACE(tmp_buf[line_length - 1])))
- {
- state->trailing_space = TRUE;
- }
- #ifdef LOCAL_DEBUG
- XP_TRACE(("Found Preformatted text (%s)\n", tmp_buf));
- #endif /* LOCAL_DEBUG */
- PA_UNLOCK(tmp_block);
-
- #if WHAT
- /*
- * If this is an empty string, just throw it out
- * and move on
- */
- if (*w_start == '\0')
- {
- *w_end = tchar1;
- #ifdef LOCAL_DEBUG
- XP_TRACE(("Throwing out empty string!\n"));
- #endif /* LOCAL_DEBUG */
- continue;
- }
- #endif
-
- /*
- * If we have extra text, Append it to the line buffer.
- * It may be necessary to expand the line
- * buffer.
- */
- if (*w_start != '\0')
- {
- int32 old_len;
- Bool old_begin_line;
-
- old_len = state->line_buf_len;
- old_begin_line = state->at_begin_line;
-
- if ((state->line_buf_len + line_length + 1) >
- state->line_buf_size)
- {
- state->line_buf = PA_REALLOC(
- state->line_buf, (state->line_buf_size +
- line_length + LINE_BUF_INC));
- if (state->line_buf == NULL)
- {
- *w_end = tchar1;
- state->top_state->out_of_memory = TRUE;
- break;
- }
- state->line_buf_size += (line_length +
- LINE_BUF_INC);
- }
- PA_LOCK(text_buf, char *, state->line_buf);
- PA_LOCK(tmp_buf, char *, tmp_block);
-
- XP_BCOPY(tmp_buf,
- (char *)(text_buf + state->line_buf_len),
- (line_length + 1));
- state->line_buf_len += (intn) line_length;
- PA_UNLOCK(state->line_buf);
- PA_UNLOCK(tmp_block);
-
- /* we have not measured this new text yet */
- lineBufMeasured = FALSE;
-
- /*
- * Having added text, we cannot be at the start
- * of a line
- */
- state->cur_ele_type = LO_TEXT;
- state->at_begin_line = FALSE;
-
- #ifdef OLD_WAY
- /*
- * Most common case is appending to the same line.
- * assume that is what we are doing here.
- */
- text_data.text = state->line_buf;
- text_data.text_len = (int16)state->line_buf_len;
- text_data.text_attr = block->text_attr;
- FE_GetTextInfo(context, &text_data,
- &(state->text_info));
- state->width =
- lo_correct_text_element_width(
- &(state->text_info));
-
- /*
- * If this is a special wrapping pre, and we would
- * wrap here, break before this, and strip all
- * following whitespace so there is none at
- * the start of the next line.
- * If we were at the beginning of the line before
- * this, then obviously trying to wrap here will
- * be pointless, and will in fact cause an
- * infinite loop.
- *
- * Also wrap here is we are in fixed column wrapping
- * pre text, and we would pass our set column.
- */
- if (((state->preformatted == PRE_TEXT_WRAP)&&
- (old_begin_line == FALSE)&&
- ((state->x + state->width) > state->right_margin))||
- ((state->preformatted == PRE_TEXT_COLS)&&
- (old_begin_line == FALSE)&&
- (state->preformat_cols > 0)&&
- (state->line_buf_len > state->preformat_cols)))
- {
- PA_LOCK(text_buf, char *, state->line_buf);
- text_buf[old_len] = '\0';
- PA_UNLOCK(state->line_buf);
- state->line_buf_len = old_len;
-
- *w_end = tchar1;
- tptr = w_start;
- while ((XP_IS_SPACE(*tptr))&&(*tptr != '\0'))
- {
- tptr++;
- }
- line_break = TRUE;
- w_start = tptr;
- w_end = tptr;
- tchar1 = *w_end;
- }
- #else
-
- text_data.text = state->line_buf;
- text_data.text_len = (int16)state->line_buf_len;
- text_data.text_attr = block->text_attr;
- FE_GetTextInfo ( context, &text_data, &(state->text_info) );
-
- /* update the block's font info cache */
- block->ascent = state->text_info.ascent;
- block->descent = state->text_info.descent;
-
- /*
- * If this is a special wrapping pre, then we need to measure this line of text to see
- * if we need to wrap.
- */
- if ( ( state->preformatted == PRE_TEXT_WRAP ) && ( old_begin_line == FALSE ) )
- {
- state->width = lo_correct_text_element_width ( &(state->text_info) );
-
- lineBufMeasured = TRUE;
-
- /*
- * If this line is now too long, wrap
- */
- if ( ( state->x + state->width ) > state->right_margin )
- {
- pre_wrap_break = TRUE;
- }
- }
-
- /*
- * Now check to see if we need to wrap based on being too long for the special pre modes
- */
- if ( ( pre_wrap_break != FALSE ) ||
- ( ( state->preformatted == PRE_TEXT_COLS ) &&
- ( old_begin_line == FALSE ) &&
- ( state->preformat_cols > 0 ) &&
- ( state->line_buf_len > state->preformat_cols ) ))
- {
- PA_LOCK(text_buf, char *, state->line_buf);
- text_buf[old_len] = '\0';
- PA_UNLOCK(state->line_buf);
- state->line_buf_len = old_len;
-
- *w_end = tchar1;
- tptr = w_start;
- while ((XP_IS_SPACE(*tptr))&&(*tptr != '\0'))
- {
- tptr++;
- }
- line_break = TRUE;
- w_start = tptr;
- w_end = tptr;
- tchar1 = *w_end;
- }
- #endif
- }
-
- if (tchar1 == LF)
- {
- line_break = TRUE;
- }
- else if (tchar1 == CR)
- {
- line_break = TRUE;
- have_CR = TRUE;
- }
-
- /* update the buffer position to refleft the new word */
- block->buffer_read_index = tptr - (char *) block->text_buffer;
-
- /*
- * If we are breaking the line here, flush the
- * line_buf, and then insert a linebreak.
- */
- if (line_break != FALSE)
- {
- #ifdef LOCAL_DEBUG
- XP_TRACE(("LineBreak, flush text.\n"));
- #endif /* LOCAL_DEBUG */
-
- /*
- * Flush the line and insert the linebreak.
- */
- PA_LOCK(text_buf, char *, state->line_buf);
- text_data.text = state->line_buf;
- text_data.text_len = (int16)state->line_buf_len;
- text_data.text_attr = block->text_attr;
- FE_GetTextInfo(context, &text_data,&(state->text_info));
- PA_UNLOCK(state->line_buf);
- state->width = lo_correct_text_element_width(
- &(state->text_info));
-
- lo_FlushLineBuffer(context, state);
-
- lineBufMeasured = TRUE;
- #ifdef EDITOR
- /* LTNOTE: do something here like: */
- /*state->edit_current_offset += (word_ptr - text_buf);*/
- #endif
- if (state->top_state->out_of_memory != FALSE)
- {
- PA_FREE(tmp_block);
- return;
- }
- /*
- * Put on a linefeed element.
- * This line is finished and will be added
- * to the line array.
- */
- lo_SoftLineBreak(context, state, TRUE);
-
- state->line_buf_len = 0;
- state->width = 0;
-
- /*
- * having just broken the line, we have no break
- * position.
- */
- state->break_pos = -1;
- state->break_width = 0;
- }
-
- *w_end = tchar1;
- if ((*tptr == CR)||(*tptr == LF))
- {
- tptr++;
- }
- PA_FREE(tmp_block);
- }
-
- #ifndef OLD_WAY
- /*
- * If we haven't measured this line of text yet, do so now
- */
- if ( !lineBufMeasured )
- {
- text_data.text = state->line_buf;
- text_data.text_len = (int16)state->line_buf_len;
- text_data.text_attr = block->text_attr;
- FE_GetTextInfo ( context, &text_data, &(state->text_info) );
- state->width = lo_correct_text_element_width ( &(state->text_info) );
- }
- #endif
-
- /*
- * Because we just might get passed text broken between the
- * CR and the LF, we need to save this state.
- */
- if ((tptr > text)&&(*(tptr - 1) == CR))
- {
- state->last_char_CR = TRUE;
- }
-
- if ( ( state->cur_ele_type != LO_TEXT ) || ( state->line_buf_len == 0 ) )
- {
- state->cur_text_block = NULL;
- }
- }
-
- #define CAPITALIZE 0
- #define UPPERCASE 1
- #define LOWERCASE 2
-
- /* transform the text inline.
- * "capitalize" : uppercase first letter of each word.
- * "uppercase" : uppercase every letter
- * "lowercase" : lowercase every letter
- * else : do nothing
- */
- PRIVATE void
- lo_transform_text(char *ptr, int method)
- {
- XP_Bool possible_first_letter = TRUE;
-
- for(; *ptr; ptr++)
- {
- switch(method)
- {
- case CAPITALIZE:
- if(!(XP_IS_SPACE(*ptr)))
- {
- if(possible_first_letter)
- {
- *ptr = XP_TO_UPPER(*ptr);
- possible_first_letter = FALSE;
- }
- }
- else /* is a space */
- {
- possible_first_letter = TRUE;
- }
- break;
-
- case UPPERCASE:
- *ptr = XP_TO_UPPER(*ptr);
- break;
-
- case LOWERCASE:
- *ptr = XP_TO_LOWER(*ptr);
- break;
-
- default:
- XP_ASSERT(0);
-
- }
- }
- }
-
- /* see lo_transform_text
- *
- * This function just maps the string method to an int
- */
- PRIVATE void
- lo_transform_text_from_string_method(char *ptr, char *method)
- {
- if(!strcasecomp(method, "capitalize"))
- lo_transform_text(ptr, CAPITALIZE);
- else if(!strcasecomp(method, "lowercase"))
- lo_transform_text(ptr, LOWERCASE);
- else if(!strcasecomp(method, "uppercase"))
- lo_transform_text(ptr, UPPERCASE);
- }
-
- /*************************************
- * Function: lo_FormatText
- *
- * Description: This function creates a text block element and then calls the format
- * text function for it.
- *
- * Params: Window context and document state., and the text to be formatted.
- *
- * Returns: Nothing
- *************************************/
- void
- lo_FormatText(MWContext *context, lo_DocState *state, char *text)
- {
- LO_TextBlock * block;
-
- /* can we use the new style layout? */
- if ( lo_CanUseBreakTable ( state ) )
- {
- block = state->cur_text_block;
-
- /* flush any existing text in a partial buffer */
- if ( ( block != NULL ) && lo_UseBreakTable ( block ) )
- {
- lo_LayoutTextBlock ( context, state, TRUE );
- }
-
- /* parse the new text and flush all but any stragglers */
- lo_AppendTextToBlock ( context, state, NULL, text );
- lo_LayoutTextBlock ( context, state, FALSE );
- }
- else
- {
- block = lo_NewTextBlock ( context, state, text, state->preformatted );
- if ( !state->top_state->out_of_memory && ( block != NULL ) )
- {
- block->buffer_read_index = 0;
- lo_LayoutFormattedText ( context, state, block );
- }
- }
- }
-
-
- /*************************************
- * Function: lo_FormatText
- *
- * Description: This function formats text by breaking it into lines
- * at word boundries. Word boundries are whitespace, or special
- * word break tags.
- *
- * Params: Window context and document state., and the text to be formatted.
- *
- * Returns: Nothing
- *************************************/
- void
- lo_LayoutFormattedText(MWContext *context, lo_DocState *state, LO_TextBlock * block)
- {
- char *tptr;
- char *w_start;
- char *w_end;
- char *text_buf;
- char tchar1;
- int32 word_len;
- Bool line_break;
- Bool word_break;
- Bool prev_word_breakable;
- Bool white_space;
- int16 charset;
- Bool multi_byte;
- LO_TextStruct text_data;
- char * text;
-
- /* start at the current text position in this text block */
- text = (char *) &block->text_buffer[ block->buffer_read_index ];
-
-
- #ifdef XP_OS2 /* performance */
- int32 maxw; /* performance - max char width for font */
- int32 estwidth; /* performance - estimated width for line */
- int textsw; /* performance */
- maxw = 0;
- textsw = 0; /* performance - need to mark no width taken */
- #endif
-
-
- /*
- * Initialize the structures to 0 (mark)
- */
- memset (&text_data, 0, sizeof (LO_TextStruct));
-
- /*
- * Error conditions
- */
- if ((state == NULL)||(state->cur_ele_type != LO_TEXT)||(text == NULL))
- {
- return;
- }
-
- charset = block->text_attr->charset;
- if ((INTL_CharSetType(charset) == SINGLEBYTE) ||
- (INTL_CharSetType(charset) & CS_SPACE))
- {
- multi_byte = FALSE;
- }
- else
- {
- multi_byte = TRUE;
- }
-
- /*
- * Move through this text fragment, breaking it up into
- * words, and then grouping the words into lines.
- */
- tptr = text;
- prev_word_breakable = FALSE;
- while ((*tptr != '\0')&&(state->top_state->out_of_memory == FALSE))
- {
- PA_Block nbsp_block;
- Bool has_nbsp;
- #ifdef TEXT_CHUNK_LIMIT
- int32 w_char_cnt;
- #endif /* TEXT_CHUNK_LIMIT */
- #ifdef XP_WIN16
- int32 ccnt;
- #endif /* XP_WIN16 */
- Bool mb_sp; /* Allow space between multibyte */
-
- /*
- * white_space is a tag to tell us if the currenct word
- * contains nothing but whitespace.
- * word_break tells us if there was whitespace
- * before this word so we know we can break it.
- */
- white_space = FALSE;
- word_break = FALSE;
- line_break = FALSE;
- nbsp_block = NULL;
- has_nbsp = FALSE;
- mb_sp = FALSE;
-
- if (multi_byte == FALSE)
- {
- /* check for textTransform properties and apply them */
- if(state->top_state && state->top_state->style_stack)
- {
- char *property;
- StyleStruct *style_struct = STYLESTACK_GetStyleByIndex(
- state->top_state->style_stack,
- 0);
-
- if(style_struct)
- {
- property = STYLESTRUCT_GetString(style_struct,
- TEXT_TRANSFORM_STYLE);
-
- if(property)
- {
- lo_transform_text_from_string_method(tptr, property);
- }
- }
- }
-
- /*
- * Find the start of the word, skipping whitespace.
- */
- w_start = tptr;
- while ((XP_IS_SPACE(*tptr))&&(*tptr != '\0'))
- {
- tptr++;
- }
-
- /*
- * if tptr has been moved at all, that means
- * there was some whitespace to skip, which means
- * we are allowed to put a linebreak before this
- * word if we want to.
- */
- if (tptr != w_start)
- {
- int32 new_break_holder;
- int32 min_width;
- int32 indent;
-
- w_start = tptr;
- word_break = TRUE;
-
- new_break_holder = state->x + state->width;
- min_width = new_break_holder - state->break_holder;
- indent = state->list_stack->old_left_margin -
- state->win_left;
- min_width += indent;
- if (min_width > state->min_width)
- {
- state->min_width = min_width;
- }
- /* If we are not within <NOBR> content, allow break_holder
- * to be set to the new position where a line break can occur.
- * This fixes BUG #70782
- */
- if (state->breakable != FALSE) {
- state->break_holder = new_break_holder;
- }
- }
-
- /*
- * If we are in text that is supposed to be
- * justified, we want each word to be in a separate
- * text element, so if we just found a word break,
- * and there is already a word in the line buffer,
- * flush that word into its own element.
- */
- if ((state->align_stack != NULL)&&
- (state->align_stack->alignment ==LO_ALIGN_JUSTIFY)&&
- (word_break != FALSE)&&
- (state->cur_ele_type == LO_TEXT)&&
- (state->line_buf_len != 0))
- {
- /* set the current text offset for this new word */
- block->buffer_read_index = tptr - (char *) block->text_buffer;
-
- lo_FlushLineBuffer(context, state);
- if (state->top_state->out_of_memory != FALSE)
- {
- return;
- }
- }
-
- /*
- * Find the end of the word.
- * Terminate the word, saving the char we replaced
- * with the terminator so it can be restored later.
- */
- #ifdef TEXT_CHUNK_LIMIT
- w_char_cnt = 0;
- #endif /* TEXT_CHUNK_LIMIT */
- #ifdef XP_WIN16
- ccnt = state->line_buf_len;
- while ((!XP_IS_SPACE(*tptr))&&(*tptr != '\0')&&(ccnt < SIZE_LIMIT))
- {
- if ((unsigned char)*tptr == NON_BREAKING_SPACE)
- {
- has_nbsp = TRUE;
- }
- tptr++;
- #ifdef TEXT_CHUNK_LIMIT
- w_char_cnt++;
- #endif /* TEXT_CHUNK_LIMIT */
- ccnt++;
- }
- if (ccnt >= SIZE_LIMIT)
- {
- line_break = TRUE;
- }
- #else
- while ((!XP_IS_SPACE(*tptr))&&(*tptr != '\0'))
- {
- if ((unsigned char)*tptr == NON_BREAKING_SPACE)
- {
- /* *tptr = ' '; Replace this later */
- has_nbsp = TRUE;
- }
- tptr++;
- #ifdef TEXT_CHUNK_LIMIT
- w_char_cnt++;
- #endif /* TEXT_CHUNK_LIMIT */
- }
- #endif /* XP_WIN16 */
- }
- else
- {
- has_nbsp = TRUE;
- /*
- * Find the start of the word, skipping whitespace.
- */
- w_start = tptr;
- while ((XP_IS_SPACE(*tptr))&&(*tptr != '\0'))
- {
- tptr = INTL_NextChar(charset, tptr);
- }
- if (w_start != tptr)
- mb_sp = TRUE;
-
- /*
- * if tptr has been moved at all, that means
- * there was some whitespace to skip, which means
- * we are allowed to put a linebreak before this
- * word if we want to.
- */
- /*
- * If this char is a two-byte thing, we can break
- * before it.
- */
- if ((tptr != w_start)||((unsigned char)*tptr > 127))
- {
- int32 new_break_holder;
- int32 min_width;
- int32 indent;
-
- /* If it's multibyte character, it always be able to break */
- if (tptr == w_start)
- prev_word_breakable = TRUE;
- w_start = tptr;
- word_break = TRUE;
-
- new_break_holder = state->x + state->width;
- min_width = new_break_holder - state->break_holder;
- indent = state->list_stack->old_left_margin -
- state->win_left;
- min_width += indent;
- if (min_width > state->min_width)
- {
- state->min_width = min_width;
- }
- /* If we are not within <NOBR> content, allow break_holder
- * to be set to the new position where a line break can occur.
- * This fixes BUG #70782
- */
- if (state->breakable != FALSE) {
- state->break_holder = new_break_holder;
- }
- }
- else if (prev_word_breakable)
- {
- int32 new_break_holder;
- int32 min_width;
- int32 indent;
-
- prev_word_breakable = FALSE;
- w_start = tptr;
- word_break = TRUE;
-
- new_break_holder = state->x + state->width;
- min_width = new_break_holder - state->break_holder;
- indent = state->list_stack->old_left_margin -
- state->win_left;
- min_width += indent;
- if (min_width > state->min_width)
- {
- state->min_width = min_width;
- }
- /* If we are not within <NOBR> content, allow break_holder
- * to be set to the new position where a line break can occur.
- * This fixes BUG #70782
- */
- if (state->breakable != FALSE) {
- state->break_holder = new_break_holder;
- }
- }
-
- /*
- * Find the end of the word.
- * Terminate the word, saving the char we replaced
- * with the terminator so it can be restored later.
- */
- #ifdef TEXT_CHUNK_LIMIT
- w_char_cnt = 0;
- #endif /* TEXT_CHUNK_LIMIT */
- #ifdef XP_WIN16
- ccnt = state->line_buf_len;
- while (( ((unsigned char)*tptr < 128)
- || (INTL_KinsokuClass(charset, (unsigned char *)tptr) == PROHIBIT_WORD_BREAK )
- ) && (!XP_IS_SPACE(*tptr))
- && (*tptr != '\0')
- && (ccnt < SIZE_LIMIT))
- {
- intn c_len;
- char *tptr2;
-
- tptr2 = INTL_NextChar(charset, tptr);
- c_len = (intn)(tptr2 - tptr);
- tptr = tptr2;
- #ifdef TEXT_CHUNK_LIMIT
- w_char_cnt += c_len;
- #endif /* TEXT_CHUNK_LIMIT */
- ccnt += c_len;
- }
- if (ccnt >= SIZE_LIMIT)
- {
- line_break = TRUE;
- }
- #else
-
- #if 0
- while ( /* Change the order so we have better performance */
- (*tptr != '\0')
- && (!XP_IS_SPACE(*tptr))
- && ( ((unsigned char)*tptr < 128)
- || ((CS_UTF8 == charset) /* hack, since we know only CS_UTF8 have PROHIBIT_WORD_BREAK*/
- && (INTL_KinsokuClass(charset, (unsigned char *)tptr) == PROHIBIT_WORD_BREAK ))
- )
- )
- #else
- while (
- (*tptr != '\0')
- && (!XP_IS_SPACE(*tptr))
- && ( ((unsigned char)*tptr < 128)
- || ((CS_UTF8 == charset) && (*(unsigned char *)tptr <= 0xE2))
- /* In case of CS_UTF8, some code range like CJK character baundary is breakable.
- * While in range UCS2 < 0x2000 (roman), character baundary is not breakable.
- */
- )
- )
- #endif
- {
- intn c_len;
- char *tptr2;
-
- tptr2 = INTL_NextChar(charset, tptr);
- c_len = (intn)(tptr2 - tptr);
- tptr = tptr2;
- #ifdef TEXT_CHUNK_LIMIT
- w_char_cnt += c_len;
- #endif /* TEXT_CHUNK_LIMIT */
- }
- #endif /* XP_WIN16 */
- } /* multi byte */
-
- #ifdef TEXT_CHUNK_LIMIT
- if (w_char_cnt > TEXT_CHUNK_LIMIT)
- {
- tptr = (char *)(tptr - (w_char_cnt - TEXT_CHUNK_LIMIT));
- w_char_cnt = TEXT_CHUNK_LIMIT;
- }
-
- if ((state->line_buf_len + w_char_cnt) > TEXT_CHUNK_LIMIT)
- {
- lo_FlushLineBuffer(context, state);
- if (state->top_state->out_of_memory != FALSE)
- {
- return;
- }
-
- if (state->cur_ele_type != LO_TEXT)
- {
- lo_FreshText(state);
- state->cur_ele_type = LO_TEXT;
- }
- }
- #endif /* TEXT_CHUNK_LIMIT */
- if (multi_byte != FALSE)
- {
- if ((w_start == tptr)&&((unsigned char)*tptr > 127))
- {
- tptr = INTL_NextChar(charset, tptr);
- }
- }
- w_end = tptr;
- tchar1 = *w_end;
- *w_end = '\0';
-
- /*
- * If the "word" is just an empty string, this
- * is just whitespace that we may wish to compress out.
- */
- if (*w_start == '\0')
- {
- white_space = TRUE;
- }
-
- /*
- * compress out whitespace if the last word added was also
- * whitespace.
- */
- if ((white_space != FALSE)&&(state->trailing_space != FALSE))
- {
- *w_end = tchar1;
- #ifdef LOCAL_DEBUG
- XP_TRACE(("Discarding(%s)\n", w_start));
- #endif /* LOCAL_DEBUG */
- continue;
- }
-
- /*
- * This places the preceeding space in front of
- * separate words on a line.
- * Unecessary if last item was trailng space.
- *
- * If there was a word break before this word, so we know it
- * was supposed to be separate, and if we are not at the
- * beginning of the line, and if the
- * preceeding word is not already whitespace, then add
- * a space before this word.
- */
- if ((word_break != FALSE)&&
- (state->at_begin_line == FALSE)&&
- (state->trailing_space == FALSE))
- {
- /*
- * Since word_break is true, we know
- * we skipped some spaces previously
- * so we know there is space to back up
- * the word pointer inside the buffer.
- */
- if ((multi_byte == FALSE)||mb_sp)
- {
- w_start--;
- *w_start = ' ';
- }
-
- /*
- * If we are formatting breakable text
- * set break position to be just before this word.
- * This is where we will break this line if the
- * new word makes it too long.
- */
- if (state->breakable != FALSE)
- {
- state->break_pos = state->line_buf_len;
- state->break_width = state->width;
- }
- }
-
- #ifdef LOCAL_DEBUG
- XP_TRACE(("Found Word (%s)\n", w_start));
- #endif /* LOCAL_DEBUG */
- /*
- * If this is an empty string, just throw it out
- * and move on
- */
- if (*w_start == '\0')
- {
- *w_end = tchar1;
- #ifdef LOCAL_DEBUG
- XP_TRACE(("Throwing out empty string!\n"));
- #endif /* LOCAL_DEBUG */
- continue;
- }
-
- /*
- * Now we catch those nasty non-breaking space special
- * characters and make them spaces. Yuck, so that
- * relayout in tables will still see the non-breaking
- * spaces, we need to copy the buffer here.
- */
- if (has_nbsp != FALSE)
- {
- char *tmp_ptr;
- char *to_ptr;
- char *tmp_buf;
-
- nbsp_block = PA_ALLOC(XP_STRLEN(w_start) + 1);
- if (nbsp_block == NULL)
- {
- *w_end = tchar1;
- state->top_state->out_of_memory = TRUE;
- break;
- }
- PA_LOCK(tmp_buf, char *, nbsp_block);
-
- tmp_ptr = w_start;
- to_ptr = tmp_buf;
- while (*tmp_ptr != '\0')
- {
- *to_ptr = *tmp_ptr;
- if (((unsigned char)*to_ptr == NON_BREAKING_SPACE)
- && (CS_USER_DEFINED_ENCODING != charset))
- {
- *to_ptr = ' ';
- }
- if(multi_byte) {
- int i;
- int len = INTL_CharLen(charset,
- (unsigned char *)tmp_ptr);
- to_ptr++;
- tmp_ptr++;
- for (i=1; (i<len) && (*tmp_ptr != '\0'); i++) {
- *to_ptr++ = *tmp_ptr++;
- }
- }
- else {
- to_ptr++;
- tmp_ptr++;
- }
- }
- *w_end = tchar1;
- w_start = tmp_buf;
- w_end = to_ptr;
- *w_end = '\0';
- }
-
- /*
- * Make this Front End specific text, and count
- * the length of the word.
- */
- /* don't need this any more since we're converting
- * elsewhere -- erik
- w_start = FE_TranslateISOText(context, charset, w_start);
- */
- word_len = XP_STRLEN(w_start);
-
- /*
- * Append this word to the line buffer.
- * It may be necessary to expand the line
- * buffer.
- */
- if ((state->line_buf_len + word_len + 1) >
- state->line_buf_size)
- {
- state->line_buf = PA_REALLOC( state->line_buf,
- (state->line_buf_size +
- word_len + LINE_BUF_INC));
- if (state->line_buf == NULL)
- {
- *w_end = tchar1;
- if ((has_nbsp != FALSE)&&(nbsp_block != NULL))
- {
- PA_UNLOCK(nbsp_block);
- PA_FREE(nbsp_block);
- nbsp_block = NULL;
- }
- state->top_state->out_of_memory = TRUE;
- break;
- }
- state->line_buf_size += (word_len + LINE_BUF_INC);
- }
- PA_LOCK(text_buf, char *, state->line_buf);
- XP_BCOPY(w_start,
- (char *)(text_buf + state->line_buf_len),
- (word_len + 1));
- state->line_buf_len += word_len;
- PA_UNLOCK(state->line_buf);
-
- /*
- * Having added a word, we cannot be at the start of a line
- */
- state->cur_ele_type = LO_TEXT;
- state->at_begin_line = FALSE;
-
- /*
- * Most common case is appending to the same line.
- * assume that is what we are doing here.
- */
- text_data.text = state->line_buf;
- text_data.text_len = (int16)state->line_buf_len;
- text_data.text_attr = state->cur_text_block->text_attr;
- FE_GetTextInfo(context, &text_data, &(state->text_info));
- state->width =
- lo_correct_text_element_width(&(state->text_info));
-
- /* udpate the block's font info cache */
- block->ascent = state->text_info.ascent;
- block->descent = state->text_info.descent;
-
- /*
- * Set line_break based on document window width
- */
- #ifdef XP_WIN16
- if (((state->x + state->width) > state->right_margin)||(line_break != FALSE))
- #else
- if ((state->x + state->width) > state->right_margin)
- #endif /* XP_WIN16 */
- {
- /*
- * INTL kinsoku line break, some of characters are not allowed to put
- * in the end of line or beginning of line
- */
- if (multi_byte && (state->break_pos != -1))
- {
- int cur_wordtype, pre_wordtype, pre_break_pos;
- cur_wordtype = INTL_KinsokuClass(charset, (unsigned char *) w_start);
-
- PA_LOCK(text_buf, char *, state->line_buf);
- pre_break_pos = INTL_PrevCharIdx(charset,
- (unsigned char *)text_buf, state->break_pos);
- pre_wordtype = INTL_KinsokuClass(charset,
- (unsigned char *)(text_buf + pre_break_pos));
-
- if (pre_wordtype == PROHIBIT_END_OF_LINE ||
- (cur_wordtype == PROHIBIT_BEGIN_OF_LINE &&
- XP_IS_ALPHA(*(text_buf+pre_break_pos)) == FALSE))
- state->break_pos = pre_break_pos;
-
- PA_UNLOCK(state->line_buf);
- }
- line_break = TRUE;
- }
- else
- {
- line_break = FALSE;
- }
-
- /*
- * We cannot break a line if we have no break positions.
- * Usually happens with a single line of unbreakable text.
- */
- if ((line_break != FALSE)&&(state->break_pos == -1))
- {
- /*
- * It may be possible to break a previous
- * text element on the same line.
- */
- if (state->old_break_pos != -1)
- {
- lo_BreakOldElement(context, state);
- line_break = FALSE;
- }
- #ifdef XP_WIN16
- else if (ccnt >= SIZE_LIMIT)
- {
- state->break_pos = state->line_buf_len - 1;
- }
- else
- {
- line_break = FALSE;
- }
- #else
- else
- {
- line_break = FALSE;
- }
- #endif /* XP_WIN16 */
- }
-
- /*
- * If we are breaking the line here, flush the
- * line_buf, and then insert a linebreak.
- */
- if (line_break != FALSE)
- {
- char *break_ptr;
- char *word_ptr;
- char *new_buf;
- PA_Block new_block;
- #ifdef LOCAL_DEBUG
- XP_TRACE(("LineBreak, flush text.\n"));
- #endif /* LOCAL_DEBUG */
-
- /*
- * Find the breaking point, and the pointer
- * to the remaining word without its leading
- * space.
- */
- PA_LOCK(text_buf, char *, state->line_buf);
- break_ptr = (char *)(text_buf + state->break_pos);
- /* word_ptr = (char *)(break_ptr + 1); */
- word_ptr = break_ptr;
-
- if ((multi_byte == FALSE)||mb_sp)
- {
- word_ptr++;
- }
-
- /*
- * Copy the remaining word into its
- * own buffer.
- */
- word_len = XP_STRLEN(word_ptr);
- new_block = PA_ALLOC((word_len + 1) *
- sizeof(char));
- if (new_block == NULL)
- {
- PA_UNLOCK(state->line_buf);
- *w_end = tchar1;
- if ((has_nbsp != FALSE)&&(nbsp_block != NULL))
- {
- PA_UNLOCK(nbsp_block);
- PA_FREE(nbsp_block);
- nbsp_block = NULL;
- }
- state->top_state->out_of_memory = TRUE;
- break;
- }
- PA_LOCK(new_buf, char *, new_block);
- XP_BCOPY(word_ptr, new_buf, (word_len + 1));
-
- *break_ptr = '\0';
- state->line_buf_len = state->line_buf_len -
- word_len;
- if ((multi_byte == FALSE)||(word_ptr != break_ptr))
- {
- state->line_buf_len--;
- }
- text_data.text = state->line_buf;
- text_data.text_len = (int16)state->line_buf_len;
- text_data.text_attr = state->cur_text_block->text_attr;
- FE_GetTextInfo(context, &text_data,&(state->text_info));
- PA_UNLOCK(state->line_buf);
- state->width = lo_correct_text_element_width(
- &(state->text_info));
-
- lo_FlushLineBuffer(context, state);
- #ifdef EDITOR
- state->edit_current_offset += (word_ptr - text_buf);
- #endif
- if (state->top_state->out_of_memory != FALSE)
- {
- PA_UNLOCK(new_block);
- PA_FREE(new_block);
- if ((has_nbsp != FALSE)&&(nbsp_block != NULL))
- {
- PA_UNLOCK(nbsp_block);
- PA_FREE(nbsp_block);
- nbsp_block = NULL;
- }
- return;
- }
-
- /*
- * Put on a linefeed element.
- * This line is finished and will be added
- * to the line array.
- */
- lo_SoftLineBreak(context, state, TRUE);
-
- /*
- * If there was no remaining word, free up
- * the unnecessary buffer, and empty out
- * the line buffer.
- */
- if (word_len == 0)
- {
- PA_UNLOCK(new_block);
- PA_FREE(new_block);
- state->line_buf_len = 0;
- state->width = 0;
- }
- else
- {
- PA_LOCK(text_buf, char *,state->line_buf);
- XP_BCOPY(new_buf, text_buf, (word_len + 1));
- PA_UNLOCK(state->line_buf);
- PA_UNLOCK(new_block);
- PA_FREE(new_block);
- state->line_buf_len = word_len;
- text_data.text = state->line_buf;
- text_data.text_len = (int16)state->line_buf_len;
- text_data.text_attr =
- state->cur_text_block->text_attr;
- FE_GetTextInfo(context, &text_data,
- &(state->text_info));
- state->width = lo_correct_text_element_width(
- &(state->text_info));
-
- /*
- * Having added text, we are no longer at the
- * start of the line.
- */
- state->at_begin_line = FALSE;
- state->cur_ele_type = LO_TEXT;
- }
-
-
- /*
- * having just broken the line, we have no break
- * position.
- */
- state->break_pos = -1;
- state->break_width = 0;
- }
- else
- {
- /* this word fits, so update the text buffer position */
- block->buffer_read_index = tptr - (char *) block->text_buffer;
-
- if (white_space != FALSE)
- {
- state->trailing_space = TRUE;
- }
- else
- {
- state->trailing_space = FALSE;
- }
- }
-
- *w_end = tchar1;
- /*
- * Free up the extra block used for non-breaking
- * spaces if we had to allocate one.
- */
- if ((has_nbsp != FALSE)&&(nbsp_block != NULL))
- {
- PA_UNLOCK(nbsp_block);
- PA_FREE(nbsp_block);
- nbsp_block = NULL;
- }
- }
- /*
- * if last char is multibyte, break position need to be set to end of string
- */
- if (multi_byte != FALSE && *tptr == '\0' && prev_word_breakable != FALSE)
- state->break_pos = state->line_buf_len;
-
- if ( ( state->cur_ele_type != LO_TEXT ) || ( state->line_buf_len == 0 ) )
- {
- state->cur_text_block = NULL;
- }
- }
-
-
- /*************************************
- * Function: lo_FlushLineBuffer
- *
- * Description: Flush out the current line buffer of text
- * into a new text element, and add that element to
- * the end of the line list of elements.
- *
- * Params: Window context and document state.
- *
- * Returns: Nothing
- *************************************/
- void
- lo_FlushLineBuffer(MWContext *context, lo_DocState *state)
- {
- LO_TextStruct *text_data;
- int32 baseline_inc;
- LO_TextBlock * block;
-
- baseline_inc = 0;
- #ifdef DEBUG
- assert (state);
- #endif
-
- block = state->cur_text_block;
-
- /* bail if we have nothing to do with text */
- if ( ( block == NULL ) || ( state->cur_ele_type != LO_TEXT ) )
- {
- return;
- }
-
- /*
- * If we're currently using the new break table layout, then bail to it
- */
- if ( lo_UseBreakTable ( block ) )
- {
- lo_FlushText ( context, state );
- return;
- }
-
- /*
- * Make sure we have some text to flush
- */
- if ( state->line_buf_len == 0 )
- {
- return;
- }
-
- /*
- * LTNOTE: probably should be grabbing state edit_element and offset from
- * state.
- */
- text_data = lo_new_text_element(context, state, NULL, 0);
-
- if (text_data == NULL)
- {
- state->top_state->out_of_memory = TRUE;
- return;
- }
- state->linefeed_state = 0;
-
- /*
- * Some fonts (particulatly italic ones with curly tails
- * on letters like 'f') have a left bearing that extends
- * back into the previous character. Since in this case the
- * previous character is probably not in the same font, we
- * move forward to avoid overlap.
- *
- * Those same funny fonts can extend past the last character,
- * and we also have to catch that, and advance the following text
- * to eliminate cutoff.
- */
- if (state->text_info.lbearing < 0)
- {
- text_data->x_offset = state->text_info.lbearing * -1;
- }
- text_data->width = state->width;
-
- /*
- * record the current doc width and text buffer offset for use
- * during relayout.
- */
- text_data->doc_width = state->right_margin - state->x;
- text_data->block_offset = block->buffer_read_index;
- XP_ASSERT(block->buffer_read_index <= 65535);
-
- baseline_inc = lo_compute_text_basline_inc ( state, block, text_data );
-
- lo_AppendToLineList(context, state, (LO_Element *)text_data, baseline_inc);
-
- if ( block->startTextElement == NULL )
- {
- block->startTextElement = text_data;
- block->endTextElement = text_data;
- }
- else
- {
- block->endTextElement = text_data;
- }
-
- text_data->height = state->text_info.ascent +
- state->text_info.descent;
-
- /*
- * If the element we just flushed had a breakable word
- * position in it, save that position in case we have
- * to go back and break this element before we finish
- * the line.
- */
- if (state->break_pos != -1)
- {
- state->old_break = text_data;
- state->old_break_block = block;
- state->old_break_pos = state->break_pos;
- state->old_break_width = state->break_width;
- }
-
- state->line_buf_len = 0;
- state->x += state->width;
- state->width = 0;
- state->cur_ele_type = LO_NONE;
- }
-
- void
- lo_FlushTextBlock ( MWContext *context, lo_DocState *state )
- {
- lo_FlushLineBuffer ( context, state );
-
- state->cur_text_block = NULL;
- }
-
- void
- lo_ChangeBodyTextFGColor(MWContext *context, lo_DocState *state,
- LO_Color *color)
- {
- lo_FontStack *fptr;
- LO_TextAttr *attr;
-
- if ((state->top_state->body_attr & BODY_ATTR_TEXT) != 0)
- return;
-
- state->top_state->body_attr |= BODY_ATTR_TEXT;
-
- state->text_fg = *color;
- fptr = state->font_stack;
-
- /*
- * If we're inside a layer, then we want this color change
- * to only affect text in the layer. So, we push a font
- * (a copy of the top of the stack) onto the font stack
- * and change its color. This font will be popped in the
- * closing of the layer.
- */
- if (lo_InsideLayer(state)) {
- LO_TextAttr tmp_attr;
-
- if (fptr)
- lo_CopyTextAttr(fptr->text_attr, &tmp_attr);
- else
- lo_SetDefaultFontAttr(state, &tmp_attr, context);
-
- tmp_attr.fg.red = STATE_DEFAULT_FG_RED(state);
- tmp_attr.fg.green = STATE_DEFAULT_FG_GREEN(state);
- tmp_attr.fg.blue = STATE_DEFAULT_FG_BLUE(state);
- attr = lo_FetchTextAttr(state, &tmp_attr);
- lo_PushFont(state, P_BODY, attr);
- }
- else if (fptr != NULL)
- {
- attr = fptr->text_attr;
- attr->fg.red = STATE_DEFAULT_FG_RED(state);
- attr->fg.green = STATE_DEFAULT_FG_GREEN(state);
- attr->fg.blue = STATE_DEFAULT_FG_BLUE(state);
- }
- }
-
-
- /*
- * Something has changed (probably the default FG and BG colors)
- * since the font stack was initialized in this state.
- * We need to reinitialie it to the new default font.
- * WARNING: This function depends on the assumption that no
- * elements have yet been placed in this state.
- */
- void
- lo_ResetFontStack(MWContext *context, lo_DocState *state)
- {
- if (state->font_stack != NULL)
- {
- lo_FontStack *fstack;
- lo_FontStack *fptr;
-
- fptr = state->font_stack;
- while (fptr != NULL)
- {
- fstack = fptr;
- fptr = fptr->next;
- XP_DELETE(fstack);
- }
- state->font_stack = NULL;
- }
- state->font_stack = lo_DefaultFont(state, context);
- }
-
-
- /*************************************
- * Function: lo_PushFont
- *
- * Description: Push the text attribute information for a new
- * font onto the font stack. Also save the type of the
- * tag that caused the change.
- *
- * Params: Document state, tag type, and the text attribute
- * structure for the new font.
- *
- * Returns: Nothing
- *************************************/
- void
- lo_PushFont(lo_DocState *state, intn tag_type, LO_TextAttr *attr)
- {
- lo_FontStack *fptr;
-
- fptr = XP_NEW(lo_FontStack);
- if (fptr == NULL)
- {
- return;
- }
- fptr->tag_type = tag_type;
- fptr->text_attr = attr;
- fptr->next = state->font_stack;
- state->font_stack = fptr;;
- }
-
-
- /*************************************
- * Function: lo_PopFontStack
- *
- * Description: This function pops the next font
- * off the font stack, and return the text attribute of the
- * previous font.
- * The last font on the font stack cannot be popped off.
- *
- * Params: Document state, and the tag type that caused the change.
- *
- * Returns: The LO_TextAttr structure of the font just passed.
- *************************************/
- PRIVATE
- LO_TextAttr *
- lo_PopFontStack(lo_DocState *state, intn tag_type)
- {
- LO_TextAttr *attr;
- lo_FontStack *fptr;
-
- if (state->font_stack->next == NULL)
- {
- #ifdef LOCAL_DEBUG
- XP_TRACE(("Popped too many fonts!\n"));
- #endif /* LOCAL_DEBUG */
- return(NULL);
- }
-
- fptr = state->font_stack;
- attr = fptr->text_attr;
- if (fptr->tag_type != tag_type)
- {
- #ifdef LOCAL_DEBUG
- XP_TRACE(("Warning: Font popped by different TAG than pushed it %d != %d\n", fptr->tag_type, tag_type));
- #endif /* LOCAL_DEBUG */
- }
- state->font_stack = fptr->next;
- XP_DELETE(fptr);
-
- return(attr);
- }
-
-
- LO_TextAttr *
- lo_PopFont(lo_DocState *state, intn tag_type)
- {
- LO_TextAttr *attr;
- lo_FontStack *fptr;
-
- /*
- * This should never happen, but we are patching a
- * more serious problem that causes us to be called
- * here after the font stack has been freed.
- */
- if ((state->font_stack == NULL)||(state->font_stack->next == NULL))
- {
- #ifdef LOCAL_DEBUG
- XP_TRACE(("Popped too many fonts!\n"));
- #endif /* LOCAL_DEBUG */
- return(NULL);
- }
-
- fptr = state->font_stack;
- attr = NULL;
-
- if (fptr->tag_type != P_ANCHOR)
- {
- attr = fptr->text_attr;
- if (fptr->tag_type != tag_type)
- {
- #ifdef LOCAL_DEBUG
- XP_TRACE(("Warning: Font popped by different TAG than pushed it %d != %d\n", fptr->tag_type, tag_type));
- #endif /* LOCAL_DEBUG */
- }
- state->font_stack = fptr->next;
- XP_DELETE(fptr);
- }
- else
- {
- while ((fptr->next != NULL)&&(fptr->next->tag_type == P_ANCHOR))
- {
- fptr = fptr->next;
- }
- if (fptr->next->next != NULL)
- {
- lo_FontStack *f_tmp;
-
- f_tmp = fptr->next;
- fptr->next = fptr->next->next;
- attr = f_tmp->text_attr;
- XP_DELETE(f_tmp);
- }
- }
-
- return(attr);
- }
-
-
- void
- lo_PopAllAnchors(lo_DocState *state)
- {
- lo_FontStack *fptr;
-
- if (state->font_stack->next == NULL)
- {
- #ifdef LOCAL_DEBUG
- XP_TRACE(("Popped too many fonts!\n"));
- #endif /* LOCAL_DEBUG */
- return;
- }
-
- /*
- * Remove all anchors on top of the font stack
- */
- fptr = state->font_stack;
- while ((fptr->tag_type == P_ANCHOR)&&(fptr->next != NULL))
- {
- lo_FontStack *f_tmp;
-
- f_tmp = fptr;
- fptr = fptr->next;
- XP_DELETE(f_tmp);
- }
- state->font_stack = fptr;
-
- /*
- * Remove all anchors buried in the stack
- */
- while (fptr->next != NULL)
- {
- /*
- * Reset spurrious anchor color text entries
- */
- if ((fptr->text_attr != NULL)&&
- (fptr->text_attr->attrmask & LO_ATTR_ANCHOR))
- {
- LO_TextAttr tmp_attr;
-
- lo_CopyTextAttr(fptr->text_attr, &tmp_attr);
- tmp_attr.attrmask =
- tmp_attr.attrmask & (~LO_ATTR_ANCHOR);
- tmp_attr.fg.red = STATE_DEFAULT_FG_RED(state);
- tmp_attr.fg.green = STATE_DEFAULT_FG_GREEN(state);
- tmp_attr.fg.blue = STATE_DEFAULT_FG_BLUE(state);
- tmp_attr.bg.red = STATE_DEFAULT_BG_RED(state);
- tmp_attr.bg.green = STATE_DEFAULT_BG_GREEN(state);
- tmp_attr.bg.blue = STATE_DEFAULT_BG_BLUE(state);
- fptr->text_attr = lo_FetchTextAttr(state, &tmp_attr);
- }
-
- if (fptr->next->tag_type == P_ANCHOR)
- {
- lo_FontStack *f_tmp;
-
- f_tmp = fptr->next;
- fptr->next = fptr->next->next;
- XP_DELETE(f_tmp);
- }
- else
- {
- fptr = fptr->next;
- }
- }
- }
-
- void
- lo_FormatBullet(MWContext *context, lo_DocState *state,
- LO_BulletStruct *bullet,
- int32 *line_height,
- int32 *baseline)
- {
- LO_TextStruct tmp_text;
- LO_TextInfo text_info;
- LO_TextAttr *tptr;
- PA_Block buff;
- char *str;
-
- #define MIN_BULLET_SIZE 5
-
- bullet->ele_id = NEXT_ELEMENT;
-
- /* bullet = (LO_BulletStruct *)lo_NewElement(context, state, LO_BULLET, NULL, 0); */
- if (bullet == NULL)
- {
- #ifdef DEBUG
- assert (state->top_state->out_of_memory);
- #endif
- return;
- }
- /*
- if(state->font_stack)
- {
- lo_CopyTextAttr(state->font_stack->text_attr, &tmp_attr);
- }
- else
- {
- lo_SetDefaultFontAttr(state, &tmp_attr, context);
- }
- */
- tptr = bullet->text_attr;
-
- memset (&tmp_text, 0, sizeof (tmp_text));
- buff = PA_ALLOC(1);
- if (buff == NULL)
- {
- state->top_state->out_of_memory = TRUE;
- return;
- }
- PA_LOCK(str, char *, buff);
- str[0] = ' ';
- PA_UNLOCK(buff);
- tmp_text.text = buff;
- tmp_text.text_len = 1;
- tmp_text.text_attr = tptr;
- FE_GetTextInfo(context, &tmp_text, &text_info);
- PA_FREE(buff);
- /*
- bullet->bullet_size = (text_info.ascent + text_info.descent) / 2;
- bullet->text_attr = tptr;
- */
- /* contain the bullet size so that it doesn't extend off the
- * left side of the page since we are using a negative offset
- * to place the bullet
- *
- * also subtract one to avoid the header code at the bottom
- * from triggering and messing up the alignment
- */
- if(bullet->bullet_size*2 >= state->x-state->win_left)
- bullet->bullet_size = ((state->x-state->win_left)/2)-1;
-
- /* enforce a minumum bullet size */
- if(bullet->bullet_size < 1)
- bullet->bullet_size = MIN_BULLET_SIZE;
-
- bullet->x = state->x - (2 * bullet->bullet_size);
- if (bullet->x < state->win_left)
- {
- bullet->x = state->win_left;
- }
- bullet->x_offset = 0;
- bullet->y = state->y;
- bullet->y_offset =
- (text_info.ascent + text_info.descent - bullet->bullet_size) / 2;
- bullet->width = bullet->bullet_size;
- bullet->height = bullet->bullet_size;
-
- *line_height = text_info.ascent + text_info.descent;
- *baseline = text_info.ascent;
- }
-
- void
- lo_UpdateStateAfterBullet(MWContext * context, lo_DocState *state,
- LO_BulletStruct *bullet,
- int32 line_height,
- int32 baseline)
- {
- state->baseline = baseline;
- state->line_height = line_height;
-
- /*
- * Clean up state
- */
- /*
- * Supporting old mistakes made in some other browsers.
- * I will put the "correct code" here, but comment it out, since
- * some other browsers allowed headers inside lists, so we should to, sigh.
- state->linefeed_state = 0;
- */
- state->at_begin_line = TRUE;
- state->cur_ele_type = LO_BULLET;
- if (bullet->x == state->win_left)
- {
- state->x += (bullet->x_offset + (2 * bullet->width));
- }
-
- /*
- * Make at_begin_line be accurate
- * so we can detect the header
- * linefeed state deception later.
- */
- state->at_begin_line = FALSE;
-
- /*
- * After much soul-searching (and brow-beating
- * by Jamie, I've agreed that really whitespace
- * should be compressed out at the start of a
- * list item. They can always add non-breaking
- * spaces if they want them.
- * Setting trailing space true means it won't
- * let the users add whitespace because it
- * thinks there already is some.
- */
- state->trailing_space = TRUE;
- }
-
- void
- lo_PlaceBullet(MWContext *context, lo_DocState *state)
- {
- LO_BulletStruct *bullet = NULL;
- int32 line_height, baseline;
- LO_TextAttr tmp_attr;
- LO_TextStruct tmp_text;
- LO_TextInfo text_info;
- LO_TextAttr *tptr;
- PA_Block buff;
- char *str;
-
- bullet = (LO_BulletStruct *)lo_NewElement(context, state, LO_BULLET, NULL, 0);
- if (bullet == NULL)
- {
- #ifdef DEBUG
- assert (state->top_state->out_of_memory);
- #endif
- return;
- }
-
- bullet->type = LO_BULLET;
- bullet->next = NULL;
- bullet->prev = NULL;
-
- bullet->FE_Data = NULL;
-
- bullet->level = state->list_stack->level;
-
- bullet->bullet_type = state->list_stack->bullet_type;
-
- /* try and get a bullet type from style sheets */
- if(state && state->top_state && state->top_state->style_stack)
- {
- StyleStruct *style_struct = STYLESTACK_GetStyleByIndex(state->top_state->style_stack, 0);
-
- if(style_struct)
- {
- char *list_style_prop = STYLESTRUCT_GetString(style_struct,
- LIST_STYLE_TYPE_STYLE);
- if(list_style_prop)
- {
- bullet->bullet_type = lo_list_bullet_type(list_style_prop, P_UNUM_LIST);
- XP_FREE(list_style_prop);
- }
- }
- }
-
- bullet->ele_attrmask = 0;
-
- bullet->sel_start = -1;
- bullet->sel_end = -1;
-
- if(state->font_stack)
- {
- lo_CopyTextAttr(state->font_stack->text_attr, &tmp_attr);
- }
- else
- {
- lo_SetDefaultFontAttr(state, &tmp_attr, context);
- }
- tptr = lo_FetchTextAttr(state, &tmp_attr);
-
- memset (&tmp_text, 0, sizeof (tmp_text));
- buff = PA_ALLOC(1);
- if (buff == NULL)
- {
- state->top_state->out_of_memory = TRUE;
- return;
- }
- PA_LOCK(str, char *, buff);
- str[0] = ' ';
- PA_UNLOCK(buff);
- tmp_text.text = buff;
- tmp_text.text_len = 1;
- tmp_text.text_attr = tptr;
- FE_GetTextInfo(context, &tmp_text, &text_info);
- PA_FREE(buff);
-
- bullet->bullet_size = (text_info.ascent + text_info.descent) / 2;
- bullet->text_attr = tptr;
-
- lo_FormatBullet(context, state, bullet, &line_height, &baseline);
-
- lo_AppendToLineList(context, state, (LO_Element *)bullet, 0);
-
- lo_UpdateStateAfterBullet(context, state, bullet,
- line_height,
- baseline);
- }
-
-
- void
- lo_FormatBulletStr(MWContext *context, lo_DocState *state,
- LO_TextStruct *bullet_text,
- int32 *line_height,
- int32 *baseline)
- {
- LO_TextInfo text_info;
-
- FE_GetTextInfo(context, bullet_text, &text_info);
-
- bullet_text->x = state->x - (bullet_text->height / 2) -
- bullet_text->width;
- if (bullet_text->x < state->win_left)
- {
- bullet_text->x = state->win_left;
- }
- bullet_text->x_offset = 0;
- bullet_text->y = state->y;
- bullet_text->y_offset = 0;
-
- state->baseline = text_info.ascent;
- state->line_height = (intn) bullet_text->height;
-
- *baseline = text_info.ascent;
- *line_height = bullet_text->height;
- }
-
- void
- lo_UpdateStateAfterBulletStr(MWContext *context,
- lo_DocState *state,
- LO_TextStruct *bullet_text,
- int32 line_height,
- int32 baseline)
- {
- state->baseline = baseline;
- state->line_height = line_height;
-
- /*
- * Clean up state
- */
- /*
- * Supporting old mistakes made in some other browsers.
- * I will put the "correct code" here, but comment it out, since
- * some other browsers allowed headers inside lists, so we should to, sigh.
- state->linefeed_state = 0;
- state->at_begin_line = FALSE;
- */
- state->at_begin_line = TRUE;
- state->cur_ele_type = LO_TEXT;
- }
-
- void
- lo_PlaceBulletStr(MWContext *context, lo_DocState *state)
- {
- intn len;
- char str2[22];
- char *str;
- char *str3;
- PA_Block buff;
- LO_TextStruct *bullet_text = NULL;
- LO_TextInfo text_info;
- int bullet_type;
- int32 line_height, baseline;
-
- bullet_text = (LO_TextStruct *)lo_NewElement(context, state, LO_TEXT, NULL, 0);
- if (bullet_text == NULL)
- {
- #ifdef DEBUG
- assert (state->top_state->out_of_memory);
- #endif
- return;
- }
-
- bullet_type = state->list_stack->bullet_type;
-
- /* try and get a bullet type from style sheets */
- if(state && state->top_state && state->top_state->style_stack)
- {
- StyleStruct *style_struct = STYLESTACK_GetStyleByIndex(state->top_state->style_stack, 0);
-
- if(style_struct)
- {
- char *list_style_prop = STYLESTRUCT_GetString(style_struct,
- LIST_STYLE_TYPE_STYLE);
- if(list_style_prop)
- {
- bullet_type = lo_list_bullet_type(list_style_prop, P_NUM_LIST);
- XP_FREE(list_style_prop);
- }
- }
- }
-
-
- if( EDT_IS_EDITOR( context ))
- {
- switch( bullet_type ){
- case BULLET_ALPHA_L:
- str = "A";
- break;
- case BULLET_ALPHA_S:
- str = "a";
- break;
- case BULLET_NUM_S_ROMAN:
- str = "x";
- break;
- case BULLET_NUM_L_ROMAN:
- str = "X";
- break;
- default:
- str = "#";
- break;
- }
- len = XP_STRLEN(str);
- buff = PA_ALLOC(len + 1);
- if (buff != NULL)
- {
- PA_LOCK(str3, char *, buff);
- XP_STRCPY(str3, str);
- PA_UNLOCK(buff);
- }
- }
- else {
- if (bullet_type == BULLET_ALPHA_S)
- {
- buff = lo_ValueToAlpha(state->list_stack->value, FALSE, &len);
- }
- else if (bullet_type == BULLET_ALPHA_L)
- {
- buff = lo_ValueToAlpha(state->list_stack->value, TRUE, &len);
- }
- else if (bullet_type == BULLET_NUM_S_ROMAN)
- {
- buff = lo_ValueToRoman(state->list_stack->value, FALSE, &len);
- }
- else if (bullet_type == BULLET_NUM_L_ROMAN)
- {
- buff = lo_ValueToRoman(state->list_stack->value, TRUE, &len);
- }
- else
- {
- XP_SPRINTF(str2, "%d.", (intn)state->list_stack->value);
- len = XP_STRLEN(str2);
- buff = PA_ALLOC(len + 1);
- if (buff != NULL)
- {
- PA_LOCK(str, char *, buff);
- XP_STRCPY(str, str2);
- PA_UNLOCK(buff);
- }
- else
- {
- state->top_state->out_of_memory = TRUE;
- }
- }
- }
-
- if (buff == NULL)
- {
- return;
- }
-
- bullet_text->bullet_type = bullet_type;
- bullet_text->text = buff;
- bullet_text->text_len = len;
- bullet_text->text_attr = state->font_stack->text_attr;
- FE_GetTextInfo(context, bullet_text, &text_info);
- bullet_text->width = lo_correct_text_element_width(&text_info);
- bullet_text->height = text_info.ascent + text_info.descent;
-
- bullet_text->type = LO_TEXT;
- bullet_text->ele_id = NEXT_ELEMENT;
-
- lo_FormatBulletStr(context, state, bullet_text, &line_height, &baseline);
-
- bullet_text->anchor_href = state->current_anchor;
-
- bullet_text->ele_attrmask = 0;
- if (state->breakable != FALSE)
- {
- bullet_text->ele_attrmask |= LO_ELE_BREAKABLE;
- }
-
- bullet_text->sel_start = -1;
- bullet_text->sel_end = -1;
-
- bullet_text->next = NULL;
- bullet_text->prev = NULL;
-
- bullet_text->FE_Data = NULL;
-
- lo_AppendToLineList(context, state, (LO_Element *)bullet_text, 0);
-
- state->baseline = text_info.ascent;
- state->line_height = (intn) bullet_text->height;
-
- lo_UpdateStateAfterBulletStr(context, state, bullet_text, line_height, baseline);
- }
-
-
- static LO_Element *
- lo_make_quote_text(MWContext *context, lo_DocState *state, int32 margin)
- {
- PA_Block buff;
- char *str;
- LO_TextStruct *quote_text;
- LO_TextAttr tmp_attr;
- LO_TextInfo text_info;
-
- quote_text = (LO_TextStruct *)lo_NewElement(context, state, LO_TEXT,
- NULL, 0);
- if (quote_text == NULL)
- {
- #ifdef DEBUG
- assert (state->top_state->out_of_memory);
- #endif
- return(NULL);
- }
-
- buff = PA_ALLOC(2);
- if (buff == NULL)
- {
- state->top_state->out_of_memory = TRUE;
- return(NULL);
- }
- PA_LOCK(str, char *, buff);
- str[0] = '>';
- str[1] = '\0';
- PA_UNLOCK(buff);
-
- quote_text->text = buff;
- quote_text->text_len = 1;
- /*
- * Fill in default fixed font information.
- */
- lo_SetDefaultFontAttr(state, &tmp_attr, context);
- tmp_attr.fontmask |= LO_FONT_FIXED;
- quote_text->text_attr = lo_FetchTextAttr(state, &tmp_attr);
- FE_GetTextInfo(context, quote_text, &text_info);
- quote_text->width = lo_correct_text_element_width(&text_info);
- quote_text->height = text_info.ascent + text_info.descent;
-
- quote_text->type = LO_TEXT;
- quote_text->ele_id = 0;
- quote_text->x = margin;
- if (quote_text->x < state->win_left)
- {
- quote_text->x = state->win_left;
- }
- quote_text->x_offset = 0;
- quote_text->y = state->y;
- quote_text->y_offset = 0;
-
- quote_text->anchor_href = state->current_anchor;
-
- quote_text->ele_attrmask = 0;
- if (state->breakable != FALSE)
- {
- quote_text->ele_attrmask |= LO_ELE_BREAKABLE;
- }
-
- quote_text->bullet_type = BULLET_MQUOTE;
-
- quote_text->sel_start = -1;
- quote_text->sel_end = -1;
-
- quote_text->next = NULL;
- quote_text->prev = NULL;
-
- quote_text->FE_Data = NULL;
-
- state->baseline = text_info.ascent;
-
- return((LO_Element *)quote_text);
- }
-
-
- static LO_Element *
- lo_make_quote_bullet(MWContext *context, lo_DocState *state, int32 margin)
- {
- PA_Block buff;
- char *str;
- LO_BulletStruct *bullet = NULL;
- LO_TextAttr tmp_attr;
- LO_TextInfo text_info;
- LO_TextStruct tmp_text;
- LO_TextAttr *tptr;
- int32 bullet_size;
-
- bullet = (LO_BulletStruct *)lo_NewElement(context, state, LO_BULLET, NULL, 0);
- if (bullet == NULL)
- {
- #ifdef DEBUG
- assert (state->top_state->out_of_memory);
- #endif
- return(NULL);
- }
-
- lo_SetDefaultFontAttr(state, &tmp_attr, context);
- tmp_attr.fg.red = 0;
- tmp_attr.fg.green = 0;
- tmp_attr.fg.blue = 255;
- tptr = lo_FetchTextAttr(state, &tmp_attr);
-
- memset (&tmp_text, 0, sizeof (tmp_text));
- buff = PA_ALLOC(1);
- if (buff == NULL)
- {
- state->top_state->out_of_memory = TRUE;
- return(NULL);
- }
- PA_LOCK(str, char *, buff);
- str[0] = ' ';
- PA_UNLOCK(buff);
- tmp_text.text = buff;
- tmp_text.text_len = 1;
- tmp_text.text_attr = tptr;
- FE_GetTextInfo(context, &tmp_text, &text_info);
- PA_FREE(buff);
-
- bullet_size = text_info.ascent + text_info.descent;
-
- if (bullet_size < 5)
- {
- bullet_size = 5;
- }
-
- bullet->type = LO_BULLET;
- bullet->ele_id = 0;
- bullet->x = margin;
- if (bullet->x < state->win_left)
- {
- bullet->x = state->win_left;
- }
- bullet->x_offset = 0;
- bullet->y = state->y;
- bullet->y_offset = 0;
- bullet->width = 5;
- bullet->height = bullet_size;
- bullet->next = NULL;
- bullet->prev = NULL;
-
- bullet->FE_Data = NULL;
-
- bullet->level = state->list_stack->level;
- bullet->bullet_type = BULLET_MQUOTE;
- bullet->text_attr = tptr;
-
- bullet->ele_attrmask = 0;
-
- bullet->sel_start = -1;
- bullet->sel_end = -1;
-
- state->baseline = text_info.ascent;
-
- return((LO_Element *)bullet);
- }
-
-
- static void
- lo_insert_quote_characters(MWContext *context, lo_DocState *state)
- {
- LO_Element *eptr;
- LO_Element *elist;
- lo_ListStack *lptr;
-
- elist = NULL;
- lptr = state->list_stack;
- while (lptr != NULL)
- {
- eptr = NULL;
- if (lptr->quote_type == QUOTE_JWZ)
- {
- eptr = lo_make_quote_text(context, state,
- lptr->mquote_x);
- }
- else if (lptr->quote_type == QUOTE_CITE)
- {
- eptr = lo_make_quote_bullet(context, state,
- lptr->mquote_x);
- }
- if (eptr != NULL)
- {
- eptr->lo_any.next = elist;
- elist = eptr;
- }
- lptr = lptr->next;
- }
-
- eptr = elist;
- while (eptr != NULL)
- {
- LO_Element *tmp_ele;
-
- tmp_ele = eptr;
- eptr = eptr->lo_any.next;
- tmp_ele->lo_any.next = NULL;
-
- tmp_ele->lo_any.ele_id = NEXT_ELEMENT;
- lo_AppendToLineList(context, state, tmp_ele, 0);
- state->line_height = (intn)tmp_ele->lo_any.height;
- state->at_begin_line = TRUE;
- state->cur_ele_type = LO_TEXT;
- }
- }
-
-
- void
- lo_PlaceQuoteMarker(MWContext *context, lo_DocState *state, lo_ListStack *lptr)
- {
- LO_Element *eptr;
-
- if (lptr != NULL)
- {
- eptr = NULL;
- if (lptr->quote_type == QUOTE_JWZ)
- {
- eptr = lo_make_quote_text(context, state,
- lptr->mquote_x);
- }
- else if (lptr->quote_type == QUOTE_CITE)
- {
- eptr = lo_make_quote_bullet(context, state,
- lptr->mquote_x);
- }
- if (eptr != NULL)
- {
- eptr->lo_any.ele_id = NEXT_ELEMENT;
- lo_AppendToLineList(context, state, eptr, 0);
- state->line_height = (intn)eptr->lo_any.height;
- state->at_begin_line = TRUE;
- if (lptr->quote_type == QUOTE_JWZ)
- {
- state->cur_ele_type = LO_TEXT;
- }
- else if (lptr->quote_type == QUOTE_CITE)
- {
- state->cur_ele_type = LO_BULLET;
- }
- }
- }
- }
-
-
- void lo_UpdateStateAfterLineBreak( MWContext *context, lo_DocState *state, Bool updateFE )
- {
- int32 line_width;
-
- /*
- * if this linefeed has a zero height, make it the height
- * of the current font.
- */
- if (state->line_height == 0)
- {
- state->line_height = state->text_info.ascent +
- state->text_info.descent;
- if ((state->line_height <= 0)&&(state->font_stack != NULL)&&
- (state->font_stack->text_attr != NULL))
- {
- lo_fillin_text_info(context, state);
- state->line_height = state->text_info.ascent +
- state->text_info.descent;
- }
- /*
- * This should never happen, but we have it
- * covered just in case it does :-)
- */
- if (state->line_height <= 0)
- {
- state->line_height = state->default_line_height;
- }
- }
-
- if (state->end_last_line != NULL)
- {
- line_width = state->end_last_line->lo_any.x + state->win_right;
- }
- else
- {
- line_width = state->x + state->win_right;
- }
-
- if (line_width > state->max_width)
- {
- state->max_width = line_width;
- }
-
- /* if LineHeightStack exists use it to offset the new Y value */
- if(state->cur_ele_type != LO_SUBDOC && state->line_height_stack)
- {
- state->y += state->line_height_stack->height;
- }
- else
- {
- state->y = state->y + state->line_height;
- }
-
- state->x = state->left_margin;
- state->width = 0;
- state->at_begin_line = TRUE;
- state->trailing_space = FALSE;
- state->line_height = 0;
- state->break_holder = state->x;
-
- state->linefeed_state++;
- if (state->linefeed_state > 2)
- {
- state->linefeed_state = 2;
- }
-
- /*
- * Reset the left and right margins
- */
- lo_FindLineMargins(context, state, updateFE);
- state->x = state->left_margin;
-
- }
-
- void lo_UpdateFEProgressBar( MWContext *context, lo_DocState *state )
- {
- if (state->is_a_subdoc == SUBDOC_NOT)
- {
- int32 percent;
-
- if (state->top_state->total_bytes < 1)
- {
- percent = -1;
- }
- else
- {
- percent = (100 * state->top_state->layout_bytes) /
- state->top_state->total_bytes;
- if (percent > 100)
- {
- percent = 100;
- }
- }
- if ((percent == 100)||(percent < 0)||
- (percent > (state->top_state->layout_percent + 1)))
- {
- if(!state->top_state->is_binary)
- FE_SetProgressBarPercent(context, percent);
- state->top_state->layout_percent = (intn)percent;
- }
- }
- }
-
- void lo_UpdateFEDocSize( MWContext *context, lo_DocState *state )
- {
- /*
- * Tell the front end how big the document is right now.
- * Only do this for the top level document.
- */
- if ((state->is_a_subdoc == SUBDOC_NOT)
- &&(state->display_blocked == FALSE)
- #ifdef EDITOR
- &&(!state->edit_relayout_display_blocked)
- #endif
- )
- {
-
- /*
- * Don't resize the layer if we're laying out a block. This
- * will be done when the line is added to the block.
- */
- if (!lo_InsideLayer(state))
- {
- LO_SetDocumentDimensions(context, state->max_width, state->y);
- }
- }
- }
-
-
- void lo_FillInLineFeed( MWContext *context, lo_DocState *state, int32 break_type, uint32 clear_type, LO_LinefeedStruct *linefeed )
- {
- linefeed->type = LO_LINEFEED;
- linefeed->ele_id = NEXT_ELEMENT;
- linefeed->x = state->x;
- linefeed->x_offset = 0;
- linefeed->y = state->y;
- linefeed->y_offset = 0;
- /*
- * If we're laying out a block, we want the contents of the block
- * to determine the size of the block. The right margin is nothing
- * more than a hint for where to wrap the contents. We don't want
- * the linefeed to extend out to the right margin, because it
- * unnecessarily extends the block contents.
- */
- if (state->layer_nest_level > 0) {
- linefeed->width = 0;
- }
- else
- linefeed->width = state->right_margin - state->x;
- if (linefeed->width < 0)
- {
- linefeed->width = 0;
- }
- linefeed->height = state->line_height;
- /*
- * if this linefeed has a zero height, make it the height
- * of the current font.
- */
- if (linefeed->height == 0)
- {
- linefeed->height = state->text_info.ascent +
- state->text_info.descent;
- if ((linefeed->height <= 0)&&(state->font_stack != NULL)&&
- (state->font_stack->text_attr != NULL))
- {
- lo_fillin_text_info(context, state);
- linefeed->height = state->text_info.ascent +
- state->text_info.descent;
- }
- /*
- * This should never happen, but we have it
- * covered just in case it does :-)
- */
- if (linefeed->height <= 0)
- {
- linefeed->height = state->default_line_height;
- }
- }
- linefeed->line_height = linefeed->height;
-
- linefeed->FE_Data = NULL;
- linefeed->anchor_href = state->current_anchor;
-
- if (state->font_stack == NULL)
- {
- LO_TextAttr tmp_attr;
- LO_TextAttr *tptr;
-
- /*
- * Fill in default font information.
- */
- lo_SetDefaultFontAttr(state, &tmp_attr, context);
- tptr = lo_FetchTextAttr(state, &tmp_attr);
- linefeed->text_attr = tptr;
- }
- else
- {
- linefeed->text_attr = state->font_stack->text_attr;
- }
-
- linefeed->baseline = state->baseline;
-
- linefeed->ele_attrmask = 0;
-
- linefeed->sel_start = -1;
- linefeed->sel_end = -1;
-
- linefeed->next = NULL;
- linefeed->prev = NULL;
- linefeed->break_type = (uint8) break_type;
- linefeed->clear_type = (uint8) clear_type;
- }
-
- Bool lo_CanUseBreakTable ( lo_DocState * state )
- {
- Bool multiByte;
- Bool useBreakTable;
-
- useBreakTable = TRUE;
-
- lo_GetTextParseAtributes ( state, &multiByte );
-
- #ifndef FAST_MULTI
- /*
- * We also need some sort of check for the script - for example Arabic should go through
- * the old algorithm for now.
- */
- if ( multiByte )
- {
- useBreakTable = FALSE;
- }
- #endif
-
- /*
- * Justified text is currently broken, route it through old layout for now
- */
- if ( ( state->align_stack != NULL ) && ( state->align_stack->alignment == LO_ALIGN_JUSTIFY ) )
- {
- useBreakTable = FALSE;
- }
-
- #ifndef FAST_EDITOR
- if ( EDT_IS_EDITOR( context ) )
- {
- useBreakTable = FALSE;
- }
- #endif
-
- #ifdef XP_MAC
- if ( !gCallNewText )
- {
- useBreakTable = FALSE;
- }
- #endif
-
- return useBreakTable;
- }
-
- /*
- * Is the current text that's being layed out using the break table layout algorithm?
- */
- Bool lo_UseBreakTable ( LO_TextBlock * block )
- {
- Bool useBreakTable;
-
- useBreakTable = FALSE;
-
- if ( block != NULL )
- {
- if ( block->break_table != NULL )
- {
- useBreakTable = TRUE;
- }
- }
-
- return useBreakTable;
- }
-
- int32 lo_compute_text_basline_inc ( lo_DocState * state, LO_TextBlock * block, LO_TextStruct * text_data )
- {
- int32 line_inc;
- int32 baseline_inc;
-
- /*
- * The baseline of the text element just added to the line may be
- * less than or greater than the baseline of the rest of the line
- * due to font changes. If the baseline is less, this is easy,
- * we just increase y_offest to move the text down so the baselines
- * line up. For greater baselines, we can't move the text up to
- * line up the baselines because we will overlay the previous line,
- * so we have to move all the previous elements in this line down.
- *
- * If the baseline is zero, we are the first element on the line,
- * and we get to set the baseline.
- */
-
- line_inc = 0;
- baseline_inc = 0;
-
- if (state->baseline == 0)
- {
- state->baseline = block->ascent;
- if (state->line_height < (state->baseline + block->descent))
- {
- state->line_height = state->baseline + block->descent;
- }
- }
- else if (block->ascent < state->baseline)
- {
- text_data->y_offset = state->baseline - block->ascent;
- if ((text_data->y_offset + block->ascent + block->descent) > state->line_height)
- {
- line_inc = text_data->y_offset +
- block->ascent +
- block->descent -
- state->line_height;
- }
- }
- else
- {
- baseline_inc = block->ascent - state->baseline;
- if ((text_data->y_offset + block->ascent + block->descent - baseline_inc) > state->line_height)
- {
- line_inc = text_data->y_offset +
- block->ascent +
- block->descent -
- state->line_height - baseline_inc;
- }
- }
-
- state->baseline += (intn) baseline_inc;
- state->line_height += (intn) (baseline_inc + line_inc);
-
- return baseline_inc;
- }
-
-
- void lo_FlushTextElement ( MWContext * context, lo_DocState * state, LO_TextBlock * block, LO_TextStruct * element );
- void lo_FlushTextElement ( MWContext * context, lo_DocState * state, LO_TextBlock * block, LO_TextStruct * element )
- {
- int32 baseline_inc;
-
- /* update the text layout state as if we had just layed this element out */
- block->buffer_read_index = element->block_offset;
-
- state->width = element->width;
-
- element->ele_id = NEXT_ELEMENT;
- element->x = state->x;
- element->y = state->y;
- element->y_offset = 0;
-
- element->sel_start = -1;
- element->sel_end = -1;
-
- baseline_inc = lo_compute_text_basline_inc ( state, block, element );
-
- element->prev = NULL;
- element->next = NULL;
- lo_AppendToLineList ( context, state, (LO_Element *) element, baseline_inc );
-
- state->line_buf_len = 0;
- state->x += state->width;
- state->width = 0;
- state->cur_ele_type = LO_NONE;
-
- /* update the element list for this block */
- if ( block->startTextElement == NULL )
- {
- block->startTextElement = element;
- }
-
- block->endTextElement = element;
- }
-
- uint32 lo_FindBlockOffset ( LO_TextBlock * block, LO_TextStruct * fromElement )
- {
- uint32 blockOffset;
- LO_Element * endElement;
- LO_Element * element;
-
- blockOffset = 0;
-
- if ( fromElement != NULL )
- {
- /* run through all elements in this text block. the correct block offset is belongs to */
- /* the previous element in this list */
- element = (LO_Element *) block->startTextElement;
- endElement = (LO_Element *) block->endTextElement;
-
- while ( element != NULL )
- {
- if ( element == endElement )
- {
- break;
- }
-
- /* is it the one we're looking for? */
- if ( element == (LO_Element *) fromElement )
- {
- break;
- }
-
- if ( element->type == LO_TEXT )
- {
- blockOffset = element->lo_text.block_offset;
- }
-
- element = element->lo_any.next;
- }
- }
-
- return blockOffset;
- }
-
- void lo_RelayoutTextElements ( MWContext * context, lo_DocState * state, LO_TextBlock * block, LO_TextStruct * fromElement )
- {
- LO_Element * next;
- LO_Element * element;
- LO_Element * startElement;
- LO_Element * endElement;
- uint32 lineWidth;
- Bool done;
- Bool fastPreformat;
-
- /* start at the beginning of this text block */
- block->buffer_read_index = 0;
- block->break_read_index = 0;
-
- /* we will rebuild the element list for this block as we go */
- startElement = (LO_Element *) block->startTextElement;
- endElement = (LO_Element *) block->endTextElement;
-
- block->startTextElement = NULL;
- block->endTextElement = NULL;
-
- if ( fromElement == NULL )
- {
- fromElement = (LO_TextStruct *) startElement;
- }
-
- /* sanity check */
- if ( fromElement == NULL )
- {
- return;
- }
-
- /*
- * We need to run through all elements up to start element and place them
- * back in the line list. We also need to recycle anything that's not text
- * (linefeeds and bullets)
- */
- element = startElement;
-
- while ( element != (LO_Element *) fromElement )
- {
- next = lo_tv_GetNextLayoutElement ( state, element, FALSE );
-
- /* if this element is text, put it on the line list */
- switch ( element->lo_any.type )
- {
- case LO_TEXT:
- lo_PrepareElementForReuse ( context, state, element, element->lo_any.edit_element,
- element->lo_any.edit_offset );
- lo_FlushTextElement ( context, state, block, (LO_TextStruct *) element );
- break;
-
- case LO_LINEFEED:
- /* recycle this element */
- element->lo_any.prev = NULL;
- element->lo_any.next = NULL;
- lo_RecycleElements( context, state, element );
-
- #if 0
- /* toshok: I'm #if 0'ing these two calls to
- lo_rl_AddSoftBreakAndFlushLine, since we shouldn't
- be adding these line breaks while we're consuming
- the old ones. They will be added again by the text
- layout machinery. This fixes the problem of blank
- regions in <pre>'s growing with every resize.
-
- An alternative for this would be to just remove the
- \n's from the pre text once we've inserted the line
- breaks.*/
-
- /* and then add a new linefeed */
- lo_rl_AddSoftBreakAndFlushLine ( context, state );
- #endif
- break;
-
- default:
- element->lo_any.prev = NULL;
- element->lo_any.next = NULL;
- lo_RecycleElements( context, state, element );
- break;
- }
-
- element = next;
- }
-
- /*
- * Now, we may not need to lay any of the following elements out as out
- * layout environment may not have changed. So, run through the remaining
- * elements until we find the first one that's changed.
- *
- * We have to layout the last element using the proper code path so that
- * we can correctly update the state record with the last break position and
- * other flags.
- *
- * Column and line wrapped preformatted text can always reuse the elements as
- * it's wrapping will never change. Word wrapped preformatted text may change
- * if the document width changes.
- */
-
- element = (LO_Element *) fromElement;
- done = element == endElement;
- fastPreformat = ( block->format_mode == PRE_TEXT_YES ) || ( block->format_mode == PRE_TEXT_COLS );
-
- while ( ( !done ) && ( element != NULL ) )
- {
- next = lo_tv_GetNextLayoutElement ( state, element, FALSE );
-
- /* if this element is text, see if it will fit. otherwise recycle it */
- switch ( element->lo_any.type )
- {
- case LO_TEXT:
- /*
- * We only assume this element can be reused if the line width is exactly
- * the same as last time. If the line is longer, we could potentially
- * reuse this element (the next one may appear on this line as well) but
- * we won't be able to set the state's old_break_position, which may be needed!
- *
- * We can also always flush column or line wrapped preformatted text (word wrapped
- * preformatted text may need to be layed out again as it's wrapping may change).
- */
- lineWidth = state->right_margin - state->x;
-
- if ( fastPreformat || ( element->lo_text.doc_width == lineWidth ) )
- {
- lo_PrepareElementForReuse ( context, state, element, element->lo_any.edit_element,
- element->lo_any.edit_offset );
- lo_FlushTextElement ( context, state, block, (LO_TextStruct *) element );
- }
- else
- {
- /* the size has changed, we must relayout this element */
- done = TRUE;
- }
-
- break;
-
- case LO_LINEFEED:
- /* recycle this element */
- element->lo_any.prev = NULL;
- element->lo_any.next = NULL;
- lo_RecycleElements( context, state, element );
-
- #if 0
- /* toshok: I'm #if 0'ing these two calls to
- lo_rl_AddSoftBreakAndFlushLine, since we shouldn't
- be adding these line breaks while we're consuming
- the old ones. They will be added again by the text
- layout machinery. This fixes the problem of blank
- regions in <pre>'s growing with every resize.
-
- An alternative for this would be to just remove the
- \n's from the pre text once we've inserted the line
- breaks.*/
-
- /* and then add a new linefeed */
- lo_rl_AddSoftBreakAndFlushLine ( context, state );
- #endif
- break;
-
- default:
- element->lo_any.prev = NULL;
- element->lo_any.next = NULL;
- lo_RecycleElements( context, state, element );
- break;
- }
-
- element = next;
-
- /*
- * if we're at the last element, bail as we always need to layout this
- * one so that the state record is updated properly
- */
- if ( element == endElement )
- {
- break;
- }
- }
-
- /*
- * now run through and delete all the remaining elements in this text block.
- */
- while ( element != NULL )
- {
- next = lo_tv_GetNextLayoutElement ( state, element, FALSE );
-
- element->lo_any.prev = NULL;
- element->lo_any.next = NULL;
- lo_RecycleElements( context, state, element );
-
- if ( element == endElement )
- {
- break;
- }
-
- element = next;
- }
- }
-
- LO_Element * lo_RelayoutTextBlock ( MWContext * context, lo_DocState * state, LO_TextBlock * block, LO_TextStruct * fromElement )
- {
- LO_Element * next;
- LO_Element * endElement;
- LO_Element * lo_ele;
-
- state->cur_text_block = block;
-
- /*
- * Update some of the global state information
- */
- state->breakable = block->ele_attrmask & LO_ELE_BREAKABLE;
-
- /* get the next element for the overall relayout process */
- if ( block->endTextElement != NULL )
- {
- next = lo_tv_GetNextLayoutElement ( state, (LO_Element *) block->endTextElement, FALSE );
- }
- else
- {
- next = lo_tv_GetNextLayoutElement ( state, (LO_Element *) block, FALSE );
- }
-
- /*
- * In the relayout case we might be able to skip layout for some elements that have not changed.
- * This happens frequenty for typing in the editor and occasionaly in table layout (very occasionally
- * on resizes).
- *
- * To do this, we run through the text elements until we come across one whose width does not match the
- * layout width or whose text has changed. That element and all others are then recycled.
- */
-
- if ( EDT_IS_EDITOR( context ) )
- {
- /*
- * for the editor we don't want to relayout the text elements that precede the current one. we just
- * want to start laying out afresh from this specified element to the end of the text block. the editor
- * will take care of merging the elements back in
- */
- block->buffer_read_index = lo_FindBlockOffset ( block, fromElement );
-
- state->edit_current_element = block->edit_element;
- state->edit_current_offset = 0;
-
- /* if we're laying this element out, then we need to reinsert it on the line list */
- if ( ( block->startTextElement == fromElement ) || ( fromElement == NULL ) )
- {
- /* record the current edit element for later use */
- lo_PrepareElementForReuse ( context, state, (LO_Element *) block, block->edit_element,
- block->edit_offset );
-
- block->ele_id = NEXT_ELEMENT;
- block->x = state->x;
- block->y = state->y;
- block->x_offset = 0;
- block->y_offset = 0;
-
- /* free all the text elements that we're going to reflow */
- endElement = (LO_Element *) block->endTextElement;
- lo_ele = (LO_Element *) block->startTextElement;
- while ( lo_ele != NULL )
- {
- LO_Element * next_ele;
-
- next_ele = lo_ele->lo_any.next;
-
- lo_ele->lo_any.next = NULL;
- lo_ele->lo_any.prev = NULL;
- lo_RecycleElements( context, state, lo_ele );
-
- if ( lo_ele == endElement )
- {
- break;
- }
-
- lo_ele = next_ele;
- }
-
- block->startTextElement = NULL;
- block->endTextElement = NULL;
-
- block->prev = NULL;
- block->next = NULL;
- lo_AppendToLineList ( context, state, (LO_Element *) block, 0 );
- }
- else
- {
- LO_TextStruct * lastText;
- Bool hitFromElement;
-
- /*
- * We're reflowing from somewhere within the text block (past the first element). We need
- * to reset the endTextElement as well as recycle from the fromElement to the end of the text block
- */
-
- hitFromElement = FALSE;
- endElement = (LO_Element *) block->endTextElement;
- lo_ele = (LO_Element *) block->startTextElement;
- lastText = block->startTextElement;
-
- while ( lo_ele != NULL )
- {
- LO_Element * next_ele;
-
- next_ele = lo_ele->lo_any.next;
-
- if ( lo_ele == (LO_Element *) fromElement )
- {
- hitFromElement = TRUE;
- }
-
- /* if we've found the fromElement, we need to start recycling */
- if ( hitFromElement )
- {
- lo_ele->lo_any.next = NULL;
- lo_ele->lo_any.prev = NULL;
- lo_RecycleElements( context, state, lo_ele );
- }
-
- if ( lo_ele == endElement )
- {
- break;
- }
-
- /* if we haven't hit the from element and this is a text element, it may be the new end */
- /* element for the block */
- if ( !hitFromElement && ( lo_ele->type == LO_TEXT ) )
- {
- lastText = &lo_ele->lo_text;
- }
-
- lo_ele = next_ele;
- }
-
- /* reset the endElement for the block */
- block->endTextElement = lastText;
- }
- }
- else
- {
- /* put the text block back in the line list and then add any existing elements that we can */
- block->prev = NULL;
- block->next = NULL;
- block->ele_id = NEXT_ELEMENT;
- block->x = state->x;
- block->y = state->y;
- lo_AppendToLineList ( context, state, (LO_Element *) block, 0 );
-
- lo_RelayoutTextElements ( context, state, block, fromElement );
- }
-
- /*
- * Because we're lame for now we just delete all the old linefeeds and lay the text out afresh
- */
-
- /* Tell everybody we're laying out text */
- if (state->cur_ele_type != LO_TEXT)
- {
- lo_FreshText(state);
- state->cur_ele_type = LO_TEXT;
- }
-
- /* actually layout the text */
- state->preformatted = block->format_mode;
-
- if ( lo_UseBreakTable ( block ) )
- {
- lo_SetupBreakState ( block );
-
- /* be sure to set up the editor offset */
- if ( EDT_IS_EDITOR( context ) )
- {
- state->edit_force_offset = TRUE;
- state->edit_current_offset = block->buffer_read_index;
- }
-
- lo_LayoutTextBlock ( context, state, TRUE );
- }
- else
- if ( block->format_mode == PRE_TEXT_NO )
- {
- lo_LayoutFormattedText ( context, state, block );
- }
- else
- {
- lo_LayoutPreformattedText ( context, state, block );
- }
-
- /*
- * If there's text left in the line buffer, then flush it.
- *
- * BRAIN DAMAGE: We don't want to do that here - there may be a following text block
- * that continues this same text buffer.
- */
- lo_FlushLineBuffer(context, state);
-
- return next;
- }
-
- Bool lo_ChangeText ( LO_TextBlock * block, char * text )
- {
- uint32 length;
- /*
- * Reset the text contents for this text block. If we have a break table,
- * then we need to rebuild it.
- */
-
- #ifdef LOCAL_DEBUG
- XP_TRACE( ("Setting text for text block %lx to %s", block, text) );
- #endif
-
- length = XP_STRLEN ( text ) + 1;
- if ( length > block->buffer_write_index )
- {
- if ( !lo_GrowTextBlock ( block, length - block->buffer_write_index ) )
- {
- return FALSE;
- }
- }
-
- if ( lo_UseBreakTable ( block ) )
- {
- block->buffer_write_index = 0;
- block->last_buffer_write_index = 0;
- block->break_write_index = 0;
- block->last_break_offset = 0;
-
- lo_AppendTextToBlock ( NULL, NULL, block, text );
- }
- else
- {
- /* for old style text we just want to replace the buffer */
- XP_BCOPY ( text, (char *) block->text_buffer, length );
- block->buffer_write_index = length;
- }
-
- return TRUE;
- }
-
- /*
- *
- * ======================================================================================================
- *
- * New text layout
- *
- * ======================================================================================================
- */
-
- /*
- * Break Table constants
- */
-
- #define MAX_NATURAL_LENGTH 0xAL
- #define LINE_FEED 0xBL
- #define BYTE_LENGTH 0xCL
- #define WORD_LENGTH 0xDL
- #define LONG_LENGTH 0xEL
- #define MULTI_BYTE 0xFL
-
- #define MULTI_BYTE_DATA_SIZE 16
-
- /*
- * Break Position Magic Constants
- */
- #define OVERRAN_BREAK_TABLE -1
- #define BREAK_LINEFEED -2
-
- #define TEXT_BUFFER_INC 256
- #define BREAK_TABLE_INC 64
-
- typedef struct BreakState
- {
- uint32 buffer_read_index;
- uint32 break_read_index;
- uint32 multibyte_index;
- uint32 multibyte_length;
- uint32 last_line_break;
- uint32 lineLength;
- } BreakState;
-
- static LO_TextBlock * lo_CurrentTextBlock ( MWContext * context, lo_DocState * state );
-
- /* routines to walk through our break table */
- static uint8 * lo_GetNextTextPosition ( LO_TextBlock * block, uint32 * outWordLength, uint32 * outLineLength, Bool * canBreak );
- static uint8 * lo_GetPrevTextPosition ( LO_TextBlock * block, uint32 * outWordLength, uint32 * outLineLength, Bool * canBreak );
- static uint8 * lo_RestoreBreakState ( LO_TextBlock * block, BreakState * state, uint32 * lineLength );
- static void lo_SetLineBreak ( LO_TextBlock * block, Bool skipSpace );
- static uint8 * lo_GetLineStart ( LO_TextBlock * block );
- static void lo_SkipCharacter ( LO_TextBlock * block );
- static Bool lo_SkipInitialSpace ( LO_TextBlock * block );
-
- static Bool lo_SetBreakPosition ( LO_TextBlock * block );
- static Bool lo_SetMultiByteRun ( LO_TextBlock * block, int32 charSize, Bool breakable, Bool eachCharBreakable );
- static Bool lo_SetBreakCommand ( LO_TextBlock * block, uint32 command, uint32 commandLength );
- static void lo_CopyText ( uint8 * src, uint8 * dst, uint32 length );
- static void lo_CopyTextToLineBuffer ( lo_DocState * state, uint8 * src, uint32 length );
-
- static uint32 lo_FindLineBreak ( MWContext * context, lo_DocState * state, LO_TextBlock * block, uint8 * text,
- uint16 * widthTable, uint32 * width, int32 * minWidth, Bool * allTextFits );
-
- /* the parsers */
- static void lo_ParseSingleText ( lo_DocState * state, LO_TextBlock * block, Bool parseAllText, char * text );
- static void lo_ParseSinglePreformattedText ( lo_DocState * state, LO_TextBlock * block, Bool parseAllText, char * text );
- static void lo_ParseDoubleText ( lo_DocState * state, LO_TextBlock * block, Bool parseAllText, char * text );
- static void lo_ParseDoublePreformattedText ( lo_DocState * state, LO_TextBlock * block, Bool parseAllText, char * text );
-
- extern int32 lo_correct_text_element_width(LO_TextInfo *text_info);
-
- #ifdef LOG
- static Bool gHaveLog = FALSE;
- #endif
-
-
- /*
- * Some helpful macros for code inlining
- */
-
- #define SAVE_BREAK_STATE(block,state,line_length) \
- (state)->buffer_read_index = (block)->buffer_read_index; \
- (state)->break_read_index = (block)->break_read_index; \
- (state)->multibyte_index = (block)->multibyte_index; \
- (state)->multibyte_length = (block)->multibyte_length; \
- (state)->last_line_break = (block)->last_line_break; \
- (state)->lineLength = line_length;
-
-
- static LO_TextBlock *
- lo_CurrentTextBlock ( MWContext * context, lo_DocState * state )
- {
- LO_TextBlock * textBlock;
-
- textBlock = state->cur_text_block;
- if ( textBlock == NULL )
- {
- textBlock = (LO_TextBlock *)lo_NewElement ( context, state, LO_TEXTBLOCK, NULL, 0 );
-
- textBlock->type = LO_TEXTBLOCK;
- textBlock->x_offset = 0;
- textBlock->ele_id = NEXT_ELEMENT;
- textBlock->x = state->x;
- textBlock->y = state->y;
- textBlock->y_offset = 0;
- textBlock->width = 0;
- textBlock->height = 0;
- textBlock->line_height = 0;
- textBlock->next = NULL;
- textBlock->prev = NULL;
- textBlock->text_attr = NULL;
- textBlock->anchor_href = NULL;
- textBlock->ele_attrmask = 0;
- textBlock->format_mode = 0;
-
- textBlock->startTextElement = NULL;
- textBlock->endTextElement = NULL;
-
- textBlock->text_buffer = NULL;
- textBlock->buffer_length = 0;
- textBlock->buffer_write_index = 0;
- textBlock->last_buffer_write_index = 0;
- textBlock->buffer_read_index = 0;
- textBlock->last_line_break = 0;
-
- textBlock->break_table = NULL;
- textBlock->break_length = 0;
- textBlock->break_write_index = 0;
- textBlock->break_read_index = 0;
- textBlock->last_break_offset = 0;
- textBlock->multibyte_index = 0;
- textBlock->multibyte_length = 0;
-
- textBlock->old_break = NULL;
- textBlock->old_break_pos = 0;
- textBlock->old_break_width = 0;
-
- textBlock->totalWidth = 0;
- textBlock->totalChars = 0;
- textBlock->break_pending = 0;
- textBlock->last_char_is_whitespace = 0;
-
- textBlock->ascent = 0;
- textBlock->descent = 0;
-
- /* BRAIN DAMAGE: Add this enum to lo_ele.h! */
- textBlock->text_buffer = XP_ALLOC ( TEXT_BUFFER_INC );
- textBlock->buffer_length = TEXT_BUFFER_INC;
-
- textBlock->break_table = XP_ALLOC ( BREAK_TABLE_INC );
- textBlock->break_length = BREAK_TABLE_INC * 2;
-
- /*
- * Since we're creating a new text block, grab some of the text state out of the
- * layout state.
- */
- textBlock->anchor_href = state->current_anchor;
-
- if ( state->font_stack != NULL )
- {
- textBlock->text_attr = state->font_stack->text_attr;
- }
-
- if (state->breakable != FALSE)
- {
- textBlock->ele_attrmask |= LO_ELE_BREAKABLE;
- }
-
- state->cur_text_block = textBlock;
-
- lo_AppendToLineList ( context, state, (LO_Element *) textBlock, 0 );
- }
-
- return textBlock;
- }
-
- void lo_AppendTextToBlock ( MWContext *context, lo_DocState *state, LO_TextBlock * block, char *text )
- {
- Bool parseAllText;
- Bool multiByte;
-
- /*
- * We have several cases in which we can just bail:
- * 1. The text string and the line buffer is empty.
- * 2. The text string is all whitespace and we already have a trailing space.
- */
-
- if ( ( state != NULL ) && ( state->line_buf_len == 0 ) )
- {
- char * t_ptr;
-
- /* if this string is empty, bail */
- if ( *text == '\0' )
- {
- return;
- }
-
- /* if it's only whitespace and we have a trailing space, then bail */
- if ( state->trailing_space )
- {
- t_ptr = text;
-
- while ( *t_ptr != '\0' )
- {
- if ( !XP_IS_SPACE( *t_ptr ) )
- {
- break;
- }
-
- ++t_ptr;
- }
-
- if ( ( *t_ptr == '\0' ) && ( t_ptr != text ) )
- {
- return;
- }
- }
- }
-
- /*
- * If we don't have a block, create one if we have a valid state record. Otherwise we
- * have an error
- */
-
- if ( block == NULL )
- {
- /* the editor may call us with a NULL state and context record, in this case we must always have a block */
- XP_ASSERT(( state != NULL ) && ( context != NULL ));
- if ( ( state != NULL ) && ( context != NULL ) )
- {
- block = lo_CurrentTextBlock ( context, state );
- }
- }
-
- /*
- * OPTIMIZATION: If the parser could tell us if we have a split buffer then we could intelligently set
- * parseAllText here and not buffer words that we think may be split across a buffer but in reality are
- * whole.
- */
-
- /*
- * If we're in an editor context, then we can always parse the whole buffer of text, we never need to
- * worry about partial buffers being passed to us.
- */
- parseAllText = EDT_IS_EDITOR( context );
-
- /*
- * Scan through the text, removing whitespace and adding words to the text buffer as we come across
- * them.
- *
- * Particular things we have to deal with:
- * - Preformatted text (normal, word wrapped and column wrapped)
- * - Normal single byte text
- * - Multibyte text
- * - non-breaking spaces
- */
-
- lo_GetTextParseAtributes ( state, &multiByte );
-
- if ( FALSE || multiByte )
- {
- lo_ParseDoubleText ( state, block, parseAllText, text );
- }
- else
- {
- lo_ParseSingleText ( state, block, parseAllText, text );
- }
- }
-
- static void
- lo_GetTextParseAtributes ( lo_DocState * state, Bool * multiByte )
- {
- int16 charset;
-
- *multiByte = FALSE;
-
- if ( state != NULL )
- {
- charset = state->font_stack->text_attr->charset;
- if ( (INTL_CharSetType ( charset ) != SINGLEBYTE ) && !( INTL_CharSetType ( charset ) & CS_SPACE ) )
- {
- *multiByte = TRUE;
- }
- }
- }
-
- static void
- lo_ParseSingleText ( lo_DocState * state, LO_TextBlock * block, Bool parseAllText, char * text )
- {
- uint8 * t_ptr;
- uint8 * w_start;
- uint8 * w_end;
- uint8 * line_buff;
- uint32 w_length;
- Bool skipped_space;
- uint32 textLength;
-
- /* check for textTransform properties and aply them */
- if( ( state != NULL ) && ( state->top_state && state->top_state->style_stack ) )
- {
- char * property;
-
- StyleStruct *style_struct = STYLESTACK_GetStyleByIndex( state->top_state->style_stack, 0);
-
- if( style_struct )
- {
- property = STYLESTRUCT_GetString(style_struct, TEXT_TRANSFORM_STYLE);
- if(property)
- {
- lo_transform_text_from_string_method(text, property);
- }
- }
- }
-
- t_ptr = (uint8 *) text;
- skipped_space = FALSE;
-
- if ( ( state != NULL ) && ( state->line_buf_len > 0 ) )
- {
- PA_LOCK(line_buff, uint8 *, state->line_buf);
- textLength = state->line_buf_len;
-
- if ( *line_buff == '\0' )
- {
- line_buff = NULL;
- textLength = 0;
- state->line_buf_len = 0;
- }
- }
- else
- {
- line_buff = NULL;
- textLength = 0;
- }
-
- /* Make sure the text block has enough space to hold this block of text */
- textLength += XP_STRLEN ( text );
- lo_GrowTextBlock ( block, textLength + 1 );
-
- /*
- * If there's anything in the line buffer, then pull that out now
- */
- if ( line_buff != NULL )
- {
- /* was there any trailing space left for us? */
- skipped_space = state->trailing_space;
-
- /* skip any white space at the head of our text */
- while ( ( *line_buff != '\0' ) && XP_IS_SPACE ( *line_buff ) )
- {
- line_buff++;
- state->line_buf_len--;
- skipped_space = TRUE;
- }
-
- /*
- * if we skipped any space and we're not at the end of the buffer,
- * then insert a break position
- */
- if ( *line_buff != '\0' )
- {
- if ( skipped_space )
- {
- if ( !lo_SetBreakPosition ( block ) )
- {
- state->top_state->out_of_memory = TRUE;
- return;
- }
-
- /*
- * If the space was real whitespace and not a trailing space from a previous layout,
- * then copy the space to the text block.
- */
- if ( !state->trailing_space )
- {
- block->text_buffer[ block->buffer_write_index ] = ' ';
- block->buffer_write_index++;
- block->last_buffer_write_index++;
- skipped_space = TRUE;
- }
-
- skipped_space = FALSE;
- }
-
- /* copy in the line buffer. it is only allowed to be one word */
- lo_CopyText ( line_buff, &block->text_buffer[ block->buffer_write_index ], state->line_buf_len );
- block->buffer_write_index += state->line_buf_len;
- state->line_buf_len = 0;
- }
- }
-
- /*
- * The last chunk of text may have left a single piece of whitespace at the end of the buffer. If so,
- * we need to skip any whitespace at the front of the buffer so we don't have to worry about this inside
- * the main loop.
- *
- * When called from the editor, we may not have a state record. However, we also won't need to worry
- * about this case as it will have taken care of it for us.
- */
- if ( ( state != NULL ) && ( state->trailing_space ) && ( *t_ptr != '\0' ) )
- {
- skipped_space = TRUE;
-
- while ( ( *t_ptr != '\0' ) && XP_IS_SPACE ( *t_ptr ) )
- {
- t_ptr++;
- }
- }
-
- while ( *t_ptr != '\0' )
- {
- w_start = t_ptr;
-
- /* skip past any whitespace before this word. */
- while ( ( *w_start != '\0' ) && XP_IS_SPACE ( *w_start ) )
- {
- w_start++;
- }
-
- /* run through the text and find the end of the word */
- w_end = w_start;
- w_length = 0;
-
- while ( ( *w_end != '\0' ) && !XP_IS_SPACE ( *w_end ) )
- {
- w_end++;
- w_length++;
- }
-
- /*
- * If we hit the end of the buffer then we may be inside a partial word. This can cause
- * problems with interword kerning, multibyte characters and other contextually sensitive
- * script systems.
- *
- * We buffer this word in the line_buff. If this is truly the end of the text, then we'll
- * be called to flush the last line. We'll do this by appending this word to our text block
- * and then laying out the last of the text.
- *
- * If this word is just split across a buffer, then it will be inserted to the beginning of
- * the next text block.
- *
- * If the caller tells us to parse the whole buffer, then we don't care.
- */
-
- if ( !parseAllText && ( *w_end == '\0' ) && ( w_length > 0 ) && ( state != NULL ) )
- {
- if ( w_start != t_ptr )
- {
- /* put a space in the line buffer */
- lo_CopyTextToLineBuffer ( state, (uint8 *) " ", 1 );
- if ( state->top_state->out_of_memory )
- {
- return;
- }
- }
-
- /* put this text in the line buffer */
- lo_CopyTextToLineBuffer ( state, w_start, w_length );
- if ( state->top_state->out_of_memory )
- {
- return;
- }
-
- /* we now have text after the whitespace */
- skipped_space = FALSE;
-
- break;
- }
-
- /*
- * If we skipped some white space, then we know that we can put a break here.
- */
- if ( w_start != t_ptr )
- {
- skipped_space = TRUE;
-
- /* add the break position */
- if ( !lo_SetBreakPosition ( block ) )
- {
- if ( state != NULL )
- {
- state->top_state->out_of_memory = TRUE;
- }
- return;
- }
-
- if ( block->buffer_length < ( block->buffer_write_index + 1 ) )
- {
- if ( !lo_GrowTextBlock ( block, 1 ) )
- {
- if ( state != NULL )
- {
- state->top_state->out_of_memory = TRUE;
- }
- return;
- }
- }
-
- block->text_buffer[ block->buffer_write_index ] = ' ';
- block->buffer_write_index++;
-
- /*
- * BRAIN DAMAGE: Add a new field to the text block struct to indicate how many
- * chars to skip when calculating the length of the next run.
- */
- block->last_buffer_write_index++;
- }
- else
- {
- skipped_space = FALSE;
- }
-
- /* if we found anything, add it to the buffer */
- if ( w_length > 0 )
- {
- if ( block->buffer_length < ( block->buffer_write_index + w_length ) )
- {
- if ( !lo_GrowTextBlock ( block, w_length ) )
- {
- if ( state != NULL )
- {
- state->top_state->out_of_memory = TRUE;
- }
- return;
- }
- }
-
- lo_CopyText ( w_start, &block->text_buffer[ block->buffer_write_index ], w_length );
- block->buffer_write_index += w_length;
-
- /* we now have text after the whitespace */
- skipped_space = FALSE;
- }
-
- t_ptr = w_end;
- }
-
- /*
- * Remember whether the last thing we added was whitespace
- */
- block->last_char_is_whitespace = skipped_space;
- }
-
-
- /*
- * Parse State Table for two byte text
- */
-
- typedef enum {
- kUnprohibited = PROHIBIT_NOWHERE,
- kBeginProhibited = PROHIBIT_BEGIN_OF_LINE,
- kEndProhibited = PROHIBIT_END_OF_LINE,
- kWordBreakProhibited = PROHIBIT_WORD_BREAK,
- kSingleByte,
- kBreakableSpace,
- kFlushFinalRun,
- kNumCharTypes
- } ParseState;
-
- /*
- * Flags for the state table command
- */
-
- #define SET_BREAKABLE 0x01 /* dump the current run as a single breakable run */
- #define SET_MULTI_BREAKABLE 0x02 /* dump the current run as a mutibyte breakable run */
- #define DUMP_TEXT_AND_BREAK 0x04 /* dump the text to the buffer with no break point and then stop processing */
- #define CARRY_LAST_CHAR 0x08 /* move the last char of this run into the next state */
- #define INC_RUN_LENGTH 0x10 /* inc the length of this run (we could do without this one) */
- #define INSERT_WHITESPACE 0x20 /* insert a single whitespace into the text buffer */
- #define SKIP_CHAR 0x40 /* skip the current char */
- #define MAINTAIN_CHAR_TYPE 0x80 /* don't change the curCharType */
-
- /*
- * Things to Note:
- *
- * 1. SET_BREAKABLE means to insert a normal break command. This run can be broken at the end of the run.
- * 2. SET_MULTI_BREAKABLE means that we have a run which can be broken at the end of every character.
- *
- */
-
- /*
- * The mutibyte breakable run data word is 16 bits big. It is organized as:
- *
- * BIT: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- * FIELD: CHAR SIZE RUN LENGTH
- */
-
- #define MULTI_CHAR_SIZE_MASK 0xE000
- #define MULTI_CHAR_SIZE_SHIFT 12
-
- #define MULTI_LENGTH_MASK 0x1FFF
-
- /*
- * The state table
- */
-
- static uint8 gParseTable[ kNumCharTypes ][ kNumCharTypes ] =
- {
- /* current char next char operations */
-
- /* Unprohibited two byte text */
- /* kUnprohibited, kUnprohibited, */ INC_RUN_LENGTH,
- /* kUnprohibited, kBeginProhibited, */ SET_MULTI_BREAKABLE + CARRY_LAST_CHAR,
- /* kUnprohibited, kEndProhibited, */ SET_MULTI_BREAKABLE,
- /* kUnprohibited, kWordBreakProhibited, */ SET_MULTI_BREAKABLE,
- /* kUnprohibited, kSingleByte, */ SET_MULTI_BREAKABLE,
- /* kUnprohibited, kBreakableSpace, */ SET_MULTI_BREAKABLE + INSERT_WHITESPACE + SKIP_CHAR,
- /* kUnprohibited, kFlushFinalRun, */ SET_MULTI_BREAKABLE,
-
- /* Begin Line Prohibited two byte text */
- /* kBeginProhibited, kUnprohibited, */ SET_BREAKABLE,
- /* kBeginProhibited, kBeginProhibited, */ INC_RUN_LENGTH,
- /* kBeginProhibited, kEndProhibited, */ SET_BREAKABLE,
- /* kBeginProhibited, kWordBreakProhibited, */ SET_BREAKABLE,
- /* kBeginProhibited, kSingleByte, */ SET_BREAKABLE,
- /* kBeginProhibited, kBreakableSpace, */ SET_BREAKABLE + INSERT_WHITESPACE + SKIP_CHAR,
- /* kBeginProhibited, kFlushFinalRun, */ DUMP_TEXT_AND_BREAK,
-
- /* End Line Prohibited two byte text */
- /* kEndProhibited, kUnprohibited, */ INC_RUN_LENGTH + SET_BREAKABLE,
- /* kEndProhibited, kBeginProhibited, */ INC_RUN_LENGTH + SET_BREAKABLE,
- /* kEndProhibited, kEndProhibited, */ INC_RUN_LENGTH,
- /* kEndProhibited, kWordBreakProhibited, */ INC_RUN_LENGTH,
- /* kEndProhibited, kSingleByte, */ INC_RUN_LENGTH,
- /* kEndProhibited, kBreakableSpace, */ INC_RUN_LENGTH + MAINTAIN_CHAR_TYPE, /* BIZZARE CASE! */
- /* kEndProhibited, kFlushFinalRun, */ DUMP_TEXT_AND_BREAK, /* not much we can do here */
-
- /* Word Break Prohibited two byte text */
- /* kWordBreakProhibited, kUnprohibited, */ SET_BREAKABLE,
- /* kWordBreakProhibited, kBeginProhibited, */ INC_RUN_LENGTH,
- /* kWordBreakProhibited, kEndProhibited, */ SET_BREAKABLE,
- /* kWordBreakProhibited, kWordBreakProhibited, */ INC_RUN_LENGTH,
- /* kWordBreakProhibited, kSingleByte, */ INC_RUN_LENGTH,
- /* kWordBreakProhibited, kBreakableSpace, */ SET_BREAKABLE + INSERT_WHITESPACE + SKIP_CHAR,
- /* kWordBreakProhibited, kFlushFinalRun, */ DUMP_TEXT_AND_BREAK,
-
- /* Single byte text */
- /* kSingleByte, kUnprohibited, */ SET_BREAKABLE,
- /* kSingleByte, kBeginProhibited, */ INC_RUN_LENGTH,
- /* kSingleByte, kEndProhibited, */ SET_BREAKABLE,
- /* kSingleByte, kWordBreakProhibited, */ SET_BREAKABLE,
- /* kSingleByte, kSingleByte, */ INC_RUN_LENGTH,
- /* kSingleByte, kBreakableSpace, */ SET_BREAKABLE + INSERT_WHITESPACE + SKIP_CHAR,
- /* kSingleByte, kFlushFinalRun, */ DUMP_TEXT_AND_BREAK,
-
- /* Single byte breakable space */
- /* kBreakableSpace, kUnprohibited, */ 0,
- /* kBreakableSpace, kBeginProhibited, */ 0,
- /* kBreakableSpace, kEndProhibited, */ 0,
- /* kBreakableSpace, kWordBreakProhibited, */ 0,
- /* kBreakableSpace, kSingleByte, */ 0,
- /* kBreakableSpace, kBreakableSpace, */ SKIP_CHAR,
- /* kBreakableSpace, kFlushFinalRun, */ 0,
-
- /* These are never hit */
- /* kFlushFinalRun, kUnprohibited, */ 0,
- /* kFlushFinalRun, kBeginProhibited, */ 0,
- /* kFlushFinalRun, kEndProhibited, */ 0,
- /* kFlushFinalRun, kWordBreakProhibited, */ 0,
- /* kFlushFinalRun, kSingleByte, */ 0,
- /* kFlushFinalRun, kBreakableSpace, */ 0,
- /* kFlushFinalRun, kFlushFinalRun, */ 0,
- };
-
- static void
- lo_ParseDoubleText ( lo_DocState * state, LO_TextBlock * block, Bool parseAllText, char * text )
- {
- char * tptr;
- char * wordStart;
- char * nextWordStart;
- int32 runLength;
- int32 nextRunLength;
- int32 curCharBytes;
- int32 nextCharBytes;
- int16 charset;
- ParseState curCharType;
- ParseState nextCharType;
- uint8 parseCommand;
- uint32 textLength;
- Bool startNewRun;
- Bool eachCharBreakable;
- Bool processLastRun;
-
- tptr = text;
- wordStart = text;
- runLength = 0;
-
- textLength = 0;
-
- processLastRun = FALSE;
-
- /* BRAIN DAMAGE: We need to see if there's anything in the line buffer for us */
-
- /* Make sure the text block has enough space to hold this block of text */
- textLength += XP_STRLEN ( text );
- lo_GrowTextBlock ( block, textLength + 1 );
-
- charset = block->text_attr->charset;
- curCharType = kSingleByte;
- curCharBytes = 1;
-
- eachCharBreakable = FALSE;
-
- /* if we have a trailing space, then set our state to be a space */
- if ( state->trailing_space )
- {
- curCharType = kBreakableSpace;
- runLength = curCharBytes;
- }
-
- startNewRun = FALSE;
-
- while ( ( *tptr != '\0' ) || processLastRun )
- {
- if ( processLastRun )
- {
- /* force the last run to be flushed */
- nextCharType = kFlushFinalRun;
- nextCharBytes = 0;
- }
- else
- {
- /* do we have an ascii char? */
- if ( ( (unsigned char) *tptr ) <= 0x7F )
- {
- nextCharBytes = 1;
-
- /* is the next char a breakable space? */
- if ( XP_IS_SPACE( *tptr ) )
- {
- nextCharType = kBreakableSpace;
- }
- else
- /* it's a normal char */
- {
- nextCharType = kSingleByte;
- }
- }
- else
- {
- /* multibyte, do that international thing */
- nextCharBytes = INTL_CharLen( charset, (unsigned char *) tptr);
- nextCharType = (ParseState) INTL_KinsokuClass( charset, (unsigned char *) tptr );
- }
- }
-
- /* now get our parse command */
- parseCommand = gParseTable[ curCharType ][ nextCharType ];
- nextRunLength = nextCharBytes;
- nextWordStart = tptr;
-
- /*
- * Unprohibited multibyte check - we need to catch cases where our byte size changes. We might
- * want to add a command bit for this check, for now I shall do it this way.
- */
-
- if ( ( curCharType == kUnprohibited ) && ( nextCharType == kUnprohibited ) )
- {
- if ( nextCharBytes != curCharBytes )
- {
- /* ok, our char size changed. we need to dump this run */
- parseCommand |= SET_BREAKABLE;
- }
- }
-
- /* process the command */
- if ( parseCommand & CARRY_LAST_CHAR )
- {
- /* move the last char of this run into the next run */
- runLength -= curCharBytes;
- nextWordStart -= curCharBytes;
-
- /* and move the carried char of the previous run to the next one */
- nextRunLength = curCharBytes + nextCharBytes;
-
- /* if the old run is empty, we don't want to do anything else */
- if ( runLength == 0 )
- {
- parseCommand = 0;
- startNewRun = TRUE;
- }
- }
-
- if ( parseCommand & INC_RUN_LENGTH )
- {
- runLength += nextCharBytes;
- }
-
- if ( parseCommand & SET_BREAKABLE )
- {
- lo_CopyText ( (uint8 *) wordStart, &block->text_buffer[ block->buffer_write_index ], runLength );
- block->buffer_write_index += runLength;
-
- /* set a breakable run */
- lo_SetBreakPosition ( block );
-
- startNewRun = TRUE;
- }
-
- if ( parseCommand & SET_MULTI_BREAKABLE )
- {
- lo_CopyText ( (uint8 *) wordStart, &block->text_buffer[ block->buffer_write_index ], runLength );
- block->buffer_write_index += runLength;
-
- /* set an unbreakable run */
- lo_SetMultiByteRun ( block, curCharBytes, TRUE, eachCharBreakable );
- startNewRun = TRUE;
- }
-
- if ( parseCommand & DUMP_TEXT_AND_BREAK )
- {
- /* copy the text out but don't set a break (only happens when flushing at the end) */
- lo_CopyText ( (uint8 *) wordStart, &block->text_buffer[ block->buffer_write_index ], runLength );
- block->buffer_write_index += runLength;
-
- /* now break out of the loop - we're done */
- break;
- }
-
- if ( startNewRun )
- {
- wordStart = nextWordStart;
- runLength = nextRunLength;
- startNewRun = FALSE;
- }
-
- if ( parseCommand & SKIP_CHAR )
- {
- wordStart++;
- }
-
- if ( parseCommand & INSERT_WHITESPACE )
- {
- block->text_buffer[ block->buffer_write_index ] = ' ';
- block->buffer_write_index++;
- block->last_buffer_write_index++;
- }
-
- curCharBytes = nextCharBytes;
-
- if ( !( parseCommand & MAINTAIN_CHAR_TYPE ) )
- {
- curCharType = nextCharType;
- }
-
- eachCharBreakable = curCharType == kUnprohibited;
-
- tptr += nextCharBytes;
-
- /* if we got here with processLastRun, then we need to bail */
- if ( processLastRun )
- {
- break;
- }
-
- /*
- * if we're on the last character, then we may have a partial run left over. if we're parsing all text
- * then we need to dump it.
- */
- if ( parseAllText && ( *tptr == 0 ) && ( runLength > 0 ) )
- {
- processLastRun = TRUE;
- }
- }
-
- /* if we've ended and we have a run, then we need to save it in the line buffer */
- if ( ( runLength > 0 ) && ( *wordStart != 0 ) )
- {
- lo_CopyTextToLineBuffer ( state,(uint8 *) wordStart, runLength );
- }
- }
-
- static void
- lo_FlushText ( MWContext * context, lo_DocState * state )
- {
- LO_TextBlock * block;
- Bool multiByte;
- char * text_buf;
-
- block = state->cur_text_block;
-
- if ( block != NULL )
- {
- lo_GetTextParseAtributes ( state, &multiByte );
-
- /* add any text to the text block that may be sitting in the line buffer */
- /* BRAIN DAMAGE: These should both be handled the same way */
- if ( multiByte )
- {
- if ( state->line_buf_len > 0 )
- {
- PA_LOCK(text_buf, char *, state->line_buf);
- lo_ParseDoubleText ( state, block, TRUE, text_buf );
- PA_UNLOCK(state->line_buf);
- }
- }
- else
- {
- lo_AppendTextToBlock ( context, state, block, "" );
- }
-
- lo_LayoutTextBlock ( context, state, TRUE );
- }
- }
-
- static void
- lo_SetupBreakState ( LO_TextBlock * block )
- {
- Bool canBreak;
- uint32 wordLength;
- uint32 lineLength;
- uint8 * runEnd;
- uint32 searchReadIndex;
- BreakState breakState;
-
- searchReadIndex = block->buffer_read_index;
-
- block->buffer_read_index = 0;
- block->break_read_index = 0;
- block->last_line_break = 0;
-
- /*
- * We need to update our state to be at the current text position (specified by buffer_read_index)
- */
-
- lineLength = 0;
-
- /* run through the break table until we get to the correct read index */
- while ( block->buffer_read_index < searchReadIndex )
- {
- SAVE_BREAK_STATE ( block, &breakState, lineLength );
-
- runEnd = lo_GetNextTextPosition ( block, &wordLength, &lineLength, &canBreak );
- if ( runEnd == NULL )
- {
- /* this should not happen, but just in case, let's do something kinda reasonable */
- lo_RestoreBreakState ( block, &breakState, NULL );
- break;
- }
- }
-
- /*
- * If we're not at the beginning of the text buffer, then we need to increment buffer_read_index so
- * that we skip the breakable space we're currently at.
- */
- if ( ( block->buffer_read_index > 0 ) && XP_IS_SPACE ( block->text_buffer[ block->buffer_read_index ] ) )
- {
- block->buffer_read_index++;
- }
-
- block->last_line_break = block->buffer_read_index;
- }
-
- void lo_LayoutTextBlock ( MWContext * context, lo_DocState * state, Bool flushLastLine )
- {
- LO_TextBlock * block;
- Bool allTextFits;
- Bool canBreakAtStart;
- LO_TextStruct * text_data;
- LO_TextStruct msTextData;
- uint32 width;
- uint32 lineLength;
- uint8 * text;
- int32 baseline_inc;
- int32 line_inc;
- int32 minWidth;
- int32 * minWidthPtr;
- BreakState breakState;
- uint16 * charLocs;
- Bool freeMeasureBuffer;
- Bool justify;
-
- block = state->cur_text_block;
- if ( block == NULL )
- {
- return;
- }
-
- justify = ( state->align_stack != NULL ) && ( state->align_stack->alignment == LO_ALIGN_JUSTIFY );
-
- /* bail if we're at the end of this block */
- if ( block->buffer_read_index == block->buffer_write_index )
- {
- /* if there's also no text in the line buffer, clear this block */
- if ( state->line_buf_len == 0 )
- {
- state->cur_ele_type = LO_NONE;
- state->cur_text_block = NULL;
- state->trailing_space = block->last_char_is_whitespace;
- }
- return;
- }
-
- lineLength = 0;
-
- charLocs = NULL;
- freeMeasureBuffer = FALSE;
-
- /* find the width of an average character */
- if ( block->totalWidth == 0 )
- {
- memset (&msTextData, 0, sizeof (LO_TextStruct));
- msTextData.text = (PA_Block) "aeiou";
- msTextData.text_attr = block->text_attr;
- msTextData.text_len = 5;
-
- FE_GetTextInfo ( context, &msTextData, &state->text_info );
- block->totalWidth = state->text_info.max_width;
- block->totalChars = msTextData.text_len;
- }
-
- #ifdef XP_MAC
- /* do a quick test to see if we could overflow a UInt16 */
- if ( ( block->buffer_write_index * block->totalWidth / block->totalChars ) < 65535 )
- {
- /* measure the text using the fast measure text */
- if ( ( block->buffer_write_index + 1 ) < kStaticMeasureTextBufferSize )
- {
- charLocs = gMeasureTextBuffer;
- }
- else
- {
- charLocs = XP_ALLOC( ( block->buffer_write_index + 1 ) * sizeof(uint16) );
- freeMeasureBuffer = TRUE;
- }
-
- if ( charLocs != NULL )
- {
- memset (&msTextData, 0, sizeof (LO_TextStruct));
- msTextData.text = (PA_Block) block->text_buffer;
- msTextData.text_attr = block->text_attr;
- msTextData.text_len = block->buffer_write_index;
- FE_MeasureText ( context, &msTextData, (int16 *) charLocs );
-
- /* BRAIN DAMAGE: We need to figure out for sure if the width has ever exceeded a uint16! */
- /* the actual interface for MeasureText says it takes an array of signed ints, however */
- /* the data stuffed in there is unsigned (we quickly overflow a int16 with an 8Kb buffer) */
- }
- }
- #endif
-
- /*
- * Given the current layout state, run through this text block and convert all that we can into text elements.
- * If flushLastLine is set, we want to flush all the text, rather than keeping the last bit in case more text
- * comes.
- */
-
- allTextFits = FALSE;
-
- /* if we're laying out a table, then we need to calculate min widths */
- minWidthPtr = state->need_min_width ? &minWidth : NULL;
-
- while ( !allTextFits )
- {
- canBreakAtStart = FALSE;
-
- /* Save the current break state in case we need to delay on the last line */
- SAVE_BREAK_STATE ( block, &breakState, lineLength );
-
- /*
- * We need to handle the case where we're at the beginning of the line and the
- * first character is a breakable space.
- */
- if ( state->at_begin_line )
- {
- canBreakAtStart = lo_SkipInitialSpace ( block );
- #ifdef LOG
- PR_LogPrint ( "canBreakAtStart: %d\n", canBreakAtStart );
- PR_LogFlush();
- #endif
- }
-
- /* we're looking for a new break position */
- state->break_pos = -1;
- state->break_width = -1;
-
- text = lo_GetLineStart ( block );
-
- lineLength = lo_FindLineBreak ( context, state, block, text, charLocs, &width, minWidthPtr, &allTextFits );
-
- /* update the state's min_width if we need to - this will be constant even if we don't flush this line */
- if ( minWidthPtr != NULL )
- {
- if ( minWidth > state->min_width )
- {
- state->min_width = minWidth;
- }
- }
-
- /*
- * if this line is too long, we either need to break at the beginning of the run (if we can)
- * or break at an old element. If we can't do either of those then we just have to make the line
- * too big
- */
-
- if ( ( width > ( state->right_margin - state->x ) ) && ( state->x > state->left_margin ) )
- {
- /* could we have broken at the start of this line? */
- if ( canBreakAtStart )
- {
- #ifdef LOG
- PR_LogPrint ( "Too long - Breaking at the start of the line\n" );
- PR_LogFlush();
- #endif
-
- /* break the line here */
- lo_SoftLineBreak( context, state, TRUE );
-
- /*
- * BUG BUG: We're restoring the break state to the beginning of the buffer - ie
- * to before the space we skipped above. We need to fix the space skipping mechanism
- * to remove this case (we can go into an infinite loop here if there's not enough space
- * for the first word).
- *
- * Should be able to have a new flag "canSkipSpace" but then actually don't skip it. Then
- * if the line does fit and it's at the beginning, we can skip the space. lo_FindLineBreak
- * should probably be the one to do this work so that the width we get back is correct.
- */
- lo_RestoreBreakState ( block, &breakState, NULL );
-
- /*
- * if all the text fits (ie there was only an unbreakable run left in this block), then
- * we need to stick with this break position. Otherwise we can go find a new one
- */
- if ( !allTextFits )
- {
- continue;
- }
- }
- else
- /* do we have an old break position we can use? */
- if ( state->old_break_pos != -1 )
- {
- #ifdef LOG
- PR_LogPrint ( "Too long - Breaking at the old_break_pos: %ld, width %ld\n"
- state->old_break_pos, state->old_break_width );
- PR_LogFlush();
- #endif
- lo_BreakOldElement ( context, state );
- lo_RestoreBreakState ( block, &breakState, NULL );
-
- /*
- * if all the text fits (ie there was only an unbreakable run left in this block), then
- * we need to stick with this break position. Otherwise we can go find a new one
- */
- if ( !allTextFits )
- {
- continue;
- }
- }
-
- /* we're screwed, we just have to make this line too long */
- }
-
- /*
- * We may not necessarily want to flush the whole buffer out to layout elements (the case where were
- * processing part of a text chunk based on what netlib has streamed to us).
- *
- * We know we do want to flush this next line out if we still have more text in this buffer to process
- * or layout really does want us to flush this whole buffer (because some other element is after us).
- */
- if ( !allTextFits || flushLastLine )
- {
- state->width = width;
-
- if ( lineLength > 0 )
- {
- text_data = (LO_TextStruct *)lo_NewElement ( context, state, LO_TEXT, NULL, 0 );
- if (text_data == NULL)
- {
- #ifdef DEBUG
- assert (state->top_state->out_of_memory);
- #endif
- break;
- }
-
- text_data->type = LO_TEXT;
- text_data->ele_id = NEXT_ELEMENT;
- text_data->x = state->x;
- text_data->x_offset = 0;
- text_data->y = state->y;
- text_data->y_offset = 0;
- text_data->width = width;
- text_data->height = 0;
- text_data->next = NULL;
- text_data->prev = NULL;
-
- text_data->text = (PA_Block) text;
- text_data->text_len = lineLength;
-
- text_data->anchor_href = block->anchor_href;
- text_data->text_attr = block->text_attr;
- text_data->ele_attrmask = block->ele_attrmask;
-
- /* BRAIN DAMAGE: Set LO_ELE_INVISIBLE to mark the element as not having a valid text ptr */
- XP_ASSERT ( !(text_data->ele_attrmask & LO_ELE_INVISIBLE ) );
- text_data->ele_attrmask |= LO_ELE_INVISIBLE;
-
- text_data->sel_start = -1;
- text_data->sel_end = -1;
-
- text_data->doc_width = state->right_margin - state->x;
- text_data->doc_width = 0;
- text_data->block_offset = block->buffer_read_index;
- XP_ASSERT(block->buffer_read_index <= 65535);
-
- /*
- * Some fonts (particulatly italic ones with curly tails
- * on letters like 'f') have a left bearing that extends
- * back into the previous character. Since in this case the
- * previous character is probably not in the same font, we
- * move forward to avoid overlap.
- *
- * Those same funny fonts can extend past the last character,
- * and we also have to catch that, and advance the following text
- * to eliminate cutoff.
- */
- if ( state->text_info.lbearing < 0 )
- {
- text_data->x_offset = state->text_info.lbearing * -1;
- }
-
- baseline_inc = 0;
- line_inc = 0;
-
- /*
- * The baseline of the text element just added to the line may be
- * less than or greater than the baseline of the rest of the line
- * due to font changes. If the baseline is less, this is easy,
- * we just increase y_offest to move the text down so the baselines
- * line up. For greater baselines, we can't move the text up to
- * line up the baselines because we will overlay the previous line,
- * so we have to move all the previous elements in this line down.
- *
- * If the baseline is zero, we are the first element on the line,
- * and we get to set the baseline.
- */
- if ( state->baseline == 0 )
- {
- state->baseline = state->text_info.ascent;
- if (state->line_height <
- (state->baseline + state->text_info.descent))
- {
- state->line_height = state->baseline +
- state->text_info.descent;
- }
- }
- else if ( state->text_info.ascent < state->baseline )
- {
- text_data->y_offset = state->baseline - state->text_info.ascent;
- if ( ( text_data->y_offset + state->text_info.ascent + state->text_info.descent ) > state->line_height )
- {
- line_inc = text_data->y_offset + state->text_info.ascent + state->text_info.descent -
- state->line_height;
- }
- }
- else
- {
- baseline_inc = state->text_info.ascent - state->baseline;
- if ( ( text_data->y_offset + state->text_info.ascent + state->text_info.descent - baseline_inc ) >
- state->line_height)
- {
- line_inc = text_data->y_offset + state->text_info.ascent + state->text_info.descent -
- state->line_height - baseline_inc;
- }
- }
-
- /*
- * Append this element to layout's linelist and our own list of text elements belonging to this block
- */
- lo_AppendToLineList ( context, state, (LO_Element *) text_data, baseline_inc );
- if ( block->startTextElement == NULL )
- {
- block->startTextElement = text_data;
- block->endTextElement = text_data;
- }
- else
- {
- block->endTextElement = text_data;
- }
-
- /* we know we're not at the beginning of the line anymore */
- state->at_begin_line = FALSE;
-
- state->baseline += (intn) baseline_inc;
- state->line_height += (intn) (baseline_inc + line_inc);
- text_data->height = state->text_info.ascent + state->text_info.descent;
-
- /*
- * If the element we just flushed had a breakable word
- * position in it, save that position in case we have
- * to go back and break this element before we finish
- * the line.
- */
- if ( state->break_pos != -1 )
- {
- state->old_break = text_data;
- state->old_break_block = block;
- state->old_break_pos = state->break_pos;
- state->old_break_width = state->break_width;
- }
-
- state->linefeed_state = 0;
- state->x += state->width;
- state->width = 0;
- }
-
- /*
- * If we're still processing text in this buffer, put a linebreak out there
- */
- if ( !allTextFits && !justify )
- {
- lo_SoftLineBreak(context, state, TRUE);
- }
-
- /* tell the break engine that we broke the line here */
- lo_SetLineBreak ( block, !justify );
-
-
- #ifdef EDITOR
- /* tell the editor where we are */
- state->edit_current_offset = block->last_line_break;
- #endif
-
- if ( !( allTextFits && !flushLastLine ) )
- {
- /*
- * Skip the break character if it's whitespace. We don't need to worry about non-breaking spaces
- * here as if the space was non-breaking, we would not have broken the line here
- */
-
- if ( XP_IS_SPACE ( *text ) && !allTextFits )
- {
- /* BRAIN DAMAGE: We should be able to do this at the start of the line */
- /* lo_SkipCharacter ( block ); */
- }
- }
-
- }
- }
-
- if ( flushLastLine )
- {
- state->cur_ele_type = LO_NONE;
- state->cur_text_block = NULL;
- state->trailing_space = block->last_char_is_whitespace;
- }
- else
- if ( allTextFits )
- {
- /* we're still inside a group of text elements */
- state->cur_ele_type = LO_TEXT;
- state->trailing_space = block->last_char_is_whitespace;
-
- /* We're not going to flush the last line, so restore our break state to the start of the line */
- lo_RestoreBreakState ( block, &breakState, NULL );
- }
-
- if ( freeMeasureBuffer )
- {
- XP_FREE( charLocs );
- }
- }
-
- int32 lo_ComputeTextMinWidth ( lo_DocState * state, int32 wordWidth, Bool canBreak );
- int32 lo_ComputeTextMinWidth ( lo_DocState * state, int32 wordWidth, Bool canBreak )
- {
- int32 new_break_holder;
- int32 min_width;
- int32 indent;
-
- new_break_holder = state->x + wordWidth;
- min_width = new_break_holder - state->break_holder;
- indent = state->list_stack->old_left_margin - state->win_left;
- min_width += indent;
-
- /* If we are not within <NOBR> content, allow break_holder
- * to be set to the new position where a line break can occur.
- * This fixes BUG #70782
- */
- if ( ( state->breakable != FALSE ) && canBreak) {
- state->break_holder = new_break_holder;
- }
-
- return min_width;
- }
-
- static uint32
- lo_FindLineBreak ( MWContext * context, lo_DocState * state, LO_TextBlock * block, uint8 * text,
- uint16 * widthTable, uint32 * width, int32 * minWidth, Bool * allTextFits )
- {
- LO_TextStruct text_data;
- uint32 breakCount;
- Bool skipEndSpace;
- Bool haveTooShort;
- Bool canBreak;
- BreakState tooShortBreak;
- Bool haveTooLong;
- uint32 wordLength;
- uint32 breakChar;
- uint32 lineLength;
- uint32 prevLineLength;
- uint8 * wordStart;
- LO_TextInfo text_info;
- uint32 runLength;
- int32 docWidth;
- BreakState breakState;
- uint8 * runEnd;
- int32 oldBreakPos;
- int32 oldBreakWidth;
- int32 lineWidth;
- Bool justify;
- #ifdef BREAK_GUESS_TRACK
- uint32 numForwardMoves;
- uint32 numBackwardMoves;
-
- numForwardMoves = 0;
- numBackwardMoves = 0;
- #endif
-
- *allTextFits = FALSE;
-
- memset (&text_data, 0, sizeof (LO_TextStruct));
- text_data.text = (PA_Block) text;
- text_data.text_attr = block->text_attr;
-
- if ( minWidth != NULL )
- {
- *minWidth = 0;
- }
-
- justify = ( state->align_stack != NULL ) && ( state->align_stack->alignment == LO_ALIGN_JUSTIFY );
-
- /* guess where we want to break this line */
- docWidth = state->right_margin - state->x;
- if ( docWidth < 0 )
- {
- /* we should never get here - the line before us needs to have been broken */
- docWidth = 0;
- }
-
- lineLength = block->buffer_write_index - block->buffer_read_index;
- if ( state->breakable )
- {
- breakChar = docWidth * block->totalChars / block->totalWidth;
- if ( breakChar > lineLength )
- {
- breakChar = lineLength;
- }
- }
- else
- {
- breakChar = lineLength;
- }
-
- /*
- * We first need to walk through the word runs until we get to the first one before our
- * break character.
- *
- * OPTIMIZATION: Make lo_GetNextTextPosition take a breakChar and have it walk forward to that
- * position in an inner loop. This will save us a ton of calls (we currently spend about 5% if our
- * time in lo_GetNextTextPosition - not huge but significant).
- */
-
- lineLength = 0;
-
- breakCount = 0;
- skipEndSpace = FALSE;
-
- wordStart = text;
-
- haveTooShort = FALSE;
- haveTooLong = FALSE;
-
- oldBreakPos = -1;
- oldBreakWidth = -1;
-
- lineWidth = 0;
- #ifdef LOG
- if ( !gHaveLog )
- {
- PR_SetLogFile ( "TextLog" );
- gHaveLog = true;
- }
-
- PR_LogPrint ( "Finding initial break position\n" );
- #endif
-
- /* get the next break position */
- while ( TRUE )
- {
- prevLineLength = lineLength;
-
- #ifdef LOG
- PR_LogPrint ( "Get next break position\n" );
- PR_LogFlush();
- #endif
- /* Save the current break position in case it ends up being the one we need */
- if ( breakCount > 0 && canBreak )
- {
- oldBreakPos = lineLength;
- }
-
- SAVE_BREAK_STATE ( block, &breakState, lineLength );
- runEnd = lo_GetNextTextPosition ( block, &wordLength, &lineLength, &canBreak );
- if ( runEnd == NULL )
- {
- #ifdef LOG
- PR_LogPrint ( "End of break table\n" );
- PR_LogFlush();
- #endif
-
- /* we hit the end of the break table */
- runEnd = lo_RestoreBreakState ( block, &breakState, &lineLength );
- break;
- }
-
- /* do we need to calculate min_width's? */
- if ( minWidth != NULL )
- {
- int32 min_width;
-
- if ( widthTable != NULL )
- {
- uint32 startWordWidth;
- uint32 endWordWidth;
-
- startWordWidth = widthTable[ block->last_line_break + prevLineLength ];
- endWordWidth = widthTable[ block->last_line_break + lineLength ];
-
- runLength = endWordWidth - startWordWidth;
- }
- else
- {
- text_data.text = (PA_Block) wordStart;
- text_data.text_len = lineLength - prevLineLength;
- FE_GetTextInfo ( context, &text_data, &text_info );
-
- runLength = text_info.max_width;
- }
-
- /* add the width of this word into our line width */
- lineWidth += runLength;
-
- /* compute the real min width based on the last break position */
- min_width = lo_ComputeTextMinWidth ( state, lineWidth, canBreak );
- if ( min_width > *minWidth )
- {
- *minWidth = min_width;
- }
-
- wordStart = runEnd;
- }
-
- #ifdef LOG
- PR_LogPrint ( "wordlen: %lu, lineLength: %lu, canBreak: %d, runEnd: %s\n", wordLength, lineLength, canBreak, runEnd );
- PR_LogFlush();
- #endif
-
- /* Are we where we want to be yet? */
- if ( lineLength >= breakChar )
- {
- #ifdef LOG
- PR_LogPrint ( "Moved past, back up\n" );
- PR_LogFlush();
- #endif
-
- /* if we moved past it then back up if we can */
- if ( ( lineLength > breakChar ) && ( breakCount > 0 ) )
- {
- runEnd = lo_RestoreBreakState ( block, &breakState, &lineLength );
- --breakCount;
- }
-
- break;
- }
-
- /* if we're justifying text, then we just dump the next break position */
- if ( justify )
- {
- break;
- }
-
- /* we can now back up to something */
- ++breakCount;
- }
-
- /*
- * So now we're looking at the nearest break position to where we guessed we'd want to be.
- * Now we loop measuring this line of text until we find the best break position
- */
-
- #ifdef LOG
- PR_LogPrint ( "Finding actual break position\n" );
- PR_LogFlush();
- #endif
-
- while ( TRUE )
- {
- if ( widthTable != NULL )
- {
- uint32 startLineWidth;
- uint32 endLineWidth;
-
- startLineWidth = widthTable[ block->last_line_break ];
- endLineWidth = widthTable[ block->last_line_break + lineLength ];
-
- runLength = endLineWidth - startLineWidth;
- }
- else
- {
- text_data.text = (PA_Block) text;
- text_data.text_len = lineLength;
- FE_GetTextInfo ( context, &text_data, &text_info );
-
- runLength = text_info.max_width;
- }
-
- #ifdef LOG
- PR_LogPrint ( "lineLength: %lu, width: %lu, docWidth: %lu, text: %s\n", lineLength, runLength, docWidth, text );
- PR_LogFlush();
- #endif
- /* if we're justified text, then we just dump the word we have now */
- if ( justify )
- {
- /* save this break position */
- if ( canBreak )
- {
- oldBreakPos = lineLength;
- oldBreakWidth = runLength;
-
- /* if the next char along is a space, then we need to include it */
- if ( ( runEnd != NULL ) && XP_IS_SPACE ( *runEnd ) )
- {
- ++lineLength;
- }
- }
-
- break;
- }
- else
- /* are we non-breakable text? */
- if ( !state->breakable )
- {
- /*
- * We always want to move to the end of the text block. We should already be
- * there from the loop above.
- */
- #ifdef LOG
- PR_LogPrint ( "Non-breakable, always get next break point\n" );
- PR_LogFlush();
- #endif
-
- /* go forward one break position and measure again (including min width) */
- SAVE_BREAK_STATE ( block, &tooShortBreak, lineLength );
- haveTooShort = TRUE;
-
- #ifdef BREAK_GUESS_TRACK
- ++numForwardMoves;
- #endif
- wordStart = runEnd;
- runEnd = lo_GetNextTextPosition ( block, &wordLength, &lineLength, &canBreak );
- if ( runEnd == NULL )
- {
- /* we've already at the end of the line */
- runEnd = lo_RestoreBreakState ( block, &tooShortBreak, &lineLength );
- #ifdef LOG
- PR_LogPrint ( "Non-breakable text, hit end of block: %lu, runEnd: %s\n", lineLength, runEnd );
- PR_LogFlush();
- #endif
- /* update min_width */
- if ( minWidth != NULL )
- {
- int32 min_width;
-
- /* compute the real min width based on the last break position - we cannot break here */
- min_width = lo_ComputeTextMinWidth ( state, runLength, FALSE );
- if ( min_width > *minWidth )
- {
- *minWidth = min_width;
- }
- }
- break;
- }
- }
- else
- /* have we gone too far? */
- if ( runLength > docWidth )
- {
- #ifdef LOG
- PR_LogPrint ( "Too long\n" );
- PR_LogFlush();
- #endif
-
- /* if we found a break position before this one that was too short, choose the too short one */
- if ( haveTooShort )
- {
- #ifdef LOG
- PR_LogPrint ( "Using too short\n" );
- PR_LogFlush();
- #endif
- runEnd = lo_RestoreBreakState ( block, &tooShortBreak, &lineLength );
- break;
- }
-
- /* if we have something to back up to, then do so. Otherwise we have to break here */
- if ( breakCount > 0 )
- {
- #ifdef LOG
- PR_LogPrint ( "Backing up to previous break position\n" );
- PR_LogFlush();
- #endif
- #ifdef BREAK_GUESS_TRACK
- ++numBackwardMoves;
- #endif
-
- /* mark that we've been to far and back up one */
- haveTooLong = TRUE;
- runEnd = lo_GetPrevTextPosition ( block, &wordLength, &lineLength, &canBreak );
- if ( runEnd == NULL )
- {
- /* we need to break at a previous break position on this line... */
- break;
- }
-
- wordStart = runEnd;
- --breakCount;
- continue;
- }
- else
- {
- #ifdef LOG
- PR_LogPrint ( "Nothing to back up to, bailing\n" );
- PR_LogFlush();
- #endif
- break;
- }
- }
- else
- /* have we not gone far enough? */
- if ( runLength < docWidth )
- {
- #ifdef LOG
- PR_LogPrint ( "Too short\n" );
- PR_LogFlush();
- #endif
- /* if we have a too long break position, then we know we're straddling the break point, choose */
- /* this one */
- if ( haveTooLong )
- {
- #ifdef LOG
- PR_LogPrint ( "Using this break, next is too long\n" );
- PR_LogFlush();
- #endif
- break;
- }
-
- /* save this break position */
- if ( canBreak )
- {
- oldBreakPos = lineLength;
- oldBreakWidth = runLength;
- }
-
- /* go forward one break position and measure again (including min width) */
- SAVE_BREAK_STATE ( block, &tooShortBreak, lineLength );
- haveTooShort = TRUE;
-
- #ifdef BREAK_GUESS_TRACK
- ++numForwardMoves;
- #endif
-
- wordStart = runEnd;
- runEnd = lo_GetNextTextPosition ( block, &wordLength, &lineLength, &canBreak );
- if ( runEnd == NULL )
- {
- /* we've already at the end of the line */
- runEnd = lo_RestoreBreakState ( block, &tooShortBreak, &lineLength );
- #ifdef LOG
- PR_LogPrint ( "No next break position, use this one. length: %lu, runEnd: %s\n", lineLength, runEnd );
- PR_LogFlush();
- #endif
- break;
- }
-
- /*
- * Update min width.
- */
- if ( minWidth != NULL )
- {
- int32 min_width;
-
- if ( widthTable != NULL )
- {
- uint32 startLineWidth;
- uint32 endLineWidth;
-
- startLineWidth = widthTable[ block->last_line_break ];
- endLineWidth = widthTable[ block->last_line_break + lineLength ];
-
- runLength = endLineWidth - startLineWidth;
- }
- else
- {
- text_data.text = (PA_Block) wordStart;
- text_data.text_len = wordLength;
- FE_GetTextInfo ( context, &text_data, &text_info );
-
- /* add the length of this word into the length of the line */
- runLength += text_info.max_width;
- }
-
- /* compute the real min width based on the last break position */
- min_width = lo_ComputeTextMinWidth ( state, runLength, canBreak );
- if ( min_width > *minWidth )
- {
- *minWidth = min_width;
- }
- }
-
- ++breakCount;
- continue;
- }
- else
- {
- /* we're spot on! */
- break;
- }
- }
-
- /*
- * We may be in a nasty case where our current break position is before our
- * last saved one in oldBreakPos. This can happen when we move backwards
- * from our initial break guess. To correct this, we need to back up from our
- * current break point, get the new position and then move forward again.
- */
- if ( ( oldBreakPos != -1 ) && ( oldBreakPos >= lineLength ) )
- {
- uint32 dummyWordLength;
- uint32 prevBreakPos;
- Bool dummyCanBreak;
- uint8 * prevRunEnd;
-
- #ifdef BREAK_GUESS_TRACK
- ++numBackwardMoves;
- #endif
- prevBreakPos = lineLength;
-
- SAVE_BREAK_STATE ( block, &breakState, prevBreakPos );
- prevRunEnd = lo_GetPrevTextPosition ( block, &dummyWordLength, &prevBreakPos, &dummyCanBreak );
- if ( prevRunEnd != NULL )
- {
- /* we found a valid previous break, so use it */
- oldBreakPos = prevBreakPos;
- oldBreakWidth = -1;
- }
- else
- {
- /* nothing to back up to, so don't record any old break */
- oldBreakPos = -1;
- oldBreakWidth = -1;
- }
-
- /* restore the current break state */
- lo_RestoreBreakState ( block, &breakState, &prevBreakPos );
- }
-
- text_data.text = (PA_Block) text;
-
- /* If we don't have a width for the oldBreakPos, measure one now */
- if ( ( oldBreakPos != -1 ) && ( oldBreakWidth == -1 ) )
- {
- if ( widthTable != NULL )
- {
- uint32 startLineWidth;
- uint32 endLineWidth;
-
- startLineWidth = widthTable[ block->last_line_break ];
- endLineWidth = widthTable[ block->last_line_break + oldBreakPos ];
-
- oldBreakWidth = endLineWidth - startLineWidth;
- }
- else
- {
- text_data.text_len = oldBreakPos;
- FE_GetTextInfo ( context, &text_data, &text_info );
- oldBreakWidth = text_info.max_width;
- }
- }
-
- text_data.text_len = lineLength;
-
- /* if we're breaking at a space at the end of this line, don't measure it */
- if ( skipEndSpace )
- {
- --text_data.text_len;
- }
-
- /* BRAIN DAMAGE: We don't need this - already got all the info */
- if ( widthTable != NULL )
- {
- uint32 startLineWidth;
- uint32 endLineWidth;
-
- /* this is really lame */
- text_data.text = (PA_Block) text;
- text_data.text_len = 1;
- FE_GetTextInfo ( context, &text_data, &text_info );
-
- startLineWidth = widthTable[ block->last_line_break ];
- endLineWidth = widthTable[ block->last_line_break + lineLength ];
-
- text_info.max_width = endLineWidth - startLineWidth;
- }
- else
- {
- text_data.text = (PA_Block) text;
- text_data.text_len = lineLength;
- FE_GetTextInfo ( context, &text_data, &text_info );
- }
-
- /* update our char width average */
- block->totalWidth += text_info.max_width;
- block->totalChars += lineLength;
-
- *width = lo_correct_text_element_width( &text_info );
-
- /* BRAIN DAMAGE: Pass this into the FE call */
- state->text_info = text_info;
-
- /* check to see if we're at the end of the buffer */
- if ( block->buffer_read_index == block->buffer_write_index )
- {
- *allTextFits = TRUE;
- }
-
- /* save the last break position */
- if ( oldBreakPos != -1 )
- {
- state->break_pos = oldBreakPos;
- state->break_width = oldBreakWidth;
- }
-
- #ifdef LOG
- PR_LogPrint ( "Final lineLength: %lu, allTextFits: %d, text: %s\n", text_data.text_len, *allTextFits, text );
- PR_LogFlush();
- #endif
-
- #ifdef BREAK_GUESS_TRACK
- XP_TRACE(("Num forward, backward break moves after initial guess: %ld, %ld", numForwardMoves, numBackwardMoves ));
- #endif
-
- return lineLength;
- }
-
-
- static uint8 *
- lo_GetNextTextPosition ( LO_TextBlock * block, uint32 * outWordLength, uint32 * outLineLength, Bool * canBreak )
- {
- uint32 breakCommand;
- uint32 breakLong;
- uint32 breakIndex;
- uint32 lineLength;
- uint32 nibbleCount;
- uint32 dataNibbles;
- int32 wordLength;
- uint32 * breakTable;
- uint8 * endTextRun;
-
- /*
- * Sanity check for already being at the end of the buffer
- */
-
- if ( block->buffer_read_index == block->buffer_write_index )
- {
- *outWordLength = 0;
- *canBreak = FALSE;
- return NULL;
- }
-
- /* are we in a run of breakable multibyte characters? */
- if ( block->multibyte_length > 0 )
- {
- uint16 charSize;
-
- /* BRAIN DAMAGE */
- /* turn this next line on when the uint16 multibyte_char_size field is added to LO_TextBlock */
- #if 0
- charSize = block->multibyte_char_size;
- #else
- charSize = 2;
- #endif
- block->multibyte_index += charSize;
-
- /* are we at the end of this run? */
- if ( block->multibyte_index == block->multibyte_length )
- {
- block->multibyte_length = 0;
- block->multibyte_index = 0;
- }
-
- /* bump by one character */
- *outWordLength = charSize;
- (*outLineLength) += charSize;
- *canBreak = TRUE;
-
- block->buffer_read_index += 2;
- endTextRun = &block->text_buffer[ block->buffer_read_index ];
-
- return endTextRun;
- }
-
- /* assume we will be able to break */
- *canBreak = TRUE;
-
- lineLength = block->buffer_read_index;
- breakIndex = block->break_read_index;
-
- /* are we at the end of the break table? */
- if ( breakIndex < block->break_write_index )
- {
- /* nope, so grab the next break position */
- breakTable = &block->break_table[ breakIndex >> 3 ];
-
- /* cache this in the TextBlock */
- breakLong = ( *breakTable++ ) << ( ( breakIndex & 0x7 ) << 2 );
- wordLength = 0;
-
- /* get the next break command */
- breakCommand = breakLong >> 28;
- breakLong <<= 4;
- if ( ( ++breakIndex & 0x7 ) == 0 )
- {
- breakLong = *breakTable++;
- }
-
- if ( breakCommand <= MAX_NATURAL_LENGTH )
- {
- /* a nibble of length data, we already have all the info we need */
- wordLength = breakCommand;
- dataNibbles = 0;
- }
- else
- if ( breakCommand == LINE_FEED )
- {
- /* we should only get this when parsing preformatted text */
- wordLength = BREAK_LINEFEED;
- dataNibbles = 0;
- }
- else
- if ( breakCommand == BYTE_LENGTH )
- {
- dataNibbles = 2;
- }
- else
- if ( breakCommand == WORD_LENGTH )
- {
- dataNibbles = 4;
- }
- else
- if ( breakCommand == MULTI_BYTE )
- {
- dataNibbles = 4;
- }
- else
- {
- /* a 24 bits of data */
- dataNibbles = 6;
- }
-
- if ( dataNibbles > 0 )
- {
- /* read in the actual count and the tail command header */
- for ( nibbleCount = dataNibbles; nibbleCount > 0; --nibbleCount )
- {
- wordLength <<= 4;
- wordLength |= breakLong >> 28;
-
- breakLong <<= 4;
- if ( ( ++breakIndex & 0x7 ) == 0 )
- {
- breakLong = *breakTable++;
- }
- }
-
- /* now skip the tail end of the command */
- ++breakIndex;
- }
-
- /* if multi byte, then extract the real data */
- if ( breakCommand == MULTI_BYTE )
- {
- int32 runLength;
-
- *canBreak = TRUE;
-
- runLength = wordLength & MULTI_LENGTH_MASK;
-
- block->multibyte_length = runLength;
- block->multibyte_index = 2;
-
- /* make sure we're not already at the end of the run */
- if ( block->multibyte_index == block->multibyte_length )
- {
- block->multibyte_index = 0;
- block->multibyte_length = 0;
- }
-
- /* extract the real word length from the command */
- wordLength = ( wordLength & MULTI_CHAR_SIZE_MASK ) >> MULTI_CHAR_SIZE_SHIFT;
-
- /* MAJOR BRAIN DAMAGE: WE NEED TO STORE THIS IN THE TEXT BLOCK AS IT WILL NOT */
- /* ALWAYS BE TWO BYTE!!!! */
- XP_ASSERT( wordLength == 2 );
- }
-
- lineLength += wordLength;
-
- /*
- * if we actually have a word here and are not the first word on the line, then
- * add one to the length to account for the interword space. We know we're not the
- * first word on the line if we're not at the linebreak or if we're at the start of the
- * buffer but have already skipped a break position (this happens when the first
- * character of the buffer is a breaking space).
- */
- if ( ( wordLength > 0 ) && ( ( block->last_line_break != block->buffer_read_index ) ||
- ( ( block->buffer_read_index == 0 ) && ( block->break_read_index > 0 ) ) ) )
- {
- /* only true if there's a space here */
- if ( XP_IS_SPACE ( block->text_buffer[ block->buffer_read_index ] ) )
- {
- lineLength++;
- (*outLineLength)++;
- }
- }
- }
- else
- {
- /*
- * We're at the end of the break table. We may have some text after this last break.
- * Either way, we cannot break here
- */
-
- *canBreak = FALSE;
-
- if ( lineLength < block->buffer_write_index )
- {
- lineLength = block->buffer_write_index;
- wordLength = lineLength - block->buffer_read_index;
- }
- }
-
- block->break_read_index = breakIndex;
- block->buffer_read_index = lineLength;
-
- endTextRun = &block->text_buffer[ lineLength ];
-
- *outLineLength += wordLength;
- *outWordLength = wordLength;
-
- return endTextRun;
- }
-
-
- static Bool
- lo_ExtractPrevBreakCommand ( LO_TextBlock * block, Bool * multiByte, uint32 * command )
- {
- Bool hasPrev;
- uint32 breakCommand;
- uint32 commandData;
- uint32 breakLong;
- uint32 breakIndex;
- uint32 * breakTable;
- uint32 nibbleCount;
- uint32 dataNibbles;
- Bool readPrevCommand;
-
- commandData = 0;
- *multiByte = FALSE;
-
- breakIndex = block->break_read_index;
-
- hasPrev = block->break_read_index > 0;
- if ( hasPrev )
- {
- readPrevCommand = TRUE;
-
- /* are we at the absolute end of the buffer? */
- if ( block->buffer_read_index == block->buffer_write_index )
- {
- /* Yup, so back up to the last break offset. */
- commandData = block->buffer_read_index - block->last_break_offset;
-
- /* was there really a true ending break command? */
- if ( commandData > 0 )
- {
- readPrevCommand = FALSE;
-
- /* this length was before the breaking space, add it back in */
- --commandData;
- }
- }
-
- /* extract the previous command from the table if we need to */
- if ( readPrevCommand )
- {
- /* nope, so back up within the break table */
- --breakIndex;
- breakTable = &block->break_table[ breakIndex >> 3 ];
-
- breakLong = *breakTable;
-
- /* shift the command down and extract the data */
- breakCommand = ( breakLong >> ( ( 7 - ( breakIndex & 0x7 ) ) << 2 ) ) & 0xF;
- if ( breakCommand <= MAX_NATURAL_LENGTH )
- {
- /* a nibble of length data, we already have all the info we need */
- dataNibbles = 0;
- commandData = breakCommand;
- }
- else
- if ( breakCommand == LINE_FEED )
- {
- /* we should only get this when parsing preformatted text */
- dataNibbles = 0;
- commandData = breakCommand;
- }
- else
- if ( breakCommand == BYTE_LENGTH )
- {
- /* a byte of length data */
- dataNibbles = 2;
- }
- else
- if ( breakCommand == WORD_LENGTH )
- {
- /* a short of length data */
- dataNibbles = 4;
- }
- else
- if ( breakCommand == MULTI_BYTE )
- {
- /* 16 bits of data */
- dataNibbles = 4;
- *multiByte = TRUE;
- }
- else
- {
- /* a 24 bits of data */
- dataNibbles = 6;
- }
-
- if ( dataNibbles > 0 )
- {
- /* skip the command tail */
- if ( ( --breakIndex & 0x7 ) == 7 )
- {
- breakLong = *--breakTable;
- }
-
- /* read in the actual count and the tail command header */
- for ( nibbleCount = 0; nibbleCount < dataNibbles; ++nibbleCount )
- {
- uint32 nibble;
-
- /* grab the next nibble */
- nibble = ( breakLong >> ( ( 7 - ( breakIndex & 0x7 ) ) << 2 ) ) & 0xF;
- commandData |= nibble << ( nibbleCount << 2 );
-
- if ( ( --breakIndex & 0x7 ) == 7 )
- {
- breakLong = *--breakTable;
- }
- }
- }
- }
- }
-
- block->break_read_index = breakIndex;
-
- *command = commandData;
-
- return hasPrev;
- }
-
- static uint8 *
- lo_GetPrevTextPosition ( LO_TextBlock * block, uint32 * outWordLength, uint32 * outLineLength, Bool * canBreak )
- {
- uint32 wordLength;
- uint32 breakCommand;
- uint32 lineLength;
- uint8 * endTextRun;
- uint32 totalSkip;
- Bool havePrevCommand;
- Bool multiByte;
-
- *canBreak = FALSE;
- totalSkip = 0;
-
- /*
- * Sanity check for already being at the beginning of the line
- */
- if ( block->buffer_read_index == block->last_line_break )
- {
- *outWordLength = 0;
- return NULL;
- }
-
- /* are we in a run of breakable multibyte characters? */
- if ( block->multibyte_length > 0 )
- {
- uint16 charSize;
-
- /* BRAIN DAMAGE */
- /* turn this next line on when the uint16 multibyte_char_size field is added to LO_TextBlock */
- #if 0
- charSize = block->multibyte_char_size;
- #else
- charSize = 2;
- #endif
- block->multibyte_index -= charSize;
-
- /* are we at the end of this run? */
- if ( block->multibyte_index == 0 )
- {
- block->multibyte_length = 0;
- }
-
- /* bump by one character */
- *outWordLength = charSize;
- (*outLineLength) -= charSize;
- *canBreak = TRUE;
-
- block->buffer_read_index -= 2;
- endTextRun = &block->text_buffer[ block->buffer_read_index ];
-
- return endTextRun;
- }
-
- /* back up to the previous command */
- havePrevCommand = lo_ExtractPrevBreakCommand ( block, &multiByte, &breakCommand );
- if ( !havePrevCommand )
- {
- *outWordLength = 0;
- return NULL;
- }
-
- /* back ourselves up in the buffer */
- if ( multiByte )
- {
- uint32 charSize;
-
- *canBreak = TRUE;
-
- /* extract the real word length from the command */
- charSize = ( breakCommand & MULTI_CHAR_SIZE_MASK ) >> MULTI_CHAR_SIZE_SHIFT;
-
- block->multibyte_length = breakCommand & MULTI_LENGTH_MASK;
- block->multibyte_index = block->multibyte_length - charSize;
-
- /* make sure we're not already at the beginning of the run */
- if ( block->multibyte_index == 0 )
- {
- block->multibyte_length = 0;
- }
-
- /* MAJOR BRAIN DAMAGE: WE NEED TO STORE THIS IN THE TEXT BLOCK AS IT WILL NOT */
- /* ALWAYS BE TWO BYTE!!!! */
- XP_ASSERT( charSize == 2 );
-
- /* the length of this run is the char size */
- wordLength = charSize;
- }
- else
- {
- wordLength = breakCommand;
- }
-
- lineLength = block->buffer_read_index;
-
- /*
- * if we actually have a word here and are not the first word on the line, then
- * subtract one to the length to account for the interword space.
- */
- if ( ( wordLength > 0 ) && ( block->last_line_break != ( lineLength - wordLength )) )
- {
- /* only true if there's a space here */
- if ( XP_IS_SPACE ( block->text_buffer[ lineLength - wordLength - 1 ] ) )
- {
- lineLength--;
- (*outLineLength)--;
- }
- }
-
- lineLength -= wordLength;
-
- block->buffer_read_index = lineLength;
-
- endTextRun = &block->text_buffer[ lineLength ];
-
- *outLineLength -= wordLength;
- *outWordLength = wordLength;
-
- /* We can't break if we've backed up all the way to the start of the buffer and there is */
- /* no break position there */
- if ( ( block->break_read_index == 0 ) && ( wordLength == 0 ) )
- {
- *canBreak = FALSE;
- }
- else
- {
- *canBreak = TRUE;
- }
-
- return endTextRun;
- }
-
- static Bool
- lo_SetBreakCommand ( LO_TextBlock * block, uint32 command, uint32 commandLength )
- {
- uint32 break_write_index;
- uint32 * break_table;
-
- /* record the current break position as it may be the last entry in the break table */
- block->last_break_offset = block->buffer_write_index;
- block->last_buffer_write_index = block->buffer_write_index;
-
- break_write_index = block->break_write_index;
-
- /* grow the break table if we need to - always have space for one long of data */
- if ( ( break_write_index + 8 ) > block->break_length )
- {
- /* allocate in bytes, count in nibbles */
- block->break_length += BREAK_TABLE_INC * 2;
- block->break_table = XP_REALLOC ( block->break_table, block->break_length / 2 );
- }
-
- break_table = block->break_table;
-
- if ( break_table != NULL )
- {
- uint32 nibble_index;
-
- nibble_index = break_write_index & 0x7;
-
- /* write the command */
- if ( nibble_index == 0 )
- {
- /* we're long aligned, write the sucker */
- break_table[ break_write_index >> 3 ] = command;
- }
- else
- {
- uint32 table_long;
- uint32 table_index;
-
- table_index = break_write_index >> 3;
- table_long = break_table[ table_index ];
-
- table_long |= command >> ( nibble_index << 2 );
- break_table[ table_index ] = table_long;
-
- /* how many nibbles did we write, and were they enough? */
- nibble_index = 0x8 - nibble_index;
- if ( commandLength > nibble_index )
- {
- /* need to write out some more data */
- break_table[ table_index + 1 ] = command << ( nibble_index << 2 );
- }
- }
-
- break_write_index += commandLength;
- }
-
- block->break_write_index = break_write_index;
-
- return break_table != NULL;
- }
-
- static Bool
- lo_SetBreakPosition ( LO_TextBlock * block )
- {
- uint32 w_length;
- uint32 data_long;
- uint32 command_size;
- Bool success;
-
- /* how many characters were added for this run */
- w_length = block->buffer_write_index - block->last_buffer_write_index;
- XP_ASSERT ( w_length >= 0 );
-
- if ( w_length <= MAX_NATURAL_LENGTH )
- {
- /* one data nibble */
- command_size = 1;
- data_long = w_length << 28;
- }
- else
- if ( w_length <= 255 )
- {
- /* a command nibble, two data nibbles and then a terminator nibble */
- command_size = 4;
- data_long = ( BYTE_LENGTH << 28 ) | ( w_length << 20 ) | ( BYTE_LENGTH << 16 );
- }
- else
- if ( w_length <= 65535 )
- {
- /* a command nibble, four data nibbles and then a terminator nibble */
- command_size = 6;
- data_long = ( WORD_LENGTH << 28 ) | ( w_length << 12 ) | ( WORD_LENGTH << 8 );
- }
- else
- {
- /* we can have 24 bits of data at most... */
- XP_ASSERT ( w_length <= ( ( 1 << 24 ) - 1 ) );
-
- /* a command nibble, six data nibbles and then a terminator nibble */
- command_size = 8;
- data_long = ( LONG_LENGTH << 28 ) | ( w_length << 4 ) | ( LONG_LENGTH );
- }
-
- success = lo_SetBreakCommand ( block, data_long, command_size );
-
- return success;
- }
-
- static Bool
- lo_SetMultiByteRun ( LO_TextBlock * block, int32 charSize, Bool breakable, Bool eachCharBreakable )
- {
- uint32 w_length;
- uint32 data_long;
- uint32 command_size;
- Bool success;
-
- /* how many characters were added for this run */
- w_length = block->buffer_write_index - block->last_buffer_write_index;
- XP_ASSERT ( w_length >= 0 );
-
- /*
- * There are two cases where we can use a normal simple break entry:
- * 1. We are not breakable on every char and the text is breakable (the normal break table case).
- * 2. We can break on every char, but we only have one char in the run
- */
- if ( breakable )
- {
- if ( !eachCharBreakable || ( charSize == w_length ) )
- {
- return lo_SetBreakPosition ( block );
- }
- }
-
- /* BRAIN DAMAGE: We need to handle overflow of our data word */
-
- /* this break command always has 16 bits of data and so it is 24 bits big */
- command_size = 6;
-
- data_long = w_length;
-
- data_long |= ( charSize << MULTI_CHAR_SIZE_SHIFT ) & MULTI_CHAR_SIZE_MASK;
-
- XP_ASSERT( w_length <= MULTI_LENGTH_MASK );
- data_long |= ( w_length & MULTI_LENGTH_MASK );
-
- /* set the command nibbles */
- data_long = ( (uint32) MULTI_BYTE << ( MULTI_BYTE_DATA_SIZE + 4 ) ) | (uint32) ( data_long << 4 ) | (uint32) MULTI_BYTE;
- data_long <<= 8;
-
- success = lo_SetBreakCommand ( block, data_long, command_size );
-
- return success;
- }
-
- void lo_BreakOldTextBlockElement(MWContext *context, lo_DocState *state)
- {
- LO_TextBlock * block;
- LO_TextStruct * text_data;
- LO_TextStruct * new_text_data;
- char * text;
- char * breakPtr;
- int32 save_width;
- uint32 newTextlength;
- LO_TextInfo text_info;
- int32 base_change;
- int32 old_baseline;
- int32 old_line_height;
- int32 adjust;
- int32 baseline_inc;
- LO_Element * tptr;
- LO_Element * eptr;
- LO_Element * line_ptr;
-
- /* note that the block can be null for a word break element */
- block = state->old_break_block;
-
- /* Move to the element we will break */
- text_data = state->old_break;
-
- /* If there is no text there to break it is an error. */
- if ( text_data == NULL )
- {
- return;
- }
-
- new_text_data = NULL;
-
- /*
- * Later operations will trash the width field.
- * So save it now to restore later.
- */
- save_width = state->width;
-
- /*
- * If this element has no text, then it's a special word break
- * element. We can simply remove it and then add a linefeed and then
- * insert the remaining text on the line buffer
- */
- if ( text_data->text == NULL )
- {
- /*
- * Back up the state to this element's location
- */
- state->x = text_data->x;
- state->y = text_data->y;
-
- tptr = text_data->next;
- text_data->next = NULL;
-
- state->width = text_data->width;
- state->x += state->width;
-
- /* add a line feed */
- lo_SoftLineBreak(context, state, TRUE);
- }
- else
- {
- /*
- * We're trying to break inside an actual text element. We need to shorten
- * this element to point up to this break position, then add a linefeed, then
- * create a new element that contains the remaining text and place it on the
- * line buffer
- */
-
- /* if we're breaking an element, we must have a text block */
- if ( block == NULL )
- {
- return;
- }
-
- PA_LOCK(text, char *, text_data->text);
-
- /*
- * Back the state up to this element's
- * location, break off the rest of the elements
- * and save them for later.
- * Flush this line, and insert a linebreak.
- */
- state->x = text_data->x;
- state->y = text_data->y;
- tptr = text_data->next;
- text_data->next = NULL;
-
- breakPtr = &text[ state->old_break_pos ];
- newTextlength = text_data->text_len - state->old_break_pos;
-
- text_data->block_offset = text_data->block_offset - text_data->text_len + state->old_break_pos;
- text_data->text_len = state->old_break_pos;
-
- FE_GetTextInfo(context, text_data, &text_info);
- state->width = lo_correct_text_element_width( &text_info );
-
- text_data->width = state->width;
- PA_UNLOCK(text_data->text);
- state->x += state->width;
-
- /* this element should point at the space before the next word, so skip it */
- if ( XP_IS_SPACE ( *breakPtr ) )
- {
- ++breakPtr;
- --newTextlength;
- }
-
- /*
- * If the element that caused this break has a different
- * baseline than the element we are breaking, we need to
- * preserve that difference after the break.
- */
- base_change = state->baseline - (text_data->y_offset + text_info.ascent);
-
- old_baseline = state->baseline;
- old_line_height = state->line_height;
-
- /*
- * Reset element_id so they are still sequencial.
- */
- state->top_state->element_id = text_data->ele_id + 1;
-
- /*
- * If we are breaking an anchor, we need to make sure the
- * linefeed gets its anchor href set properly.
- */
- if (text_data->anchor_href != NULL)
- {
- LO_AnchorData *tmp_anchor;
-
- tmp_anchor = state->current_anchor;
- state->current_anchor = text_data->anchor_href;
- lo_SoftLineBreak(context, state, TRUE);
- state->current_anchor = tmp_anchor;
- }
- else
- {
- lo_SoftLineBreak(context, state, TRUE);
- }
-
- adjust = lo_baseline_adjust( context, state, tptr, old_baseline, old_line_height );
- state->baseline = old_baseline - adjust;
- state->line_height = (intn) old_line_height - adjust;
-
- /* now create a new text element */
- baseline_inc = -1 * adjust;
-
- new_text_data = (LO_TextStruct *)lo_NewElement ( context, state, LO_TEXT, text_data->edit_element, text_data->edit_offset+text_data->text_len+1 );
- if (text_data == NULL)
- {
- #ifdef DEBUG
- assert (state->top_state->out_of_memory);
- #endif
- return;
- }
-
- new_text_data->type = LO_TEXT;
- new_text_data->ele_id = NEXT_ELEMENT;
- new_text_data->x = state->x;
- new_text_data->x_offset = 0;
- new_text_data->y = state->y;
- new_text_data->y_offset = 0;
- new_text_data->height = 0;
- new_text_data->next = NULL;
- new_text_data->prev = NULL;
-
- new_text_data->anchor_href = block->anchor_href;
- new_text_data->text_attr = block->text_attr;
- new_text_data->ele_attrmask = block->ele_attrmask;
-
- /* BRAIN DAMAGE: Set LO_ELE_INVISIBLE to mark the element as not having a valid text ptr */
- XP_ASSERT ( !(new_text_data->ele_attrmask & LO_ELE_INVISIBLE ) );
- new_text_data->ele_attrmask |= LO_ELE_INVISIBLE;
-
- new_text_data->sel_start = -1;
- new_text_data->sel_end = -1;
-
- new_text_data->doc_width = state->right_margin - state->x;
- new_text_data->doc_width = 0;
- new_text_data->block_offset = text_data->block_offset + text_data->text_len;
- XP_ASSERT(new_text_data->block_offset <= 65535);
-
- new_text_data->text = (PA_Block) breakPtr;
- new_text_data->text_len = newTextlength;
- FE_GetTextInfo(context, new_text_data, &text_info);
- new_text_data->width = lo_correct_text_element_width(&text_info);
-
- /*
- * Some fonts (particulatly italic ones with curly
- * tails on letters like 'f') have a left bearing
- * that extends back into the previous character.
- * Since in this case the previous character is
- * probably not in the same font, we move forward
- * to avoid overlap.
- */
- if (text_info.lbearing < 0)
- {
- new_text_data->x_offset = text_info.lbearing * -1;
- }
-
- /*
- * The baseline of the text element just inserted in
- * the line may be less than or greater than the
- * baseline of the rest of the line due to font
- * changes. If the baseline is less, this is easy,
- * we just increase y_offest to move the text down
- * so the baselines line up. For greater baselines,
- * we can't move the text up to line up the baselines
- * because we will overlay the previous line, so we
- * have to move all rest of the elements in this line
- * down.
- *
- * If the baseline is zero, we are the first element
- * on the line, and we get to set the baseline.
- */
- if (state->baseline == 0)
- {
- state->baseline = text_info.ascent;
- }
- else
- if (text_info.ascent < state->baseline)
- {
- new_text_data->y_offset = state->baseline - text_info.ascent;
- }
- else
- {
- baseline_inc = baseline_inc + (text_info.ascent - state->baseline);
- state->baseline = text_info.ascent;
- }
-
- /*
- * Now that we have broken, and added the new
- * element, we need to move it down to restore the
- * baseline difference that previously existed.
- */
- new_text_data->y_offset -= base_change;
-
- /*
- * Calculate the height of this new
- * text element.
- */
- new_text_data->height = text_info.ascent + text_info.descent;
- state->x += new_text_data->width;
- }
-
- /*
- * if our previous element was the last text element for this block, then our
- * new element is the new end
- */
- if ( block->endTextElement == text_data )
- {
- block->endTextElement = new_text_data;
- }
-
- /*
- * Now add the remaining elements to the line list
- */
-
- /* first find the end of the line list */
- line_ptr = state->line_list;
- while ((line_ptr != NULL)&&(line_ptr->lo_any.next != NULL))
- {
- line_ptr = line_ptr->lo_any.next;
- }
-
- /* now add the new_text_data if there is one */
- if ( new_text_data != NULL )
- {
- if (line_ptr == NULL)
- {
- state->line_list = (LO_Element *) new_text_data;
- new_text_data->prev = NULL;
- line_ptr = (LO_Element *) new_text_data;
- }
- else
- {
- line_ptr->lo_any.next = (LO_Element *) new_text_data;
- new_text_data->prev = line_ptr;
- line_ptr = (LO_Element *) new_text_data;
- }
- }
-
- /* and then add tptr */
- if ( tptr != NULL )
- {
- if (line_ptr == NULL)
- {
- state->line_list = tptr;
- tptr->lo_any.prev = NULL;
- line_ptr = tptr;
- }
- else
- {
- line_ptr->lo_any.next = tptr;
- tptr->lo_any.prev = line_ptr;
- line_ptr = tptr;
- }
- }
-
- /* if we've added a new element then increment the element id's of the following elements */
- if ( new_text_data != NULL )
- {
- eptr = tptr;
-
- while (eptr != NULL)
- {
- eptr->lo_any.ele_id = NEXT_ELEMENT;
- eptr->lo_any.y_offset += baseline_inc;
- eptr = eptr->lo_any.next;
- }
-
- /* and bump the line height if we need to */
- if ( ( new_text_data->y_offset + new_text_data->height ) > state->line_height )
- {
- state->line_height = (intn) new_text_data->y_offset + new_text_data->height;
- }
- }
-
- /*
- * Upgrade forward the x and y text positions in the document
- * state.
- */
- while ( tptr != NULL )
- {
- lo_UpdateElementPosition ( state, tptr );
- tptr = tptr->lo_any.next;
- }
-
- state->at_begin_line = FALSE;
- state->width = save_width;
- }
-
- static uint8 *
- lo_GetLineStart ( LO_TextBlock * block )
- {
- return &block->text_buffer[ block->last_line_break ];
- }
-
-
- static void
- lo_SkipCharacter ( LO_TextBlock * block )
- {
- ++block->buffer_read_index;
- }
-
-
- static void
- lo_SetLineBreak ( LO_TextBlock * block, Bool skipSpace )
- {
- /* if the current character is a space, we can skip it as we're breaking here */
- /* NOT FOR PREFORMATTED TEXT THOUGH */
- if ( skipSpace && ( XP_IS_SPACE ( block->text_buffer[ block->buffer_read_index ] ) ) )
- {
- ++block->buffer_read_index;
- }
-
- block->last_line_break = block->buffer_read_index;
- }
-
- static uint8 *
- lo_RestoreBreakState ( LO_TextBlock * block, BreakState * state, uint32 * lineLength )
- {
- uint8 * runEnd;
-
- block->buffer_read_index = state->buffer_read_index;
- block->break_read_index = state->break_read_index;
- block->multibyte_index = state->multibyte_index;
- block->multibyte_length = state->multibyte_length;
- block->last_line_break = state->last_line_break;
-
- if ( lineLength != NULL )
- {
- *lineLength = state->lineLength;
- }
-
- runEnd = &block->text_buffer[ block->buffer_read_index ];
-
- return runEnd;
- }
-
-
- static Bool
- lo_SkipInitialSpace ( LO_TextBlock * block )
- {
- Bool canBreak;
- uint32 breakLong;
- uint32 breakIndex;
-
- canBreak = FALSE;
- breakIndex = block->break_read_index;
-
- /* do we have a break point at the current text offset */
- if ( ( breakIndex == 0 ) && ( breakIndex < block->break_write_index ) && XP_IS_SPACE ( block->text_buffer[ 0 ] ) )
- {
- breakLong = block->break_table[ breakIndex >> 3 ];
- breakLong >>= ( 7 - breakIndex & 0x7 ) << 2;
-
- if ( breakLong == 0 )
- {
- block->buffer_read_index++;
- block->break_read_index++;
- block->last_line_break++;
- canBreak = TRUE;
- }
- }
-
- return canBreak;
- }
-
-
- Bool lo_GrowTextBlock ( LO_TextBlock * block, uint32 length )
- {
- Bool success = TRUE;
- uint32 growBy;
- uint32 oldTextBase;
- uint32 offset;
- LO_TextStruct * textElement;
- LO_TextStruct * endElement;
-
- if ( block->buffer_length < ( block->buffer_write_index + length ) )
- {
- /* need to make sure that the new size is enough to contain the new data */
- growBy = TEXT_BUFFER_INC;
- if ( growBy < length )
- {
- growBy = length;
- }
-
- oldTextBase = (uint32) block->text_buffer;
-
- growBy += block->buffer_length;
- block->text_buffer = XP_REALLOC ( block->text_buffer, growBy );
-
- block->buffer_length = growBy;
- success = block->text_buffer != NULL;
-
- /* update any break table related information */
- if ( success && ( block->break_table != NULL ) )
- {
- /*
- * Run through all the text elements and adjust their text addresses to point to the newly
- * relocated block
- */
-
- textElement = block->startTextElement;
- endElement = block->endTextElement;
-
- while ( textElement != NULL )
- {
- if ( textElement->type == LO_TEXT )
- {
- offset = (uint32) textElement->text - oldTextBase;
- textElement->text = (PA_Block) ( (uint32) block->text_buffer + offset );
- }
-
- if ( textElement == endElement )
- {
- break;
- }
-
- textElement = (LO_TextStruct *) textElement->next;
- }
- }
- }
-
- return success;
- }
-
- static void
- lo_CopyText ( uint8 * src, uint8 * dst, uint32 length )
- {
- char c;
-
- /* copy a text string, converting non breaking spaces to normal spaces as we go */
- while ( length-- )
- {
- c = *src++;
-
- if ( c == NON_BREAKING_SPACE )
- {
- c = ' ';
- }
-
- *dst++ = c;
- }
- }
-
- static void
- lo_CopyTextToLineBuffer ( lo_DocState * state, uint8 * src, uint32 length )
- {
- char * text_buf;
-
- /* do we need to grow the buffer? */
- if ( ( state->line_buf_len + length + 1 ) > state->line_buf_size )
- {
- state->line_buf = PA_REALLOC ( state->line_buf, ( state->line_buf_size + length + LINE_BUF_INC ) );
- if ( state->line_buf == NULL )
- {
- state->top_state->out_of_memory = TRUE;
- return;
- }
- }
-
- PA_LOCK(text_buf, char *, state->line_buf);
-
- XP_BCOPY ( (char *) src, (char *) ( text_buf + state->line_buf_len ), ( length + 1 ) );
- state->line_buf_len += length;
-
- text_buf[ state->line_buf_len ] = 0;
-
- PA_UNLOCK(state->line_buf);
- }
-
- #ifdef TEST_16BIT
- #undef XP_WIN16
- #endif /* TEST_16BIT */
-
- #ifdef PROFILE
- #pragma profile off
- #endif
-