home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / layout / laytext.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  214.8 KB  |  8,472 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19.  
  20.  
  21. #include "xp.h"
  22. #include "pa_tags.h"
  23. #include "layout.h"
  24. #include "laylayer.h"
  25. #include "layers.h"
  26. #include "libi18n.h"
  27. #include "edt.h"
  28. #include "laystyle.h"
  29. #include "laytrav.h"
  30.  
  31. /*
  32.  * Turn this define on to get the new multibyte parsing code
  33. #define    FAST_MULTI
  34. */
  35.  
  36. #define    FAST_EDITOR
  37.  
  38. #ifdef TEST_16BIT
  39. #define XP_WIN16
  40. #endif /* TEST_16BIT */
  41.  
  42. #ifdef XP_WIN16
  43. #define SIZE_LIMIT              32000
  44. #endif /* XP_WIN16 */
  45.  
  46. #ifdef XP_UNIX
  47. #define TEXT_CHUNK_LIMIT    500
  48. #else
  49. #define TEXT_CHUNK_LIMIT    1600
  50. #endif
  51.  
  52. #ifdef XP_MAC
  53. #define NON_BREAKING_SPACE    0x07
  54. #else
  55. #define NON_BREAKING_SPACE    160
  56. #endif
  57.  
  58. #ifdef PROFILE
  59. #pragma profile on
  60. #endif
  61.  
  62. int32 lo_correct_text_element_width(LO_TextInfo *);
  63.  
  64. static void lo_insert_quote_characters(MWContext *context, lo_DocState *state);
  65. static LO_TextStruct * lo_new_text_element(MWContext *context, lo_DocState *state, ED_Element *edit_element,
  66.      intn edit_offset );
  67.  
  68. void lo_LayoutFormattedText(MWContext *context, lo_DocState *state, LO_TextBlock * block);
  69. void lo_LayoutPreformattedText(MWContext *context, lo_DocState *state, LO_TextBlock * block);
  70. LO_TextBlock * lo_NewTextBlock ( MWContext * context, lo_DocState * state, char * text, uint16 formatMode );
  71. int32 lo_compute_text_basline_inc ( lo_DocState * state, LO_TextBlock * block, LO_TextStruct * text_data );
  72.  
  73. uint32 lo_FindBlockOffset ( LO_TextBlock * block, LO_TextStruct * fromElement );
  74. void lo_RelayoutTextElements ( MWContext * context, lo_DocState * state, LO_TextBlock * block, LO_TextStruct * fromElement );
  75. Bool lo_CanUseBreakTable ( lo_DocState * state );
  76. Bool lo_UseBreakTable ( LO_TextBlock * block );
  77.  
  78. void lo_AppendTextToBlock ( MWContext *context, lo_DocState *state, LO_TextBlock * block, char *text );
  79. void lo_LayoutTextBlock ( MWContext * context, lo_DocState * state, Bool flushLastLine );
  80. static void lo_FlushText ( MWContext * context, lo_DocState * state );
  81. static void lo_SetupBreakState ( LO_TextBlock * block );
  82.  
  83. Bool lo_GrowTextBlock ( LO_TextBlock * block, uint32 length );
  84. static void lo_GetTextParseAtributes ( lo_DocState * state, Bool * multiByte );
  85.  
  86.  
  87. #ifdef XP_MAC
  88. #define    kStaticMeasureTextBufferSize    512
  89. static uint16 gMeasureTextBuffer[ kStaticMeasureTextBufferSize ];
  90.  
  91. /* This needs to go in fe_proto.h - will move it there once we're out of the branch */
  92. #define FE_MeasureText(context, text, charLocs) \
  93.             (*context->funcs->MeasureText)(context, text, charLocs)
  94. #endif
  95.  
  96. #ifdef XP_MAC
  97. Bool gCallNewText = TRUE;
  98. #endif
  99.  
  100. LO_TextBlock * lo_NewTextBlock ( MWContext * context, lo_DocState * state, char * text, uint16 formatMode )
  101. {
  102.     LO_TextBlock *    block;
  103.     uint32            length;
  104.     
  105.     length = strlen ( text );
  106.     
  107.     block = (LO_TextBlock *)lo_NewElement ( context, state, LO_TEXTBLOCK, NULL, 0 );
  108.     if ( block == NULL )
  109.         {
  110.         state->top_state->out_of_memory = TRUE;
  111.         return NULL;
  112.         }
  113.         
  114.     block->type = LO_TEXTBLOCK;
  115.     block->x_offset = 0;
  116.     block->ele_id = NEXT_ELEMENT;
  117.     block->x = state->x;
  118.     block->y = state->y;
  119.     block->y_offset = 0;
  120.     block->width = 0;
  121.     block->height = 0;
  122.     block->line_height = 0;
  123.     block->next = NULL;
  124.     block->prev = NULL;
  125.     block->text_attr = NULL;
  126.     block->anchor_href = NULL;
  127.     block->ele_attrmask = 0;
  128.     block->format_mode = 0;
  129.     
  130.     block->startTextElement = NULL;
  131.     block->endTextElement = NULL;
  132.     
  133.     block->text_buffer = NULL;
  134.     block->buffer_length = 0;
  135.     block->buffer_write_index = 0;
  136.     block->last_buffer_write_index = 0;
  137.     block->buffer_read_index = 0;
  138.     block->last_line_break = 0;
  139.     
  140.     block->break_table = NULL;
  141.     block->break_length = 0;
  142.     block->break_write_index = 0;
  143.     block->break_read_index = 0;
  144.     block->last_break_offset = 0;
  145.     
  146.     block->multibyte_index = 0;
  147.     block->multibyte_length = 0;
  148.     
  149.     block->old_break = NULL;
  150.     block->old_break_pos = 0;
  151.     block->old_break_width = 0;
  152.     
  153.     block->totalWidth = 0;
  154.     block->totalChars = 0;
  155.     block->break_pending = 0;
  156.     block->last_char_is_whitespace = 0;
  157.     
  158.     block->ascent = 0;
  159.     block->descent = 0;
  160.     
  161.     block->text_buffer = XP_ALLOC ( length + 1 );
  162.     if ( block->text_buffer == NULL )
  163.         {
  164.         XP_FREE ( block );
  165.         state->top_state->out_of_memory = TRUE;
  166.         return NULL;
  167.         }
  168.     
  169.     block->buffer_length = length + 1;
  170.     XP_BCOPY ( text, block->text_buffer, length );
  171.     block->text_buffer[ length ] = '\0';
  172.     block->buffer_write_index = length;
  173.     
  174.     /* default text does not have a break table */
  175.     block->break_table = NULL;
  176.     block->break_length = 0;
  177.     
  178.     block->format_mode = formatMode;
  179.     
  180.     /*
  181.      * Since we're creating a new text block, grab some of the text state out of the
  182.      * layout state.
  183.      */
  184.     block->anchor_href = state->current_anchor;
  185.     
  186.     if ( state->font_stack != NULL )
  187.         {
  188.         block->text_attr = state->font_stack->text_attr;
  189.         }
  190.         
  191.     if (state->breakable != FALSE)
  192.         {
  193.         block->ele_attrmask |= LO_ELE_BREAKABLE;
  194.         }
  195.     
  196.     state->cur_text_block = block;
  197.  
  198.     lo_AppendToLineList ( context, state, (LO_Element *) block, 0 );
  199.     
  200.     return block;
  201. }
  202.  
  203. void
  204. lo_SetDefaultFontAttr(lo_DocState *state, LO_TextAttr *tptr,
  205.     MWContext *context)
  206. {
  207.     tptr->size = DEFAULT_BASE_FONT_SIZE;
  208.     tptr->fontmask = 0;
  209.     tptr->no_background = TRUE;
  210.     tptr->attrmask = 0;
  211.     tptr->fg.red =   STATE_DEFAULT_FG_RED(state);
  212.     tptr->fg.green = STATE_DEFAULT_FG_GREEN(state);
  213.     tptr->fg.blue =  STATE_DEFAULT_FG_BLUE(state);
  214.     tptr->bg.red =   STATE_DEFAULT_BG_RED(state);
  215.     tptr->bg.green = STATE_DEFAULT_BG_GREEN(state);
  216.     tptr->bg.blue =  STATE_DEFAULT_BG_BLUE(state);
  217.     tptr->charset = INTL_DefaultTextAttributeCharSetID(context);
  218.     tptr->font_face = NULL;
  219.     tptr->FE_Data = NULL;
  220.     tptr->point_size = 0;
  221.     tptr->font_weight = 0;
  222. }
  223.  
  224.  
  225. /*************************************
  226.  * Function: lo_DefaultFont
  227.  *
  228.  * Description: This function sets up the text attribute
  229.  *      structure for the default text drawing font.
  230.  *      This is the font that sits at the bottom of the font
  231.  *    stack, and can never be popped off.
  232.  *
  233.  * Params: Document state
  234.  *
  235.  * Returns: A pointer to a lo_FontStack structure.
  236.  *    Returns a NULL on error (such as out of memory);
  237.  *************************************/
  238. lo_FontStack *
  239. lo_DefaultFont(lo_DocState *state, MWContext *context)
  240. {
  241.     LO_TextAttr tmp_attr;
  242.     lo_FontStack *fptr;
  243.     LO_TextAttr *tptr;
  244.  
  245.     /*
  246.      * Fill in default font information.
  247.      */
  248.     lo_SetDefaultFontAttr(state, &tmp_attr, context);
  249.     tptr = lo_FetchTextAttr(state, &tmp_attr);
  250.  
  251.     fptr = XP_NEW(lo_FontStack);
  252.     if (fptr == NULL)
  253.     {
  254.         return(NULL);
  255.     }
  256.     fptr->tag_type = P_UNKNOWN;
  257.     fptr->text_attr = tptr;
  258.     fptr->next = NULL;
  259.  
  260.     return(fptr);
  261. }
  262.  
  263.  
  264. void
  265. lo_PushAlignment(lo_DocState *state, intn tag_type, int32 alignment)
  266. {
  267.     lo_AlignStack *aptr;
  268.  
  269.     aptr = XP_NEW(lo_AlignStack);
  270.     if (aptr == NULL)
  271.     {
  272.         state->top_state->out_of_memory = TRUE;
  273.         return;
  274.     }
  275.  
  276.     aptr->type = tag_type;
  277.     aptr->alignment = alignment;
  278.  
  279.     aptr->next = state->align_stack;
  280.     state->align_stack = aptr;
  281. }
  282.  
  283.  
  284. lo_AlignStack *
  285. lo_PopAlignment(lo_DocState *state)
  286. {
  287.     lo_AlignStack *aptr;
  288.  
  289.     aptr = state->align_stack;
  290.     if (aptr != NULL)
  291.     {
  292.         state->align_stack = aptr->next;
  293.         aptr->next = NULL;
  294.     }
  295.     return(aptr);
  296. }
  297.  
  298. void
  299. lo_PushLineHeight(lo_DocState *state, int32 height)
  300. {
  301.     lo_LineHeightStack *aptr;
  302.  
  303.     aptr = XP_NEW(lo_LineHeightStack);
  304.     if (aptr == NULL)
  305.     {
  306.         state->top_state->out_of_memory = TRUE;
  307.         return;
  308.     }
  309.  
  310.     aptr->height = height;
  311.  
  312.     aptr->next = state->line_height_stack;
  313.     state->line_height_stack = aptr;
  314. }
  315.  
  316. lo_LineHeightStack *
  317. lo_PopLineHeight(lo_DocState *state)
  318. {
  319.     lo_LineHeightStack *aptr;
  320.  
  321.     aptr = state->line_height_stack;
  322.     if (aptr != NULL)
  323.     {
  324.         state->line_height_stack = aptr->next;
  325.         aptr->next = NULL;
  326.     }
  327.     return(aptr);
  328. }
  329.  
  330. /*************************************
  331.  * Function: lo_FreshText
  332.  *
  333.  * Description: This function clears out the information from the
  334.  *    document state that refers to text in the midst of being
  335.  *    laid out.  It is called to clear the layout state before
  336.  *    Laying out new text potentially in a new font.
  337.  *
  338.  * Params: Document state structure.
  339.  *
  340.  * Returns: Nothing
  341.  *************************************/
  342. void
  343. lo_FreshText(lo_DocState *state)
  344. {
  345.     state->text_info.max_width = 0;
  346.     state->text_info.ascent = 0;
  347.     state->text_info.descent = 0;
  348.     state->text_info.lbearing = 0;
  349.     state->text_info.rbearing = 0;
  350.     state->line_buf_len = 0;
  351.     state->break_pos = -1;
  352.     state->break_width = 0;
  353.     state->last_char_CR = FALSE;
  354. #ifdef EDITOR
  355.     if( !state->edit_force_offset )
  356.     {
  357.         state->edit_current_offset = 0;
  358.     }
  359. #endif
  360. }
  361.  
  362.  
  363. /*************************************
  364.  * Function: lo_new_text_element
  365.  *
  366.  * Description: Create a new text element structure based
  367.  *    on the current text information stored in the document
  368.  *    state.
  369.  *
  370.  * Params: Document state
  371.  *
  372.  * Returns: A pointer to a LO_TextStruct structure.
  373.  *    Returns a NULL on error (such as out of memory);
  374.  *************************************/
  375. static LO_TextStruct *
  376. lo_new_text_element(MWContext *context, lo_DocState *state, ED_Element *edit_element,
  377.         intn edit_offset )
  378. {
  379.     LO_TextStruct *text_ele = NULL;
  380. #ifdef DEBUG
  381.     assert (state);
  382. #endif
  383.  
  384.     if (state == NULL)
  385.     {
  386.         return(NULL);
  387.     }
  388.  
  389.     text_ele = (LO_TextStruct *)lo_NewElement(context, state, LO_TEXT, 
  390.             edit_element, edit_offset);
  391.     if (text_ele == NULL)
  392.     {
  393. #ifdef DEBUG
  394.         assert (state->top_state->out_of_memory);
  395. #endif
  396.         return(NULL);
  397.     }
  398.  
  399.     text_ele->type = LO_TEXT;
  400.     text_ele->ele_id = NEXT_ELEMENT;
  401.     text_ele->x = state->x;
  402.     text_ele->x_offset = 0;
  403.     text_ele->y = state->y;
  404.     text_ele->y_offset = 0;
  405.     text_ele->width = 0;
  406.     text_ele->height = 0;
  407.     text_ele->next = NULL;
  408.     text_ele->prev = NULL;
  409.  
  410.     if (state->line_buf_len > 0)
  411.     {
  412.         text_ele->text = PA_ALLOC((state->line_buf_len + 1) *
  413.             sizeof(char));
  414.         if (text_ele->text != NULL)
  415.         {
  416.             char *text_buf;
  417.             char *line;
  418.  
  419.             PA_LOCK(line, char *, state->line_buf);
  420.             PA_LOCK(text_buf, char *, text_ele->text);
  421.             XP_BCOPY(line, text_buf, state->line_buf_len);
  422.             text_buf[state->line_buf_len] = '\0';
  423.             PA_UNLOCK(text_ele->text);
  424.             PA_UNLOCK(state->line_buf);
  425.             text_ele->text_len = (int16)state->line_buf_len;
  426.         }
  427.         else
  428.         {
  429.             state->top_state->out_of_memory = TRUE;
  430.             /*
  431.              * free text element && return; it's no different
  432.              * than if we had run out of memory allocating
  433.              * the text element
  434.              */
  435.             lo_FreeElement(context, (LO_Element *)text_ele, FALSE);
  436.             return(NULL);
  437.         }
  438.     }
  439.     else
  440.     {
  441.         text_ele->text = NULL;
  442.         text_ele->text_len = 0;
  443.     }
  444.  
  445.     text_ele->anchor_href = state->current_anchor;
  446.  
  447.     if (state->font_stack == NULL)
  448.     {
  449.         text_ele->text_attr = NULL;
  450.     }
  451.     else
  452.     {
  453.         text_ele->text_attr = state->cur_text_block->text_attr;
  454.     }
  455.  
  456.     text_ele->ele_attrmask = 0;
  457.     if (state->breakable != FALSE)
  458.     {
  459.         text_ele->ele_attrmask |= LO_ELE_BREAKABLE;
  460.     }
  461.  
  462.     text_ele->sel_start = -1;
  463.     text_ele->sel_end = -1;
  464.  
  465.     return(text_ele);
  466. }
  467.  
  468.  
  469. /*
  470.  * A bogus an probably too expensive function to fill in the
  471.  * textinfo for whatever font is now on the font stack.
  472.  */
  473. MODULE_PRIVATE void
  474. lo_fillin_text_info(MWContext *context, lo_DocState *state)
  475. {
  476.     LO_TextStruct tmp_text;
  477.     PA_Block buff;
  478.     char *str;
  479.  
  480.     memset (&tmp_text, 0, sizeof (tmp_text));
  481.     buff = PA_ALLOC(1);
  482.     if (buff == NULL)
  483.     {
  484.         return;
  485.     }
  486.     PA_LOCK(str, char *, buff);
  487.     str[0] = ' ';
  488.     PA_UNLOCK(buff);
  489.     tmp_text.text = buff;
  490.     tmp_text.text_len = 1;
  491.     
  492.     /* if we have a text block, use it's font info, otherwise get it from the state */
  493.     if ( state->cur_text_block == NULL )
  494.         {
  495.         tmp_text.text_attr = state->font_stack->text_attr;
  496.         }
  497.     else
  498.         {
  499.         tmp_text.text_attr = state->cur_text_block->text_attr;
  500.         }
  501.         
  502.     FE_GetTextInfo(context, &tmp_text, &(state->text_info));
  503.     PA_FREE(buff);
  504. }
  505.  
  506.  
  507. /*************************************
  508.  * Function: lo_NewLinefeed
  509.  *
  510.  * Description: Create a new linefeed element structure based
  511.  *    on the current information stored in the document
  512.  *    state.
  513.  *
  514.  * Params: Document state, linefeed type
  515.  *
  516.  * Returns: A pointer to a LO_LinefeedStruct structure.
  517.  *    Returns a NULL on error (such as out of memory);
  518.  *************************************/
  519. LO_LinefeedStruct *
  520. lo_NewLinefeed(lo_DocState *state, MWContext * context, uint32 break_type, uint32 clear_type)
  521. {    
  522.     LO_LinefeedStruct *linefeed = NULL;
  523.  
  524.     if (state == NULL)
  525.     {
  526.         return(NULL);
  527.     }
  528.  
  529.     linefeed = (LO_LinefeedStruct *)lo_NewElement(context, state, LO_LINEFEED, NULL, 0);
  530. #ifdef DEBUG
  531.     assert (state);
  532. #endif
  533.     if (linefeed == NULL)
  534.     {
  535. #ifdef DEBUG
  536.         assert (state->top_state->out_of_memory);
  537. #endif
  538.         state->top_state->out_of_memory = TRUE;
  539.         return(NULL);
  540.     }
  541.  
  542.     lo_FillInLineFeed( context, state, break_type, clear_type, linefeed );
  543.  
  544.     /*
  545.     linefeed->type = LO_LINEFEED;
  546.     linefeed->ele_id = NEXT_ELEMENT;
  547.     linefeed->x = state->x;
  548.     linefeed->x_offset = 0;
  549.     linefeed->y = state->y;
  550.     linefeed->y_offset = 0;
  551.     */
  552.     /* 
  553.      * If we're laying out a block, we want the contents of the block
  554.      * to determine the size of the block. The right margin is nothing
  555.      * more than a hint for where to wrap the contents. We don't want 
  556.      * the linefeed to extend out to the right margin, because it 
  557.      * unnecessarily extends the block contents.
  558.      */
  559.     /*
  560.     if (state->layer_nest_level > 0) {
  561.         linefeed->width = 0;
  562.     }
  563.     else
  564.     */
  565.     /*
  566.     linefeed->width = state->right_margin - state->x;
  567.     if (linefeed->width < 0)
  568.     {
  569.         linefeed->width = 0;
  570.     }
  571.     linefeed->height = state->line_height;
  572.     */
  573.     /*
  574.      * if this linefeed has a zero height, make it the height
  575.      * of the current font.
  576.      */
  577.     /*
  578.     if (linefeed->height == 0)
  579.     {
  580.         linefeed->height = state->text_info.ascent +
  581.             state->text_info.descent;
  582.         if ((linefeed->height <= 0)&&(state->font_stack != NULL)&&
  583.             (state->font_stack->text_attr != NULL))
  584.         {
  585.             lo_fillin_text_info(context, state);
  586.             linefeed->height = state->text_info.ascent +
  587.                 state->text_info.descent;
  588.         }
  589.     */
  590.         /*
  591.          * This should never happen, but we have it
  592.          * covered just in case it does :-)
  593.          */
  594.     /*
  595.         if (linefeed->height <= 0)
  596.         {
  597.             linefeed->height = state->default_line_height;
  598.         }
  599.     }
  600.     linefeed->line_height = linefeed->height;
  601.  
  602.     linefeed->FE_Data = NULL;
  603.     linefeed->anchor_href = state->current_anchor;
  604.  
  605.     if (state->font_stack == NULL)
  606.     {
  607.         LO_TextAttr tmp_attr;
  608.         LO_TextAttr *tptr;
  609.     */
  610.         /*
  611.          * Fill in default font information.
  612.          */
  613.     /*
  614.         lo_SetDefaultFontAttr(state, &tmp_attr, context);
  615.         tptr = lo_FetchTextAttr(state, &tmp_attr);
  616.         linefeed->text_attr = tptr;
  617.     }
  618.     else
  619.     {
  620.         linefeed->text_attr = state->font_stack->text_attr;
  621.     }
  622.  
  623.     linefeed->baseline = state->baseline;
  624.  
  625.     linefeed->ele_attrmask = 0;
  626.  
  627.     linefeed->sel_start = -1;
  628.     linefeed->sel_end = -1;
  629.  
  630.     linefeed->next = NULL;
  631.     linefeed->prev = NULL;
  632.     linefeed->break_type = (uint8) break_type;
  633.     */
  634.  
  635.     return(linefeed);
  636. }
  637.  
  638.  
  639.  
  640. /*************************************
  641.  * Function: lo_InsertLineBreak
  642.  *
  643.  * Description: This function forces a linebreak at this time.
  644.  *    Forcing the current text insetion point down one line
  645.  *    and back to the left margin.
  646.  *    It also maintains the linefeed state:
  647.  *        0 - middle of a line.
  648.  *        1 - at left margin below a line of text.
  649.  *        2 - at left margin below a blank line.
  650.  *
  651.  * Params: Window context, Document state, break type and a boolean
  652.  *    That describes whether this is a breaking linefeed or not.
  653.  *    (Breaking linefeed is one inserted just to break a formatted
  654.  *     line to the current document width.)
  655.  *
  656.  * Returns: Nothing
  657.  *************************************/
  658. void
  659. lo_InsertLineBreak(MWContext *context, lo_DocState *state, uint32 break_type, uint32 clear_type, Bool breaking)
  660. {
  661.     /* int32 line_width; */
  662.     Bool scroll_at_bottom;
  663.  
  664.     scroll_at_bottom = FALSE;
  665.     /*
  666.      * If this is an auto-scrolling doc, we will be scrolling
  667.      * later if we are at the bottom now.
  668.      */
  669.     if ((state->is_a_subdoc == SUBDOC_NOT)&&
  670.         (state->display_blocked == FALSE)&&
  671.         (state->top_state->auto_scroll > 0))
  672.     {
  673.         int32 doc_x, doc_y;
  674.  
  675.         FE_GetDocPosition(context, FE_VIEW, &doc_x, &doc_y);
  676.         if ((doc_y + state->win_height + state->win_bottom) >= state->y)
  677.         {
  678.             scroll_at_bottom = TRUE;
  679.         }
  680.     }
  681.  
  682.     /*
  683.      * This line is done, flush it into the line table and
  684.      * display it to the front end.
  685.      */
  686.     lo_FlushLineList(context, state, break_type, clear_type, breaking);
  687.     if (state->top_state->out_of_memory != FALSE)
  688.     {
  689.         return;
  690.     }
  691.  
  692.     lo_UpdateStateAfterLineBreak( context, state, TRUE );
  693.  
  694.     /*
  695.      * if this linefeed has a zero height, make it the height
  696.      * of the current font.
  697.      */
  698.     /*
  699.     if (state->line_height == 0)
  700.     {
  701.         state->line_height = state->text_info.ascent +
  702.             state->text_info.descent;
  703.         if ((state->line_height <= 0)&&(state->font_stack != NULL)&&
  704.             (state->font_stack->text_attr != NULL))
  705.         {
  706.             lo_fillin_text_info(context, state);
  707.             state->line_height = state->text_info.ascent +
  708.                 state->text_info.descent;
  709.         }
  710.     */
  711.         /*
  712.          * This should never happen, but we have it
  713.          * covered just in case it does :-)
  714.          */
  715.     /*
  716.         if (state->line_height <= 0)
  717.         {
  718.             state->line_height = state->default_line_height;
  719.         }
  720.     }
  721.  
  722.     if (state->end_last_line != NULL)
  723.     {
  724.         line_width = state->end_last_line->lo_any.x + state->win_right;
  725.     }
  726.     else
  727.     {
  728.         line_width = state->x + state->win_right;
  729.     }
  730.  
  731.     if (line_width > state->max_width)
  732.     {
  733.         state->max_width = line_width;
  734.     }
  735.     */
  736.  
  737.     /* if LineHeightStack exists use it to offset the new Y value */
  738.     /*
  739.     if(state->cur_ele_type != LO_SUBDOC && state->line_height_stack)
  740.     {
  741.         state->y += state->line_height_stack->height;
  742.     }
  743.     else
  744.     {
  745.         state->y = state->y + state->line_height;
  746.     }
  747.  
  748.     state->x = state->left_margin;
  749.     state->width = 0;
  750.     state->at_begin_line = TRUE;
  751.     state->trailing_space = FALSE;
  752.     state->line_height = 0;
  753.     state->break_holder = state->x;
  754.  
  755.     state->linefeed_state++;
  756.     if (state->linefeed_state > 2)
  757.     {
  758.         state->linefeed_state = 2;
  759.     }
  760.  
  761.     */
  762.  
  763.     lo_UpdateFEProgressBar(context, state);
  764.  
  765.     /*
  766.     if (state->is_a_subdoc == SUBDOC_NOT)
  767.     {
  768.         int32 percent;
  769.  
  770.         if (state->top_state->total_bytes < 1)
  771.         {
  772.             percent = -1;
  773.         }
  774.         else
  775.         {
  776.             percent = (100 * state->top_state->layout_bytes) /
  777.                 state->top_state->total_bytes;
  778.             if (percent > 100)
  779.             {
  780.                 percent = 100;
  781.             }
  782.         }
  783.         if ((percent == 100)||(percent < 0)||
  784.             (percent > (state->top_state->layout_percent + 1)))
  785.         {
  786.             if(!state->top_state->is_binary)
  787.                 FE_SetProgressBarPercent(context, percent);
  788.             state->top_state->layout_percent = (intn)percent;
  789.         }
  790.     }
  791.     */
  792.  
  793.     lo_UpdateFEDocSize( context, state );
  794.  
  795.     /*
  796.      * Tell the front end how big the document is right now.
  797.      * Only do this for the top level document.
  798.      */
  799.     /*
  800.     if ((state->is_a_subdoc == SUBDOC_NOT)
  801.         &&(state->display_blocked == FALSE)
  802. #ifdef EDITOR
  803.         &&(!state->edit_relayout_display_blocked)
  804. #endif
  805.         )
  806.     {
  807.     */
  808.  
  809.         /* 
  810.          * Don't resize the layer if we're laying out a block. This
  811.          * will be done when the line is added to the block.
  812.          */
  813.     /*
  814.         if (!lo_InsideLayer(state))
  815.         {
  816.             LO_SetDocumentDimensions(context, state->max_width, state->y);
  817.         }
  818.     }
  819.     */
  820.  
  821.     if ((state->display_blocked != FALSE)&&
  822. #ifdef EDITOR
  823.         (!state->edit_relayout_display_blocked)&&
  824. #endif
  825.        (state->display_blocking_element_y > 0)&&
  826.        (state->y > (state->display_blocking_element_y + state->win_height)))
  827.     {
  828.         int32 y;
  829.  
  830.         state->display_blocked = FALSE;
  831.         y = state->display_blocking_element_y;
  832.         state->display_blocking_element_y = 0;
  833.         if (!lo_InsideLayer(state))
  834.         {
  835.             LO_SetDocumentDimensions(context, state->max_width, state->y);
  836.         }
  837.         FE_SetDocPosition(context, FE_VIEW, 0, y);
  838.  
  839.         if (context->compositor)
  840.         {
  841.             XP_Rect rect;
  842.             
  843.             rect.left = 0;
  844.             rect.top = y;
  845.             rect.right = state->win_width;
  846.             rect.bottom = y + state->win_height;
  847.             CL_UpdateDocumentRect(context->compositor,
  848.                                   &rect, (PRBool)FALSE);
  849.         }
  850.     }
  851.  
  852.     /*
  853.      * Reset the left and right margins
  854.      */
  855.     /*
  856.     lo_FindLineMargins(context, state, TRUE);
  857.     state->x = state->left_margin;
  858.     */
  859.     if ((state->is_a_subdoc == SUBDOC_NOT)&&
  860.         (state->top_state->auto_scroll > 0)&&
  861.         ((state->line_num - 1) > state->top_state->auto_scroll))
  862.     {
  863.         Bool redraw;
  864.         int32 old_y, dy, new_y;
  865.         int32 doc_x, doc_y;
  866.  
  867.         old_y = state->y;
  868.         FE_GetDocPosition(context, FE_VIEW, &doc_x, &doc_y);
  869.         lo_ClipLines(context, state, 1);
  870.         /*
  871.          * Calculate how much of the top was clipped
  872.          * off, and if any of that area was in the users
  873.          * window we need to redraw their window.
  874.          */
  875.         redraw = FALSE;
  876.         dy = old_y - state->y;
  877.         if (dy >= doc_y)
  878.         {
  879.             redraw = TRUE;
  880.         }
  881.         new_y = doc_y - dy;
  882.         if (new_y < 0)
  883.         {
  884.             new_y = 0;
  885.         }
  886.  
  887.         FE_SetDocPosition(context, FE_VIEW, doc_x, new_y);
  888.         if (!lo_InsideLayer(state))
  889.         {
  890.             LO_SetDocumentDimensions(context, state->max_width, state->y);
  891.         }
  892.  
  893.         if (redraw != FALSE)
  894.         {
  895.             FE_ClearView(context, FE_VIEW );
  896.             if (context->compositor)
  897.             {
  898.                     XP_Rect rect;
  899.                     
  900.                     rect.left = 0;
  901.                     rect.top = new_y;
  902.                     rect.right = state->win_width;
  903.                     rect.bottom = new_y + state->win_height;
  904.                     CL_UpdateDocumentRect(context->compositor,
  905.                                           &rect, (PRBool)FALSE);
  906.             }
  907.         }
  908.     }
  909.  
  910.     if ((state->is_a_subdoc == SUBDOC_NOT)&&
  911.         (state->display_blocked == FALSE)&&
  912.         (state->top_state->auto_scroll > 0))
  913.     {
  914.         int32 doc_x, doc_y;
  915.  
  916.         FE_GetDocPosition(context, FE_VIEW, &doc_x, &doc_y);
  917.         if (((doc_y + state->win_height) < state->y)&&
  918.             (scroll_at_bottom != FALSE))
  919.         {
  920.             int32 y;
  921.  
  922.             y = state->y - state->win_height + state->win_bottom;
  923.             if (y < 0)
  924.             {
  925.                 y = 0;
  926.             }
  927.             else
  928.             {
  929.                 FE_SetDocPosition(context, FE_VIEW, 0, y);
  930.             }
  931.         }
  932.     }
  933.  
  934.     lo_insert_quote_characters(context, state);
  935. }
  936.  
  937. /*************************************
  938.  * Function: lo_HardLineBreak
  939.  *
  940.  * Description: This function forces a linebreak at this time.
  941.  *    Forcing the current text insetion point down one line
  942.  *    and back to the left margin.
  943.  *
  944.  * Params: Window context, Document state and a boolean
  945.  *    That describes whether this is a breaking linefeed or not.
  946.  *    (Breaking linefeed is one inserted just to break a formatted
  947.  *     line to the current document width.)
  948.  *
  949.  * Returns: Nothing
  950.  *************************************/
  951. void
  952. lo_HardLineBreak(MWContext *context, lo_DocState *state, Bool breaking)
  953. {
  954.     lo_InsertLineBreak ( context, state, LO_LINEFEED_BREAK_HARD, LO_CLEAR_NONE, breaking );
  955. }
  956.  
  957. /*************************************
  958.  * Function: lo_HardLineBreakWithBreak
  959.  *
  960.  * Returns: Nothing
  961.  *************************************/
  962. void
  963. lo_HardLineBreakWithClearType(MWContext *context, lo_DocState *state, uint32 clear_type, Bool breaking)
  964. {
  965.     lo_InsertLineBreak ( context, state, LO_LINEFEED_BREAK_HARD, clear_type, breaking );
  966. }
  967.  
  968. /*************************************
  969.  * Function: lo_SoftLineBreak
  970.  *
  971.  * Description: This function adds a single soft line break.
  972.  *
  973.  * Params: Window context, Document state and a boolean that
  974.  *    describes if this is a breaking linefeed.
  975.  *
  976.  * Returns: Nothing
  977.  *************************************/
  978. void
  979. lo_SoftLineBreak(MWContext *context, lo_DocState *state, Bool breaking)
  980. {
  981.     lo_InsertLineBreak ( context, state, LO_LINEFEED_BREAK_SOFT, LO_CLEAR_NONE, breaking);
  982. }
  983.  
  984. /*************************************
  985.  * Function: lo_SetSoftLineBreakState
  986.  *
  987.  * Description: Add soft linebreaks to bring the linefeed state to the
  988.  *  specified level.
  989.  *    Linefeed states:
  990.  *        0 - middle of a line.
  991.  *        1 - at left margin below a line of text.
  992.  *        2 - at left margin below a blank line.
  993.  *
  994.  * Params: Window context, Document state, a boolean that
  995.  *    describes if this is a breaking linefeed, and the desired
  996.  *    linefeed state.
  997.  *
  998.  * Returns: Nothing
  999.  *************************************/
  1000. void
  1001. lo_SetSoftLineBreakState(MWContext *context, lo_DocState *state, Bool breaking,
  1002.     intn linefeed_state)
  1003. {
  1004.     lo_SetLineBreakState ( context, state, breaking, LO_LINEFEED_BREAK_SOFT,
  1005.         linefeed_state, FALSE);
  1006. }
  1007.  
  1008. /*************************************
  1009.  * Function: lo_SetLineBreakState
  1010.  *
  1011.  * Description: Add linebreaks to bring the linefeed state to the
  1012.  *  specified level.
  1013.  * Linefeed states:
  1014.  *        0 - middle of a line.
  1015.  *        1 - at left margin below a line of text.
  1016.  *        2 - at left margin below a blank line.
  1017.  *
  1018.  * Params: Window context, Document state, a boolean that
  1019.  *    describes if this is a breaking linefeed, and the desired
  1020.  *    linefeed state.
  1021.  *
  1022.  * Returns: Nothing
  1023.  *************************************/
  1024. void
  1025. lo_SetLineBreakState(MWContext *context, lo_DocState *state, Bool breaking,
  1026.     uint32 break_type, intn linefeed_state, Bool relayout)
  1027. {
  1028.     /*
  1029.      * Linefeeds are partially disabled if we are placing
  1030.      * preformatted text.
  1031.      */
  1032.     if (state->preformatted != PRE_TEXT_NO)
  1033.     {
  1034.         if ((breaking == FALSE)&&(state->linefeed_state < 1)&&
  1035.             (state->top_state->out_of_memory == FALSE))
  1036.         {
  1037.             if (relayout == FALSE) 
  1038.             {
  1039.                 lo_InsertLineBreak(context, state, break_type, LO_CLEAR_NONE, breaking);                
  1040.             }
  1041.             else 
  1042.             {
  1043.                 lo_rl_AddBreakAndFlushLine( context, state, break_type, LO_CLEAR_NONE, breaking);
  1044.             }
  1045.         }
  1046.         return;
  1047.     }
  1048.  
  1049.     /* check for the style sheet display: inline property.
  1050.       * ignore newlines if it is set
  1051.      */
  1052.     if(state->top_state && state->top_state->style_stack)
  1053.     {
  1054.         StyleStruct *style = STYLESTACK_GetStyleByIndex(
  1055.                                                     state->top_state->style_stack,
  1056.                                                     0);
  1057.         if(style)
  1058.         {
  1059.             char * property = STYLESTRUCT_GetString(style, DISPLAY_STYLE);
  1060.             if(property && !strcasecomp(property, INLINE_STYLE))
  1061.             {
  1062.                 XP_FREE(property);
  1063.                 return;
  1064.             }
  1065.             XP_FREEIF(property);
  1066.         }
  1067.     }
  1068.  
  1069.     if (linefeed_state > 2)
  1070.     {
  1071.         linefeed_state = 2;
  1072.     }
  1073.  
  1074.     while ((state->linefeed_state < linefeed_state)&&
  1075.         (state->top_state->out_of_memory == FALSE))
  1076.     {
  1077.         if (relayout == FALSE) 
  1078.         {
  1079.             lo_InsertLineBreak(context, state, break_type, LO_CLEAR_NONE, breaking);
  1080.         }
  1081.         else 
  1082.         {
  1083.             lo_rl_AddBreakAndFlushLine( context, state, break_type, LO_CLEAR_NONE, breaking );
  1084.         }
  1085.     }
  1086. }
  1087.  
  1088. /*************************************
  1089.  * Function: lo_InsertWordBreak
  1090.  *
  1091.  * Description: This insert a word break into the laid out
  1092.  *    element chain for the current line.  A word break is
  1093.  *    basically an empty text element structure.  All
  1094.  *    word break elements should probably be removed
  1095.  *    from the layout chain once the line list for this
  1096.  *    line is flushed.
  1097.  *
  1098.  * Params: Window context and document state.
  1099.  *
  1100.  * Returns: Nothing
  1101.  *************************************/
  1102. void
  1103. lo_InsertWordBreak(MWContext *context, lo_DocState *state)
  1104. {
  1105.     LO_TextStruct *text_ele = NULL;
  1106.  
  1107.     if (state == NULL)
  1108.     {
  1109.         return;
  1110.     }
  1111.  
  1112.     text_ele = (LO_TextStruct *)lo_NewElement(context, state, LO_TEXT, NULL, 0);
  1113.     if (text_ele == NULL)
  1114.     {
  1115. #ifdef DEBUG
  1116.         assert (state->top_state->out_of_memory);
  1117. #endif
  1118.         return;
  1119.     }
  1120.  
  1121.     text_ele->type = LO_TEXT;
  1122.     text_ele->ele_id = NEXT_ELEMENT;
  1123.     text_ele->x = state->x;
  1124.     text_ele->x_offset = 0;
  1125.     text_ele->y = state->y;
  1126.     text_ele->y_offset = 0;
  1127.     text_ele->width = 0;
  1128.     text_ele->height = 0;
  1129.     text_ele->next = NULL;
  1130.     text_ele->prev = NULL;
  1131.  
  1132.     text_ele->block_offset = 0;
  1133.     text_ele->doc_width = 0;
  1134.     
  1135. #if 0
  1136.     text_ele->text = PA_ALLOC(sizeof(char));
  1137.     if (text_ele->text != NULL)
  1138.     {
  1139.         PA_LOCK(text_buf, char *, text_ele->text);
  1140.         text_buf[0] = '\0';
  1141.         PA_UNLOCK(text_ele->text);
  1142.     }
  1143.     else
  1144.     {
  1145.         state->top_state->out_of_memory = TRUE;
  1146.     }
  1147. #else
  1148.     text_ele->text = NULL;
  1149. #endif
  1150.     text_ele->text_len = 0;
  1151.     text_ele->anchor_href = state->current_anchor;
  1152.  
  1153.     if (state->font_stack == NULL)
  1154.     {
  1155.         text_ele->text_attr = NULL;
  1156.     }
  1157.     else
  1158.     {
  1159.         text_ele->text_attr = state->font_stack->text_attr;
  1160.     }
  1161.  
  1162.     text_ele->ele_attrmask = 0;
  1163.     if (state->breakable != FALSE)
  1164.     {
  1165.         text_ele->ele_attrmask |= LO_ELE_BREAKABLE;
  1166.     }
  1167.  
  1168.     text_ele->sel_start = -1;
  1169.     text_ele->sel_end = -1;
  1170.     text_ele->bullet_type = WORDBREAK;
  1171.  
  1172.     lo_AppendToLineList(context, state, (LO_Element *)text_ele, 0);
  1173.  
  1174.     state->old_break = text_ele;
  1175.     state->old_break_block = state->cur_text_block;
  1176.     state->old_break_pos = 0;
  1177.     state->old_break_width = 0;
  1178. }
  1179.  
  1180.  
  1181. int32
  1182. lo_baseline_adjust(MWContext *context, lo_DocState * state, LO_Element *ele_list,
  1183.             int32 old_baseline, int32 old_line_height)
  1184. {
  1185.     LO_Element *eptr;
  1186.     int32 baseline;
  1187.     int32 new_height;
  1188.     int32 baseline_adjust;
  1189.     LO_TextInfo text_info;
  1190.  
  1191.     baseline = 0;
  1192.     new_height = 0;
  1193.     eptr = ele_list;
  1194.     while (eptr != NULL)
  1195.     {
  1196.         switch (eptr->type)
  1197.         {
  1198.             case LO_TEXT:
  1199.                 FE_GetTextInfo(context, (LO_TextStruct *)eptr,
  1200.                     &text_info);
  1201.                 if (text_info.ascent > baseline)
  1202.                 {
  1203.                     baseline = text_info.ascent;
  1204.                 }
  1205.                 if ((text_info.ascent + text_info.descent) >
  1206.                     new_height)
  1207.                 {
  1208.                     new_height = text_info.ascent +
  1209.                         text_info.descent;
  1210.                 }
  1211.                 break;
  1212.             case LO_FORM_ELE:
  1213.                 if (eptr->lo_form.baseline > baseline)
  1214.                 {
  1215.                     baseline = eptr->lo_form.baseline;
  1216.                 }
  1217.                 if (eptr->lo_any.height > new_height)
  1218.                 {
  1219.                     new_height = eptr->lo_any.height;
  1220.                 }
  1221.                 break;
  1222.             case LO_IMAGE:
  1223.                 if ((old_baseline - eptr->lo_any.y_offset) >
  1224.                     baseline)
  1225.                 {
  1226.                     baseline = old_baseline -
  1227.                         eptr->lo_any.y_offset;
  1228.                 }
  1229.                 if ((eptr->lo_image.height +
  1230.                     (2 * eptr->lo_image.border_width) +
  1231.                     (2 * eptr->lo_image.border_vert_space))
  1232.                     > new_height)
  1233.                 {
  1234.                     new_height = eptr->lo_image.height +
  1235.                      (2 * eptr->lo_image.border_width) +
  1236.                      (2 * eptr->lo_image.border_vert_space);
  1237.                 }
  1238.                 break;
  1239.             case LO_HRULE:
  1240.             case LO_BULLET:
  1241.                 if ((old_baseline - eptr->lo_any.y_offset) >
  1242.                     baseline)
  1243.                 {
  1244.                     baseline = old_baseline -
  1245.                         eptr->lo_any.y_offset;
  1246.                 }
  1247.                 if (eptr->lo_any.height > new_height)
  1248.                 {
  1249.                     new_height = eptr->lo_any.height;
  1250.                 }
  1251.                 break;
  1252.             default:
  1253.                 break;
  1254.         }
  1255.         eptr = eptr->lo_any.next;
  1256.     }
  1257.     baseline_adjust = old_baseline - baseline;
  1258.     return(baseline_adjust);
  1259. }
  1260.  
  1261.  
  1262. /*************************************
  1263.  * Function: lo_BreakOldElement
  1264.  *
  1265.  * Description: This function goes back to a previous
  1266.  *    element in the element chain, that is still on the
  1267.  *    same line, and breaks the line there, putting the
  1268.  *    remaining elements (if any) on the next line.
  1269.  *
  1270.  * Params: Window context and document state.
  1271.  *
  1272.  * Returns: Nothing
  1273.  *************************************/
  1274. void
  1275. lo_BreakOldElement(MWContext *context, lo_DocState *state)
  1276. {
  1277.     char *text_buf;
  1278.     char *break_ptr;
  1279.     char *word_ptr;
  1280.     char *new_buf;
  1281.     PA_Block new_block;
  1282.     intn word_len;
  1283.     int32 save_width;
  1284.     int32 old_baseline;
  1285.     int32 old_line_height;
  1286.     int32 adjust;
  1287.     LO_TextStruct *text_data;
  1288.     LO_TextStruct *new_text_data;
  1289.     LO_Element *tptr;
  1290.     int16 charset;
  1291.     int multi_byte;
  1292.     LO_TextBlock * block;
  1293. #ifdef LOCAL_DEBUG
  1294. XP_TRACE(("lo_BreakOldElement, flush text.\n"));
  1295. #endif /* LOCAL_DEBUG */
  1296.  
  1297.     if (state == NULL)
  1298.     {
  1299.         return;
  1300.     }
  1301.  
  1302.     /* BRAIN DAMAGE: Make sure the correct element is at the end of the list for this block */
  1303.     block = state->old_break_block;
  1304.     
  1305.     if ( block == NULL )
  1306.     {
  1307.         return;
  1308.     }
  1309.     
  1310.     /*
  1311.      * If this text block is using the new breaktable algorithm, call that code
  1312.      */
  1313.     if ( lo_UseBreakTable ( block ) )
  1314.         {
  1315.         lo_BreakOldTextBlockElement ( context, state );
  1316.         return;
  1317.         }
  1318.         
  1319.     charset = block->text_attr->charset;
  1320.     multi_byte = (INTL_CharSetType(charset) != SINGLEBYTE);
  1321.  
  1322.     /*
  1323.      * Move to the element we will break
  1324.      */
  1325.     text_data = state->old_break;
  1326.  
  1327.     /*
  1328.      * If there is no text there to break
  1329.      * it is an error.
  1330.      */
  1331.     if ( text_data == NULL )
  1332.     {
  1333.         return;
  1334.     }
  1335.  
  1336.     /*
  1337.      * Later operations will trash the width field.
  1338.      * So save it now to restore later.
  1339.      */
  1340.     save_width = state->width;
  1341.  
  1342.     /*
  1343.      * If this buffer is just an empty string, then we are
  1344.      * breaking at a previously inserted word break element.
  1345.      * Knowing that, the breaking is easier, so it is special 
  1346.      * cased here.
  1347.      */
  1348.     if (text_data->text == NULL)
  1349.     {
  1350.         LO_Element *line_ptr;
  1351.  
  1352.         /*
  1353.          * Back the state up to this element's
  1354.          * location, break off the rest of the elements
  1355.          * and save them for later.
  1356.          * Flush this line, and insert a linebreak.
  1357.          */
  1358.         state->x = text_data->x;
  1359.         state->y = text_data->y;
  1360.         tptr = text_data->next;
  1361.         text_data->next = NULL;
  1362.         PA_UNLOCK(text_data->text);
  1363.         state->width = text_data->width;
  1364.         state->x += state->width;
  1365.         lo_SoftLineBreak(context, state, TRUE);
  1366.  
  1367.         /*
  1368.          * The remaining elements go on the next line.
  1369.          * The nextline may already have special mail
  1370.          * bullets on it.
  1371.          */
  1372.         line_ptr = state->line_list;
  1373.         while ((line_ptr != NULL)&&(line_ptr->lo_any.next != NULL))
  1374.         {
  1375.             line_ptr = line_ptr->lo_any.next;
  1376.         }
  1377.         if (line_ptr == NULL)
  1378.         {
  1379.             state->line_list = tptr;
  1380.             if (tptr != NULL)
  1381.             {
  1382.                 tptr->lo_any.prev = NULL;
  1383.             }
  1384.         }
  1385.         else
  1386.         {
  1387.             line_ptr->lo_any.next = tptr;
  1388.             if (tptr != NULL)
  1389.             {
  1390.                 tptr->lo_any.prev = line_ptr;
  1391.             }
  1392.         }
  1393.  
  1394.         /*
  1395.          * If there are no elements to place on the next
  1396.          * line, and we have new text buffered,
  1397.          * remove any spaces at the start of that new
  1398.          * text since new lines are not allowed to begin
  1399.          * with whitespace.
  1400.          */
  1401.         if ((tptr == NULL)&&(state->line_buf_len != 0))
  1402.         {
  1403.             char *cptr;
  1404.             int32 wlen;
  1405.  
  1406.             PA_LOCK(text_buf, char *, state->line_buf);
  1407.             cptr = text_buf;
  1408.             wlen = 0;
  1409.             while ((XP_IS_SPACE(*cptr))&&(*cptr != '\0'))
  1410.             {
  1411.                 cptr++;
  1412.                 wlen++;
  1413.             }
  1414.             if (wlen)
  1415.             {
  1416.                 LO_TextStruct tmp_text;
  1417.  
  1418.                 memset (&tmp_text, 0, sizeof (tmp_text));
  1419.  
  1420.                 /*
  1421.                  * We removed space, move the string up
  1422.                  * and recalculate its current width.
  1423.                  */
  1424.                 XP_BCOPY(cptr, text_buf,
  1425.                     (state->line_buf_len - wlen + 1));
  1426.                 state->line_buf_len -= (intn) wlen;
  1427.                 PA_UNLOCK(state->line_buf);
  1428.                 tmp_text.text = state->line_buf;
  1429.                 tmp_text.text_len = (int16)state->line_buf_len;
  1430.                 tmp_text.text_attr =
  1431.                     block->text_attr;
  1432.                 FE_GetTextInfo(context, &tmp_text,
  1433.                     &(state->text_info));
  1434.  
  1435.                 /*
  1436.                  * Override the saved width since we did want
  1437.                  * to change the real width in this case.
  1438.                  */
  1439.                 save_width = lo_correct_text_element_width(
  1440.                     &(state->text_info));
  1441.             }
  1442.             else
  1443.             {
  1444.                 PA_UNLOCK(state->line_buf);
  1445.             }
  1446.         }
  1447.     }
  1448.     /*
  1449.      * We are breaking somewhere in the middle of an old
  1450.      * text element, this will mean splitting it into 2 text
  1451.      * elements, and putting a line break between them.
  1452.      */
  1453.     else
  1454.     {
  1455.         LO_TextInfo text_info;
  1456.         int32 base_change;
  1457.  
  1458.         PA_LOCK(text_buf, char *, text_data->text);
  1459.         
  1460.         /*
  1461.          * Locate our break location, and a pointer to
  1462.          * the remaining word (with its preceeding space removed).
  1463.          */
  1464.         break_ptr = (char *)(text_buf + state->old_break_pos);
  1465.         /*
  1466.          * On multibyte, we often break on character which is 
  1467.          * not space.
  1468.          */ 
  1469.         if (multi_byte == FALSE || XP_IS_SPACE(*break_ptr))
  1470.         {
  1471.             *break_ptr = '\0';
  1472.             word_ptr = (char *)(break_ptr + 1);
  1473.         }
  1474.         else
  1475.         {
  1476.             word_ptr = (char *)break_ptr;
  1477.         }
  1478.  
  1479.         /*
  1480.          * Copy the remaining word into its own block.
  1481.          */
  1482.         word_len = XP_STRLEN(word_ptr);
  1483.         new_block = PA_ALLOC((word_len + 1));
  1484.         if (new_block == NULL)
  1485.         {
  1486.             state->top_state->out_of_memory = TRUE;
  1487.             return;
  1488.         }
  1489.         PA_LOCK(new_buf, char *, new_block);
  1490.         XP_BCOPY(word_ptr, new_buf, (word_len + 1));
  1491.         new_buf[word_len] = '\0';
  1492.  
  1493.         /*
  1494.          * Back the state up to this element's
  1495.          * location, break off the rest of the elements
  1496.          * and save them for later.
  1497.          * Flush this line, and insert a linebreak.
  1498.          */
  1499.         state->x = text_data->x;
  1500.         state->y = text_data->y;
  1501.         tptr = text_data->next;
  1502.         text_data->next = NULL;
  1503.         if (word_ptr != break_ptr)
  1504.             text_data->text_len = text_data->text_len - word_len - 1;
  1505.         else
  1506.             text_data->text_len = text_data->text_len - word_len;
  1507.         FE_GetTextInfo(context, text_data, &text_info);
  1508.         state->width = lo_correct_text_element_width(&text_info);
  1509.         PA_UNLOCK(text_data->text);
  1510.         state->x += state->width;
  1511.  
  1512.         /*
  1513.          * Make the split element know its new width.
  1514.          */
  1515.         text_data->width = state->width;
  1516.  
  1517.         /*
  1518.          * If the element that caused this break has a different
  1519.          * baseline than the element we are breaking, we need to
  1520.          * preserve that difference after the break.
  1521.          */
  1522.         base_change = state->baseline -
  1523.             (text_data->y_offset + text_info.ascent);
  1524.  
  1525.         old_baseline = state->baseline;
  1526.         old_line_height = state->line_height;
  1527.  
  1528.         /*
  1529.          * Reset element_id so they are still sequencial.
  1530.          */
  1531.         state->top_state->element_id = text_data->ele_id + 1;
  1532.  
  1533.         /*
  1534.          * If we are breaking an anchor, we need to make sure the
  1535.          * linefeed gets its anchor href set properly.
  1536.          */
  1537.         if (text_data->anchor_href != NULL)
  1538.         {
  1539.             LO_AnchorData *tmp_anchor;
  1540.  
  1541.             tmp_anchor = state->current_anchor;
  1542.             state->current_anchor = text_data->anchor_href;
  1543.             lo_SoftLineBreak(context, state, TRUE);
  1544.             state->current_anchor = tmp_anchor;
  1545.         }
  1546.         else
  1547.         {
  1548.             lo_SoftLineBreak(context, state, TRUE);
  1549.         }
  1550.  
  1551.         adjust = lo_baseline_adjust(context, state, tptr,
  1552.             old_baseline, old_line_height);
  1553.         state->baseline = old_baseline - adjust;
  1554.         state->line_height = (intn) old_line_height - adjust;
  1555.  
  1556.         /*
  1557.          * If there was really no remaining word, free the
  1558.          * unneeded buffer.
  1559.          */
  1560.         if (word_len == 0)
  1561.         {
  1562.             LO_Element *eptr;
  1563.             LO_Element *line_ptr;
  1564.  
  1565.             PA_UNLOCK(new_block);
  1566.             PA_FREE(new_block);
  1567.  
  1568.             line_ptr = state->line_list;
  1569.             while ((line_ptr != NULL)&&
  1570.                 (line_ptr->lo_any.next != NULL))
  1571.             {
  1572.                 line_ptr = line_ptr->lo_any.next;
  1573.             }
  1574.             if (line_ptr == NULL)
  1575.             {
  1576.                 state->line_list = tptr;
  1577.                 if (tptr != NULL)
  1578.                 {
  1579.                     tptr->lo_any.prev = NULL;
  1580.                 }
  1581.             }
  1582.             else
  1583.             {
  1584.                 line_ptr->lo_any.next = tptr;
  1585.                 if (tptr != NULL)
  1586.                 {
  1587.                     tptr->lo_any.prev = line_ptr;
  1588.                 }
  1589.             }
  1590.  
  1591.             state->width = 0;
  1592.  
  1593.             eptr = tptr;
  1594.             while (eptr != NULL)
  1595.             {
  1596.                 eptr->lo_any.ele_id = NEXT_ELEMENT;
  1597.                 eptr->lo_any.y_offset -= adjust;
  1598.                 eptr = eptr->lo_any.next;
  1599.             }
  1600.         }
  1601.         /*
  1602.          * Else create a new text element for the remaining word.
  1603.          * and stick it in the begining of the next line of
  1604.          * text elements.
  1605.          */
  1606.         else
  1607.         {
  1608.             LO_Element *line_ptr;
  1609.             LO_Element *eptr;
  1610.             int32 baseline_inc;
  1611.             LO_TextInfo text_info;
  1612.             baseline_inc = -1 * adjust;
  1613.             new_text_data = lo_new_text_element(context, state, 
  1614.                     text_data->edit_element,
  1615.                     text_data->edit_offset+text_data->text_len+1 );
  1616.             if (new_text_data == NULL)
  1617.             {
  1618.                 return;
  1619.             }
  1620.             if (new_text_data->text != NULL)
  1621.             {
  1622.                 PA_FREE(new_text_data->text);
  1623.                 new_text_data->text = NULL;
  1624.                 new_text_data->text_len = 0;
  1625.             }
  1626.             new_text_data->anchor_href = text_data->anchor_href;
  1627.             new_text_data->text_attr = text_data->text_attr;
  1628.             new_text_data->x = state->x;
  1629.             new_text_data->y = state->y;
  1630.                 
  1631. #ifdef LOCAL_DEBUG
  1632. XP_TRACE(("lo_BreakOldElement, left over word (%s)\n", new_buf));
  1633. #endif /* LOCAL_DEBUG */
  1634.  
  1635.             PA_UNLOCK(new_block);
  1636.             new_text_data->text = new_block;
  1637.             new_text_data->text_len = word_len;
  1638.             FE_GetTextInfo(context, new_text_data, &text_info);
  1639.             new_text_data->width =
  1640.                 lo_correct_text_element_width(&text_info);
  1641.  
  1642.             /*
  1643.              * Some fonts (particulatly italic ones with curly
  1644.              * tails on letters like 'f') have a left bearing
  1645.              * that extends back into the previous character.
  1646.              * Since in this case the previous character is
  1647.              * probably not in the same font, we move forward
  1648.              * to avoid overlap.
  1649.              */
  1650.             if (text_info.lbearing < 0)
  1651.             {
  1652.                 new_text_data->x_offset =
  1653.                     text_info.lbearing * -1;
  1654.             }
  1655.  
  1656.             /*
  1657.              * The baseline of the text element just inserted in
  1658.              * the line may be less than or greater than the
  1659.              * baseline of the rest of the line due to font
  1660.              * changes.  If the baseline is less, this is easy,
  1661.              * we just increase y_offest to move the text down
  1662.              * so the baselines line up.  For greater baselines,
  1663.              * we can't move the text up to line up the baselines
  1664.              * because we will overlay the previous line, so we
  1665.              * have to move all rest of the elements in this line
  1666.              * down.
  1667.              *
  1668.              * If the baseline is zero, we are the first element
  1669.              * on the line, and we get to set the baseline.
  1670.              */
  1671.             if (state->baseline == 0)
  1672.             {
  1673.                 state->baseline = text_info.ascent;
  1674.             }
  1675.             else if (text_info.ascent < state->baseline)
  1676.             {
  1677.                 new_text_data->y_offset = state->baseline -
  1678.                         text_info.ascent;
  1679.             }
  1680.             else
  1681.             {
  1682.                 baseline_inc = baseline_inc +
  1683.                     (text_info.ascent - state->baseline);
  1684.                 state->baseline =
  1685.                     text_info.ascent;
  1686.             }
  1687.  
  1688.             /*
  1689.              * Now that we have broken, and added the new
  1690.              * element, we need to move it down to restore the
  1691.              * baseline difference that previously existed.
  1692.              */
  1693.             new_text_data->y_offset -= base_change;
  1694.  
  1695.             /*
  1696.              * Calculate the height of this new
  1697.              * text element.
  1698.              */
  1699.             new_text_data->height = text_info.ascent +
  1700.                 text_info.descent;
  1701.             state->x += new_text_data->width;
  1702.  
  1703.             /*
  1704.              * Stick this new text element at the beginning
  1705.              * of the remaining line elements
  1706.              * There may be some special mail bullets already
  1707.              * on the line that we have to insert after.
  1708.              */
  1709.             line_ptr = state->line_list;
  1710.             while ((line_ptr != NULL)&&
  1711.                 (line_ptr->lo_any.next != NULL))
  1712.             {
  1713.                 line_ptr = line_ptr->lo_any.next;
  1714.             }
  1715.             if (line_ptr == NULL)
  1716.             {
  1717.                 state->line_list = (LO_Element *)new_text_data;
  1718.                 new_text_data->prev = NULL;
  1719.             }
  1720.             else
  1721.             {
  1722.                 line_ptr->lo_any.next =
  1723.                     (LO_Element *)new_text_data;
  1724.                 new_text_data->prev = line_ptr;
  1725.             }
  1726.             new_text_data->next = tptr;
  1727.             if (tptr != NULL)
  1728.             {
  1729.                 tptr->lo_any.prev = (LO_Element *)new_text_data;
  1730.             }
  1731.  
  1732.             eptr = tptr;
  1733.  
  1734.             while (eptr != NULL)
  1735.             {
  1736.                 eptr->lo_any.ele_id = NEXT_ELEMENT;
  1737.                 eptr->lo_any.y_offset += baseline_inc;
  1738.                 eptr = eptr->lo_any.next;
  1739.             }
  1740.  
  1741.             if ((new_text_data->y_offset + new_text_data->height) > 
  1742.                 state->line_height)
  1743.             {
  1744.                 state->line_height = (intn) new_text_data->y_offset +
  1745.                     new_text_data->height;
  1746.             }
  1747.  
  1748.             state->at_begin_line = FALSE;
  1749.         }
  1750.     }
  1751.  
  1752.     /*
  1753.      * If we are at the beginning of a line, and there is
  1754.      * remaining text to place here, remove leading space
  1755.      * which is not allowed at the start of lines.
  1756.      * ERIC, make a test case for this, I suspect right now
  1757.      * this code is never being executed and may contain an error.
  1758.      */
  1759.     if ((state->at_begin_line != FALSE)&&(tptr != NULL)&&
  1760.         (tptr->type == LO_TEXT))
  1761.     {
  1762.         char *cptr;
  1763.         int32 wlen;
  1764.         LO_TextStruct *tmp_text;
  1765.         LO_TextInfo text_info;
  1766.  
  1767.         tmp_text = (LO_TextStruct *)tptr;
  1768.  
  1769.         PA_LOCK(text_buf, char *, tmp_text->text);
  1770.         cptr = text_buf;
  1771.         wlen = 0;
  1772.         while ((XP_IS_SPACE(*cptr))&&(*cptr != '\0'))
  1773.         {
  1774.             cptr++;
  1775.             wlen++;
  1776.         }
  1777.         if (wlen)
  1778.         {
  1779.             XP_BCOPY(cptr, text_buf,
  1780.                 (tmp_text->text_len - wlen + 1));
  1781.             tmp_text->text_len -= (intn) wlen;
  1782.  
  1783.             PA_UNLOCK(tmp_text->text);
  1784.             FE_GetTextInfo(context, tmp_text, &text_info);
  1785.             tmp_text->width = lo_correct_text_element_width(
  1786.                 &text_info);
  1787.         }
  1788.         else
  1789.         {
  1790.             PA_UNLOCK(tmp_text->text);
  1791.         }
  1792.     }
  1793.  
  1794.     /*
  1795.      * Upgrade forward the x and y text positions in the document
  1796.      * state.
  1797.      */
  1798.     while (tptr != NULL)
  1799.     {
  1800.         lo_UpdateElementPosition ( state, tptr );
  1801.         tptr = tptr->lo_any.next;
  1802.     }
  1803.  
  1804.     state->at_begin_line = FALSE;
  1805.     state->width = save_width;
  1806. }
  1807.  
  1808.  
  1809. void lo_UpdateElementPosition ( lo_DocState * state, LO_Element * element )
  1810. {
  1811.     element->lo_any.x = state->x;
  1812.     element->lo_any.y = state->y;
  1813.     state->x = state->x + element->lo_any.width;
  1814.     
  1815.     /* move any element specific items */
  1816.     switch ( element->lo_any.type )
  1817.         {
  1818.         case LO_IMAGE:
  1819.             CL_MoveLayer(element->lo_image.layer, element->lo_any.x, element->lo_any.y);
  1820.             break;
  1821.             
  1822.         case LO_EMBED:
  1823.             CL_MoveLayer(element->lo_embed.layer, element->lo_any.x, element->lo_any.y);
  1824.             break;
  1825.         }
  1826. }
  1827.  
  1828.  
  1829. /*************************************
  1830.  * Function: lo_correct_text_element_width
  1831.  *
  1832.  * Description: Calculate the correct width of this text element
  1833.  *    if it is a complete element surrounded by elements of potentially
  1834.  *    different fonts, so we have to take care not to truncate
  1835.  *    any slanted characters at either end of the element.
  1836.  *
  1837.  * Params: LO_TextInfo structure for this text element's text string.
  1838.  *
  1839.  * Returns: The width this element would have if it stood alone.
  1840.  *************************************/
  1841. int32
  1842. lo_correct_text_element_width(LO_TextInfo *text_info)
  1843. {
  1844.     int32 x_offset;
  1845.     int32 width;
  1846.  
  1847.     width = text_info->max_width;
  1848.     x_offset = 0;
  1849.  
  1850.     /*
  1851.      * For text that leans into the previous character.
  1852.      */
  1853.     if (text_info->lbearing < 0)
  1854.     {
  1855.         x_offset = text_info->lbearing * -1;
  1856.         width += x_offset;
  1857.     }
  1858.  
  1859.     /*
  1860.      * For text that leans right into the following characters.
  1861.      */
  1862.     if (text_info->rbearing > text_info->max_width)
  1863.     {
  1864.         width += (text_info->rbearing - text_info->max_width);
  1865.     }
  1866.  
  1867.     return(width);
  1868. }
  1869.  
  1870.  
  1871. PRIVATE
  1872. int32
  1873. lo_characters_in_line(lo_DocState *state)
  1874. {
  1875.     int32 cnt;
  1876.     LO_Element *eptr;
  1877.  
  1878.     cnt = 0;
  1879.     eptr = state->line_list;
  1880.     while (eptr != NULL)
  1881.     {
  1882.         if (eptr->type == LO_TEXT)
  1883.         {
  1884.             cnt += eptr->lo_text.text_len;
  1885.         }
  1886.         eptr = eptr->lo_any.next;
  1887.     }
  1888.     return(cnt);
  1889. }
  1890.  
  1891. void
  1892. lo_PreformatedText(MWContext *context, lo_DocState *state, char *text)
  1893. {
  1894.     LO_TextBlock *    block;
  1895.     
  1896.     block = lo_NewTextBlock ( context, state, text, state->preformatted );
  1897.     if ( !state->top_state->out_of_memory && ( block != NULL ) )
  1898.         {
  1899.         block->buffer_read_index = 0;
  1900.         lo_LayoutPreformattedText ( context, state, block );
  1901.         }
  1902. }
  1903.  
  1904.  
  1905. void
  1906. lo_LayoutPreformattedText(MWContext *context, lo_DocState *state, LO_TextBlock * block)
  1907. {
  1908.     char *tptr;
  1909.     char *w_start;
  1910.     char *w_end;
  1911.     char *text_buf;
  1912.     char tchar1;
  1913.     Bool have_CR;
  1914.     Bool line_break;
  1915.     Bool white_space;
  1916.     LO_TextStruct text_data;
  1917.     char *tmp_buf;
  1918.     PA_Block tmp_block;
  1919.     int32 tab_count, ignore_cnt, line_length;
  1920.     int16 charset;
  1921.     int multi_byte;
  1922.     int kinsoku_class, last_kinsoku_class;
  1923.     int i;
  1924.     int bytestocopy;
  1925.     char * text;
  1926.     Bool lineBufMeasured;
  1927.     
  1928.     /* start at the current text position in this text block */
  1929.     text = (char *) &block->text_buffer[ block->buffer_read_index ];
  1930.     
  1931.     kinsoku_class = PROHIBIT_NOWHERE;
  1932.     
  1933.     lineBufMeasured = FALSE;
  1934.     
  1935.     /*
  1936.      * Initialize the structures to 0 (mark)
  1937.      */
  1938.     memset (&text_data, 0, sizeof (LO_TextStruct));
  1939.  
  1940.     /*
  1941.      * Error conditions
  1942.      */
  1943.     if ((state == NULL)||(state->cur_ele_type != LO_TEXT)||(text == NULL))
  1944.     {
  1945.         return;
  1946.     }
  1947.  
  1948.     charset = block->text_attr->charset;
  1949.     multi_byte = (INTL_CharSetType(charset) != SINGLEBYTE);
  1950.  
  1951.     /*
  1952.      * Move through this text fragment, expand tabs, honor LF/CR.
  1953.      */
  1954.     have_CR = state->last_char_CR;
  1955.     tptr = text;
  1956.     while ((*tptr != '\0')&&(state->top_state->out_of_memory == FALSE))
  1957.     {
  1958.         Bool has_nbsp;
  1959.         Bool in_word;
  1960.         Bool wrap_break;
  1961.         Bool pre_wrap_break;
  1962.  
  1963.         /*
  1964.          * white_space is a tag to tell us if the current word
  1965.          * ends in whitespace.
  1966.          */
  1967.         white_space = FALSE;
  1968.         line_break = FALSE;
  1969.         has_nbsp = FALSE;
  1970.         if (multi_byte)
  1971.             has_nbsp = TRUE;
  1972.  
  1973.         /*
  1974.          * Find the end of the line, counting tabs.
  1975.          */
  1976.         tab_count = 0;
  1977.         ignore_cnt = 0;
  1978.         line_length = 0;
  1979.         w_start = tptr;
  1980.  
  1981.         /*
  1982.          * If the last character processed was a CR, and the next
  1983.          * char is a LF, ignore it.  Otherwise we know we
  1984.          * can break the line on the first CR or LF found.
  1985.          */
  1986.         if ((have_CR != FALSE)&&(*tptr == LF))
  1987.         {
  1988.             ignore_cnt++;
  1989.             tptr++;
  1990.         }
  1991.         have_CR = FALSE;
  1992.  
  1993.         in_word = FALSE;
  1994.         wrap_break = FALSE;
  1995.         pre_wrap_break = FALSE;
  1996.         
  1997. #ifdef XP_WIN16
  1998.         while ((*tptr != CR)&&(*tptr != LF)&&(*tptr != '\0')&&
  1999. #ifdef TEXT_CHUNK_LIMIT
  2000.             ((line_length + (tab_count * state->tab_stop)) < TEXT_CHUNK_LIMIT)&&
  2001. #endif /* TEXT_CHUNK_LIMIT */
  2002.             (((state->line_buf_len + line_length + (tab_count * state->tab_stop))) < SIZE_LIMIT))
  2003. #else
  2004.         while ((*tptr != CR)&&(*tptr != LF)&&(*tptr != '\0')
  2005. #ifdef TEXT_CHUNK_LIMIT
  2006.             &&((line_length + (tab_count * state->tab_stop)) < TEXT_CHUNK_LIMIT)
  2007. #endif /* TEXT_CHUNK_LIMIT */
  2008.             )
  2009. #endif /* XP_WIN16 */
  2010.         {
  2011.             /*
  2012.              * In the special wrapping preformatted text
  2013.              * we need to chunk by word instead of by
  2014.              * line.
  2015.              */
  2016.             if ((state->preformatted == PRE_TEXT_WRAP)||
  2017.                 (state->preformatted == PRE_TEXT_COLS))
  2018.             {
  2019.                 if(multi_byte && (! (INTL_CharSetType(charset) & CS_SPACE)))
  2020.                 {
  2021.                     last_kinsoku_class = kinsoku_class;
  2022.                     kinsoku_class = INTL_KinsokuClass(charset, (unsigned char *)tptr);
  2023.                     /* We need to conser PROHIBIT_WORD_BREAK for UTF8 case */
  2024.                     if(( PROHIBIT_WORD_BREAK == kinsoku_class) || (0x00 == (*tptr & 0x80)))
  2025.                     {
  2026.                         if ((in_word == FALSE)&&(!XP_IS_SPACE(*tptr)) )
  2027.                         {
  2028.                             in_word = TRUE;
  2029.                         }
  2030.                         else if ((in_word != FALSE)&&
  2031.                             (XP_IS_SPACE(*tptr)))
  2032.                         {
  2033.                             wrap_break = TRUE;
  2034.                             break;
  2035.                         }
  2036.                     }
  2037.                     else
  2038.                     {
  2039.                         if( (line_length != 0) &&
  2040.                             (PROHIBIT_END_OF_LINE != last_kinsoku_class) &&
  2041.                             (PROHIBIT_BEGIN_OF_LINE != kinsoku_class)
  2042.                           )
  2043.                         {
  2044.                             wrap_break = TRUE;
  2045.                             break;
  2046.                         }
  2047.                     }
  2048.                 }
  2049.                 else
  2050.                 {
  2051.                     if ((in_word == FALSE)&&(!XP_IS_SPACE(*tptr)))
  2052.                     {
  2053.                         in_word = TRUE;
  2054.                     }
  2055.                     else if ((in_word != FALSE)&&
  2056.                         (XP_IS_SPACE(*tptr)))
  2057.                     {
  2058.                         wrap_break = TRUE;
  2059.                         break;
  2060.                     }
  2061.                 }
  2062.             }
  2063.  
  2064.             if ((*tptr == FF)||(*tptr == VTAB))
  2065.             {
  2066.                 /*
  2067.                  * Ignore the form feeds
  2068.                  * thrown in by some platforms.
  2069.                  * Ignore vertical tabs since we don't know
  2070.                  * what else to do with them.
  2071.                  */
  2072.                 ignore_cnt++;
  2073.             }
  2074.             else if (*tptr == TAB)
  2075.             {
  2076.                 tab_count++;
  2077.             }
  2078.             else if (!multi_byte && (unsigned char)*tptr == NON_BREAKING_SPACE)
  2079.             {
  2080.                 /* *tptr = ' '; Replace this later */
  2081.                 has_nbsp = TRUE;
  2082.                 line_length++;
  2083.             }
  2084.             else
  2085.             {
  2086.                 if(multi_byte)
  2087.                     line_length += INTL_CharLen(charset, (unsigned char*)tptr);
  2088.                 else
  2089.                     line_length++;
  2090.             }
  2091.  
  2092.             if(multi_byte)
  2093.                 tptr = INTL_NextChar(charset, tptr);
  2094.             else
  2095.                 tptr++;
  2096.         }
  2097.         line_length = line_length + (state->tab_stop * tab_count);
  2098.  
  2099. #ifdef TEXT_CHUNK_LIMIT
  2100.         if ((state->line_buf_len + line_length) > TEXT_CHUNK_LIMIT)
  2101.         {
  2102.             lo_FlushLineBuffer(context, state);
  2103.             if (state->cur_ele_type != LO_TEXT)
  2104.             {
  2105.                 lo_FreshText(state);
  2106.                 state->cur_ele_type = LO_TEXT;
  2107.             }
  2108.         }
  2109. #endif /* TEXT_CHUNK_LIMIT */
  2110.  
  2111. #ifdef XP_WIN16
  2112.         if ((state->line_buf_len + line_length) >= SIZE_LIMIT)
  2113.         {
  2114.             line_break = TRUE;
  2115.         }
  2116. #endif /* XP_WIN16 */
  2117.  
  2118.         /*
  2119.          * Terminate the word, saving the char we replaced
  2120.          * with the terminator so it can be restored later.
  2121.          */
  2122.         w_end = tptr;
  2123.         tchar1 = *w_end;
  2124.         *w_end = '\0';
  2125.  
  2126.         tmp_block = PA_ALLOC(line_length + 1);
  2127.         if (tmp_block == NULL)
  2128.         {
  2129.             *w_end = tchar1;
  2130.             state->top_state->out_of_memory = TRUE;
  2131.             break;
  2132.         }
  2133.         PA_LOCK(tmp_buf, char *, tmp_block);
  2134.  
  2135.         if ((tab_count)||(ignore_cnt))
  2136.         {
  2137.             char *cptr;
  2138.             char *text_ptr;
  2139.             int32 cnt;
  2140.  
  2141.             text_ptr = tmp_buf;
  2142.             cptr = w_start;
  2143.             cnt = lo_characters_in_line(state);
  2144.             cnt += state->line_buf_len;
  2145.             while (*cptr != '\0')
  2146.             {
  2147.                 if ((*cptr == LF)||(*cptr == FF)||
  2148.                     (*cptr == VTAB))
  2149.                 {
  2150.                     /*
  2151.                      * Ignore any linefeeds that must have
  2152.                      * been after CR, and form feeds.
  2153.                      * Ignore vertical tabs since we
  2154.                      * don't know what else to do with them.
  2155.                      */
  2156.                     cptr++;
  2157.                 }
  2158.                 else if (*cptr == TAB)
  2159.                 {
  2160.                     int32 i, tab_pos;
  2161.  
  2162.                     tab_pos = ((cnt / state->tab_stop) +
  2163.                         1) * state->tab_stop;
  2164.                     for (i=0; i<(tab_pos - cnt); i++)
  2165.                     {
  2166.                         *text_ptr++ = ' ';
  2167.                     }
  2168.                     cnt = tab_pos;
  2169.                     cptr++;
  2170.                 }
  2171.                 else
  2172.                 {
  2173.                     /*
  2174.                      * Bug #77467
  2175.                      * If multibyte, character != char by default, so copy 
  2176.                      * the CHARACTER, not the char
  2177.                      */
  2178.                     if(multi_byte) 
  2179.                     {
  2180.                         bytestocopy = INTL_CharLen(charset, (unsigned char*)cptr);
  2181.                         for (i=0; i<bytestocopy; i++)
  2182.                         {
  2183.                             *text_ptr++ = *cptr++;
  2184.                             cnt++;
  2185.                         }
  2186.                     }
  2187.                     else
  2188.                     {
  2189.                         *text_ptr++ = *cptr++;
  2190.                         cnt++;
  2191.                     }
  2192.                 }
  2193.             }
  2194.             *text_ptr = *cptr;
  2195.         }
  2196.         else
  2197.         {
  2198.             XP_BCOPY(w_start, tmp_buf, line_length + 1);
  2199.         }
  2200.  
  2201.         /*
  2202.          * Now we catch those nasty non-breaking space special
  2203.          * characters and make them spaces.
  2204.          */
  2205.         if (has_nbsp != FALSE)
  2206.         {
  2207.             char *tmp_ptr;
  2208.  
  2209.             tmp_ptr = tmp_buf;
  2210.             while (*tmp_ptr != '\0')
  2211.             {
  2212.                 if (((unsigned char)*tmp_ptr == NON_BREAKING_SPACE)
  2213.                     && (CS_USER_DEFINED_ENCODING != charset))
  2214.                 {
  2215.                     *tmp_ptr = ' ';
  2216.                 }
  2217.                 if(multi_byte)
  2218.                     tmp_ptr = INTL_NextChar(charset, tmp_ptr);
  2219.                 else
  2220.                     tmp_ptr++;
  2221.             }
  2222.         }
  2223.  
  2224.         /* don't need this any more since we're converting
  2225.          * elsewhere -- erik
  2226.         tmp_buf = FE_TranslateISOText(context, charset, tmp_buf);
  2227.          */
  2228.         line_length = XP_STRLEN(tmp_buf);
  2229.  
  2230.         if ((line_length > 0)&&(XP_IS_SPACE(tmp_buf[line_length - 1])))
  2231.         {
  2232.             state->trailing_space = TRUE;
  2233.         }
  2234. #ifdef LOCAL_DEBUG
  2235. XP_TRACE(("Found Preformatted text (%s)\n", tmp_buf));
  2236. #endif /* LOCAL_DEBUG */
  2237.         PA_UNLOCK(tmp_block);
  2238.  
  2239. #if WHAT
  2240.         /*
  2241.          * If this is an empty string, just throw it out
  2242.          * and move on
  2243.          */
  2244.         if (*w_start == '\0')
  2245.         {
  2246.             *w_end = tchar1;
  2247. #ifdef LOCAL_DEBUG
  2248. XP_TRACE(("Throwing out empty string!\n"));
  2249. #endif /* LOCAL_DEBUG */
  2250.             continue;
  2251.         }
  2252. #endif
  2253.  
  2254.         /*
  2255.          * If we have extra text, Append it to the line buffer.
  2256.          * It may be necessary to expand the line
  2257.          * buffer.
  2258.          */
  2259.         if (*w_start != '\0')
  2260.         {
  2261.             int32 old_len;
  2262.             Bool old_begin_line;
  2263.  
  2264.             old_len = state->line_buf_len;
  2265.             old_begin_line = state->at_begin_line;
  2266.  
  2267.             if ((state->line_buf_len + line_length + 1) >
  2268.                 state->line_buf_size)
  2269.             {
  2270.                 state->line_buf = PA_REALLOC(
  2271.                     state->line_buf, (state->line_buf_size +
  2272.                     line_length + LINE_BUF_INC));
  2273.                 if (state->line_buf == NULL)
  2274.                 {
  2275.                     *w_end = tchar1;
  2276.                     state->top_state->out_of_memory = TRUE;
  2277.                     break;
  2278.                 }
  2279.                 state->line_buf_size += (line_length +
  2280.                     LINE_BUF_INC);
  2281.             }
  2282.             PA_LOCK(text_buf, char *, state->line_buf);
  2283.             PA_LOCK(tmp_buf, char *, tmp_block);
  2284.  
  2285.             XP_BCOPY(tmp_buf,
  2286.                 (char *)(text_buf + state->line_buf_len),
  2287.                 (line_length + 1));
  2288.             state->line_buf_len += (intn) line_length;
  2289.             PA_UNLOCK(state->line_buf);
  2290.             PA_UNLOCK(tmp_block);
  2291.  
  2292.             /* we have not measured this new text yet */
  2293.             lineBufMeasured = FALSE;
  2294.             
  2295.             /*
  2296.              * Having added text, we cannot be at the start
  2297.              * of a line
  2298.              */
  2299.             state->cur_ele_type = LO_TEXT;
  2300.             state->at_begin_line = FALSE;
  2301.  
  2302. #ifdef OLD_WAY
  2303.             /*
  2304.              * Most common case is appending to the same line.
  2305.              * assume that is what we are doing here.
  2306.              */
  2307.             text_data.text = state->line_buf;
  2308.             text_data.text_len = (int16)state->line_buf_len;
  2309.             text_data.text_attr = block->text_attr;
  2310.             FE_GetTextInfo(context, &text_data,
  2311.                 &(state->text_info));
  2312.             state->width =
  2313.                 lo_correct_text_element_width(
  2314.                 &(state->text_info));
  2315.  
  2316.             /*
  2317.              * If this is a special wrapping pre, and we would
  2318.              * wrap here, break before this, and strip all
  2319.              * following whitespace so there is none at
  2320.              * the start of the next line.
  2321.              * If we were at the beginning of the line before
  2322.              * this, then obviously trying to wrap here will
  2323.              * be pointless, and will in fact cause an
  2324.              * infinite loop.
  2325.              *
  2326.              * Also wrap here is we are in fixed column wrapping
  2327.              * pre text, and we would pass our set column.
  2328.              */
  2329.             if (((state->preformatted == PRE_TEXT_WRAP)&&
  2330.                 (old_begin_line == FALSE)&&
  2331.                 ((state->x + state->width) > state->right_margin))||
  2332.                ((state->preformatted == PRE_TEXT_COLS)&&
  2333.                 (old_begin_line == FALSE)&&
  2334.                 (state->preformat_cols > 0)&&
  2335.                 (state->line_buf_len > state->preformat_cols)))
  2336.             {
  2337.                 PA_LOCK(text_buf, char *, state->line_buf);
  2338.                 text_buf[old_len] = '\0';
  2339.                 PA_UNLOCK(state->line_buf);
  2340.                 state->line_buf_len = old_len;
  2341.  
  2342.                 *w_end = tchar1;
  2343.                 tptr = w_start;
  2344.                 while ((XP_IS_SPACE(*tptr))&&(*tptr != '\0'))
  2345.                 {
  2346.                     tptr++;
  2347.                 }
  2348.                 line_break = TRUE;
  2349.                 w_start = tptr;
  2350.                 w_end = tptr;
  2351.                 tchar1 = *w_end;
  2352.             }
  2353. #else
  2354.  
  2355.             text_data.text = state->line_buf;
  2356.             text_data.text_len = (int16)state->line_buf_len;
  2357.             text_data.text_attr = block->text_attr;
  2358.             FE_GetTextInfo ( context, &text_data, &(state->text_info) );
  2359.  
  2360.             /* update the block's font info cache */
  2361.             block->ascent = state->text_info.ascent;
  2362.             block->descent = state->text_info.descent;
  2363.  
  2364.             /*
  2365.              * If this is a special wrapping pre, then we need to measure this line of text to see
  2366.              * if we need to wrap.
  2367.              */
  2368.             if ( ( state->preformatted == PRE_TEXT_WRAP ) && ( old_begin_line == FALSE ) )
  2369.             {
  2370.                 state->width = lo_correct_text_element_width ( &(state->text_info) );
  2371.                 
  2372.                 lineBufMeasured = TRUE;
  2373.                 
  2374.                 /*
  2375.                  * If this line is now too long, wrap
  2376.                  */
  2377.                 if ( ( state->x + state->width ) > state->right_margin )
  2378.                 {
  2379.                     pre_wrap_break = TRUE;
  2380.                 }
  2381.             }
  2382.             
  2383.             /*
  2384.              * Now check to see if we need to wrap based on being too long for the special pre modes
  2385.              */
  2386.             if ( ( pre_wrap_break != FALSE ) ||
  2387.                 (    ( state->preformatted == PRE_TEXT_COLS ) &&
  2388.                     ( old_begin_line == FALSE ) &&
  2389.                     ( state->preformat_cols > 0 ) &&
  2390.                     ( state->line_buf_len > state->preformat_cols ) ))
  2391.             {
  2392.                 PA_LOCK(text_buf, char *, state->line_buf);
  2393.                 text_buf[old_len] = '\0';
  2394.                 PA_UNLOCK(state->line_buf);
  2395.                 state->line_buf_len = old_len;
  2396.  
  2397.                 *w_end = tchar1;
  2398.                 tptr = w_start;
  2399.                 while ((XP_IS_SPACE(*tptr))&&(*tptr != '\0'))
  2400.                 {
  2401.                     tptr++;
  2402.                 }
  2403.                 line_break = TRUE;
  2404.                 w_start = tptr;
  2405.                 w_end = tptr;
  2406.                 tchar1 = *w_end;
  2407.             }
  2408. #endif
  2409.         }
  2410.  
  2411.         if (tchar1 == LF)
  2412.         {
  2413.             line_break = TRUE;
  2414.         }
  2415.         else if (tchar1 == CR)
  2416.         {
  2417.             line_break = TRUE;
  2418.             have_CR = TRUE;
  2419.         }
  2420.  
  2421.         /* update the buffer position to refleft the new word */
  2422.         block->buffer_read_index = tptr - (char *) block->text_buffer;
  2423.         
  2424.         /*
  2425.          * If we are breaking the line here, flush the
  2426.          * line_buf, and then insert a linebreak.
  2427.          */
  2428.         if (line_break != FALSE)
  2429.         {
  2430. #ifdef LOCAL_DEBUG
  2431. XP_TRACE(("LineBreak, flush text.\n"));
  2432. #endif /* LOCAL_DEBUG */
  2433.  
  2434.             /*
  2435.              * Flush the line and insert the linebreak.
  2436.              */
  2437.             PA_LOCK(text_buf, char *, state->line_buf);
  2438.             text_data.text = state->line_buf;
  2439.             text_data.text_len = (int16)state->line_buf_len;
  2440.             text_data.text_attr = block->text_attr;
  2441.             FE_GetTextInfo(context, &text_data,&(state->text_info));
  2442.             PA_UNLOCK(state->line_buf);
  2443.             state->width = lo_correct_text_element_width(
  2444.                 &(state->text_info));
  2445.  
  2446.             lo_FlushLineBuffer(context, state);
  2447.  
  2448.             lineBufMeasured = TRUE;
  2449. #ifdef EDITOR
  2450.     /* LTNOTE: do something here like: */
  2451.             /*state->edit_current_offset += (word_ptr - text_buf);*/
  2452. #endif
  2453.             if (state->top_state->out_of_memory != FALSE)
  2454.             {
  2455.                 PA_FREE(tmp_block);
  2456.                 return;
  2457.             }
  2458.             /*
  2459.              * Put on a linefeed element.
  2460.              * This line is finished and will be added
  2461.              * to the line array.
  2462.              */
  2463.             lo_SoftLineBreak(context, state, TRUE);
  2464.  
  2465.             state->line_buf_len = 0;
  2466.             state->width = 0;
  2467.  
  2468.             /*
  2469.              * having just broken the line, we have no break
  2470.              * position.
  2471.              */
  2472.             state->break_pos = -1;
  2473.             state->break_width = 0;
  2474.         }
  2475.  
  2476.         *w_end = tchar1;
  2477.         if ((*tptr == CR)||(*tptr == LF))
  2478.         {
  2479.             tptr++;
  2480.         }
  2481.         PA_FREE(tmp_block);
  2482.     }
  2483.  
  2484. #ifndef OLD_WAY
  2485.     /*
  2486.      * If we haven't measured this line of text yet, do so now
  2487.      */
  2488.     if ( !lineBufMeasured )
  2489.     {
  2490.         text_data.text = state->line_buf;
  2491.         text_data.text_len = (int16)state->line_buf_len;
  2492.         text_data.text_attr = block->text_attr;
  2493.         FE_GetTextInfo ( context, &text_data, &(state->text_info) );
  2494.         state->width = lo_correct_text_element_width ( &(state->text_info) );
  2495.     }
  2496. #endif
  2497.  
  2498.     /*
  2499.      * Because we just might get passed text broken between the
  2500.      * CR and the LF, we need to save this state.
  2501.      */
  2502.     if ((tptr > text)&&(*(tptr - 1) == CR))
  2503.     {
  2504.         state->last_char_CR = TRUE;
  2505.     }
  2506.     
  2507.     if ( ( state->cur_ele_type != LO_TEXT ) || ( state->line_buf_len == 0 ) )
  2508.         {
  2509.         state->cur_text_block = NULL;
  2510.         }
  2511. }
  2512.  
  2513. #define CAPITALIZE 0
  2514. #define UPPERCASE  1
  2515. #define LOWERCASE  2
  2516.  
  2517. /* transform the text inline.
  2518.  * "capitalize" : uppercase first letter of each word.
  2519.  * "uppercase" : uppercase every letter
  2520.  * "lowercase" : lowercase every letter
  2521.  * else : do nothing
  2522.  */
  2523. PRIVATE void
  2524. lo_transform_text(char *ptr, int method)
  2525. {
  2526.     XP_Bool possible_first_letter = TRUE;
  2527.  
  2528.     for(; *ptr; ptr++)
  2529.     {
  2530.         switch(method)
  2531.         {
  2532.         case CAPITALIZE:
  2533.                 if(!(XP_IS_SPACE(*ptr)))
  2534.                 {
  2535.                     if(possible_first_letter)
  2536.                     {
  2537.                         *ptr = XP_TO_UPPER(*ptr);
  2538.                         possible_first_letter = FALSE;
  2539.                     }
  2540.                 }
  2541.                 else /* is a space */
  2542.                 {
  2543.                     possible_first_letter = TRUE;
  2544.                 }
  2545.                 break;
  2546.  
  2547.             case UPPERCASE:
  2548.                 *ptr = XP_TO_UPPER(*ptr);
  2549.                 break;
  2550.  
  2551.             case LOWERCASE:
  2552.                 *ptr = XP_TO_LOWER(*ptr);
  2553.                 break;
  2554.  
  2555.             default:
  2556.                 XP_ASSERT(0);
  2557.  
  2558.         }
  2559.     }
  2560. }
  2561.  
  2562. /* see lo_transform_text
  2563.  *
  2564.  * This function just maps the string method to an int
  2565.  */
  2566. PRIVATE void
  2567. lo_transform_text_from_string_method(char *ptr, char *method)
  2568. {
  2569.     if(!strcasecomp(method, "capitalize"))
  2570.         lo_transform_text(ptr, CAPITALIZE);
  2571.     else if(!strcasecomp(method, "lowercase"))
  2572.         lo_transform_text(ptr, LOWERCASE);
  2573.     else if(!strcasecomp(method, "uppercase"))
  2574.         lo_transform_text(ptr, UPPERCASE);
  2575. }
  2576.  
  2577. /*************************************
  2578.  * Function: lo_FormatText
  2579.  *
  2580.  * Description: This function creates a text block element and then calls the format
  2581.  *    text function for it.
  2582.  *
  2583.  * Params: Window context and document state., and the text to be formatted.
  2584.  *
  2585.  * Returns: Nothing
  2586.  *************************************/
  2587. void
  2588. lo_FormatText(MWContext *context, lo_DocState *state, char *text)
  2589. {
  2590.     LO_TextBlock *    block;
  2591.     
  2592.     /* can we use the new style layout? */
  2593.     if ( lo_CanUseBreakTable ( state ) )
  2594.         {
  2595.         block = state->cur_text_block;
  2596.         
  2597.         /* flush any existing text in a partial buffer */
  2598.         if ( ( block != NULL ) && lo_UseBreakTable ( block ) )
  2599.             {
  2600.             lo_LayoutTextBlock ( context, state, TRUE );
  2601.             }
  2602.         
  2603.         /* parse the new text and flush all but any stragglers */
  2604.         lo_AppendTextToBlock ( context, state, NULL, text );
  2605.         lo_LayoutTextBlock ( context, state, FALSE );
  2606.         }
  2607.     else
  2608.         {
  2609.         block = lo_NewTextBlock ( context, state, text, state->preformatted );
  2610.         if ( !state->top_state->out_of_memory && ( block != NULL ) )
  2611.             {
  2612.             block->buffer_read_index = 0;
  2613.             lo_LayoutFormattedText ( context, state, block );
  2614.             }
  2615.         }
  2616. }
  2617.  
  2618.  
  2619. /*************************************
  2620.  * Function: lo_FormatText
  2621.  *
  2622.  * Description: This function formats text by breaking it into lines
  2623.  *    at word boundries.  Word boundries are whitespace, or special
  2624.  *    word break tags.
  2625.  *
  2626.  * Params: Window context and document state., and the text to be formatted.
  2627.  *
  2628.  * Returns: Nothing
  2629.  *************************************/
  2630. void
  2631. lo_LayoutFormattedText(MWContext *context, lo_DocState *state, LO_TextBlock * block)
  2632. {
  2633.     char *tptr;
  2634.     char *w_start;
  2635.     char *w_end;
  2636.     char *text_buf;
  2637.     char tchar1;
  2638.     int32 word_len;
  2639.     Bool line_break;
  2640.     Bool word_break;
  2641.     Bool prev_word_breakable;
  2642.     Bool white_space;
  2643.     int16 charset;
  2644.     Bool multi_byte;
  2645.     LO_TextStruct text_data;
  2646.     char * text;
  2647.     
  2648.     /* start at the current text position in this text block */
  2649.     text = (char *) &block->text_buffer[ block->buffer_read_index ];
  2650.     
  2651.  
  2652. #ifdef XP_OS2                  /* performance                            */
  2653.    int32 maxw;            /* performance - max char width for font  */
  2654.    int32 estwidth;        /* performance - estimated width for line */
  2655.    int textsw;            /* performance                            */
  2656.    maxw  = 0;
  2657.    textsw = 0;            /* performance - need to mark no width taken */
  2658. #endif
  2659.  
  2660.  
  2661.     /*
  2662.      * Initialize the structures to 0 (mark)
  2663.      */
  2664.     memset (&text_data, 0, sizeof (LO_TextStruct));
  2665.  
  2666.     /*
  2667.      * Error conditions
  2668.      */
  2669.     if ((state == NULL)||(state->cur_ele_type != LO_TEXT)||(text == NULL))
  2670.     {
  2671.         return;
  2672.     }
  2673.  
  2674.     charset = block->text_attr->charset;
  2675.     if ((INTL_CharSetType(charset) == SINGLEBYTE) ||
  2676.         (INTL_CharSetType(charset) & CS_SPACE))
  2677.     {
  2678.         multi_byte = FALSE;
  2679.     }
  2680.     else
  2681.     {
  2682.         multi_byte = TRUE;
  2683.     }
  2684.  
  2685.     /*
  2686.      * Move through this text fragment, breaking it up into
  2687.      * words, and then grouping the words into lines.
  2688.      */
  2689.     tptr = text;
  2690.     prev_word_breakable = FALSE;
  2691.     while ((*tptr != '\0')&&(state->top_state->out_of_memory == FALSE))
  2692.     {
  2693.         PA_Block nbsp_block;
  2694.         Bool has_nbsp;
  2695. #ifdef TEXT_CHUNK_LIMIT
  2696.         int32 w_char_cnt;
  2697. #endif /* TEXT_CHUNK_LIMIT */
  2698. #ifdef XP_WIN16
  2699.         int32 ccnt;
  2700. #endif /* XP_WIN16 */
  2701.         Bool mb_sp;  /* Allow space between multibyte */
  2702.         
  2703.         /*
  2704.          * white_space is a tag to tell us if the currenct word
  2705.          * contains nothing but whitespace.
  2706.          * word_break tells us if there was whitespace
  2707.          * before this word so we know we can break it.
  2708.          */
  2709.         white_space = FALSE;
  2710.         word_break = FALSE;
  2711.         line_break = FALSE;
  2712.         nbsp_block = NULL;
  2713.         has_nbsp = FALSE;
  2714.         mb_sp = FALSE;
  2715.  
  2716.         if (multi_byte == FALSE)
  2717.         {
  2718.             /* check for textTransform properties and apply them */
  2719.             if(state->top_state && state->top_state->style_stack)
  2720.             {
  2721.                 char *property;
  2722.                 StyleStruct *style_struct = STYLESTACK_GetStyleByIndex(
  2723.                                                 state->top_state->style_stack, 
  2724.                                                 0);
  2725.  
  2726.                 if(style_struct)
  2727.                 {
  2728.                     property = STYLESTRUCT_GetString(style_struct, 
  2729.                                                      TEXT_TRANSFORM_STYLE);
  2730.  
  2731.                     if(property)
  2732.                     {
  2733.                         lo_transform_text_from_string_method(tptr, property);
  2734.                     }
  2735.                 }
  2736.             }
  2737.         
  2738.             /*
  2739.              * Find the start of the word, skipping whitespace.
  2740.              */
  2741.             w_start = tptr;
  2742.             while ((XP_IS_SPACE(*tptr))&&(*tptr != '\0'))
  2743.             {
  2744.                 tptr++;
  2745.             }
  2746.  
  2747.             /*
  2748.              * if tptr has been moved at all, that means
  2749.              * there was some whitespace to skip, which means
  2750.              * we are allowed to put a linebreak before this
  2751.              * word if we want to.
  2752.              */
  2753.             if (tptr != w_start)
  2754.             {
  2755.                 int32 new_break_holder;
  2756.                 int32 min_width;
  2757.                 int32 indent;
  2758.  
  2759.                 w_start = tptr;
  2760.                 word_break = TRUE;
  2761.  
  2762.                 new_break_holder = state->x + state->width;
  2763.                 min_width = new_break_holder - state->break_holder;
  2764.                 indent = state->list_stack->old_left_margin -
  2765.                         state->win_left;
  2766.                 min_width += indent;
  2767.                 if (min_width > state->min_width)
  2768.                 {
  2769.                     state->min_width = min_width;
  2770.                 }
  2771.                 /* If we are not within <NOBR> content, allow break_holder
  2772.                  * to be set to the new position where a line break can occur.
  2773.                  * This fixes BUG #70782
  2774.                  */
  2775.                 if (state->breakable != FALSE) {
  2776.                     state->break_holder = new_break_holder;
  2777.                 }
  2778.             }
  2779.  
  2780.             /*
  2781.                 * If we are in text that is supposed to be
  2782.              * justified, we want each word to be in a separate
  2783.              * text element, so if we just found a word break,
  2784.              * and there is already a word in the line buffer,
  2785.              * flush that word into its own element.
  2786.              */
  2787.             if ((state->align_stack != NULL)&&
  2788.                (state->align_stack->alignment ==LO_ALIGN_JUSTIFY)&&
  2789.                (word_break != FALSE)&&
  2790.                (state->cur_ele_type == LO_TEXT)&&
  2791.                (state->line_buf_len != 0))
  2792.             {
  2793.                 /* set the current text offset for this new word */
  2794.                 block->buffer_read_index = tptr - (char *) block->text_buffer;
  2795.                 
  2796.                 lo_FlushLineBuffer(context, state);
  2797.                 if (state->top_state->out_of_memory != FALSE)
  2798.                 {
  2799.                     return;
  2800.                 }
  2801.             }
  2802.  
  2803.             /*
  2804.              * Find the end of the word.
  2805.              * Terminate the word, saving the char we replaced
  2806.              * with the terminator so it can be restored later.
  2807.              */
  2808. #ifdef TEXT_CHUNK_LIMIT
  2809.             w_char_cnt = 0;
  2810. #endif /* TEXT_CHUNK_LIMIT */
  2811. #ifdef XP_WIN16
  2812.             ccnt = state->line_buf_len;
  2813.             while ((!XP_IS_SPACE(*tptr))&&(*tptr != '\0')&&(ccnt < SIZE_LIMIT))
  2814.             {
  2815.                 if ((unsigned char)*tptr == NON_BREAKING_SPACE)
  2816.                 {
  2817.                     has_nbsp = TRUE;
  2818.                 }
  2819.                 tptr++;
  2820. #ifdef TEXT_CHUNK_LIMIT
  2821.                 w_char_cnt++;
  2822. #endif /* TEXT_CHUNK_LIMIT */
  2823.                 ccnt++;
  2824.             }
  2825.             if (ccnt >= SIZE_LIMIT)
  2826.             {
  2827.                 line_break = TRUE;
  2828.             }
  2829. #else
  2830.             while ((!XP_IS_SPACE(*tptr))&&(*tptr != '\0'))
  2831.             {
  2832.                 if ((unsigned char)*tptr == NON_BREAKING_SPACE)
  2833.                 {
  2834.                     /* *tptr = ' '; Replace this later */
  2835.                     has_nbsp = TRUE;
  2836.                 }
  2837.                 tptr++;
  2838. #ifdef TEXT_CHUNK_LIMIT
  2839.                 w_char_cnt++;
  2840. #endif /* TEXT_CHUNK_LIMIT */
  2841.             }
  2842. #endif /* XP_WIN16 */
  2843.         }
  2844.         else
  2845.         {
  2846.             has_nbsp = TRUE;
  2847.             /*
  2848.              * Find the start of the word, skipping whitespace.
  2849.              */
  2850.             w_start = tptr;
  2851.             while ((XP_IS_SPACE(*tptr))&&(*tptr != '\0'))
  2852.             {
  2853.                 tptr = INTL_NextChar(charset, tptr);
  2854.             }
  2855.             if (w_start != tptr)
  2856.                 mb_sp = TRUE;
  2857.  
  2858.             /*
  2859.              * if tptr has been moved at all, that means
  2860.              * there was some whitespace to skip, which means
  2861.              * we are allowed to put a linebreak before this
  2862.              * word if we want to.
  2863.              */
  2864.             /*
  2865.              * If this char is a two-byte thing, we can break
  2866.              * before it.
  2867.              */
  2868.             if ((tptr != w_start)||((unsigned char)*tptr > 127))
  2869.             {
  2870.                 int32 new_break_holder;
  2871.                 int32 min_width;
  2872.                 int32 indent;
  2873.  
  2874.                 /* If it's multibyte character, it always be able to break */
  2875.                 if (tptr == w_start)
  2876.                     prev_word_breakable = TRUE;
  2877.                 w_start = tptr;
  2878.                 word_break = TRUE;
  2879.  
  2880.                 new_break_holder = state->x + state->width;
  2881.                 min_width = new_break_holder - state->break_holder;
  2882.                 indent = state->list_stack->old_left_margin -
  2883.                         state->win_left;
  2884.                 min_width += indent;
  2885.                 if (min_width > state->min_width)
  2886.                 {
  2887.                     state->min_width = min_width;
  2888.                 }
  2889.                 /* If we are not within <NOBR> content, allow break_holder
  2890.                  * to be set to the new position where a line break can occur.
  2891.                  * This fixes BUG #70782
  2892.                  */
  2893.                 if (state->breakable != FALSE) {
  2894.                     state->break_holder = new_break_holder;
  2895.                 }
  2896.             }
  2897.             else if (prev_word_breakable)
  2898.             {
  2899.                 int32 new_break_holder;
  2900.                 int32 min_width;
  2901.                 int32 indent;
  2902.  
  2903.                 prev_word_breakable = FALSE;
  2904.                 w_start = tptr;
  2905.                 word_break = TRUE;
  2906.  
  2907.                 new_break_holder = state->x + state->width;
  2908.                 min_width = new_break_holder - state->break_holder;
  2909.                 indent = state->list_stack->old_left_margin -
  2910.                         state->win_left;
  2911.                 min_width += indent;
  2912.                 if (min_width > state->min_width)
  2913.                 {
  2914.                     state->min_width = min_width;
  2915.                 }
  2916.                 /* If we are not within <NOBR> content, allow break_holder
  2917.                  * to be set to the new position where a line break can occur.
  2918.                  * This fixes BUG #70782
  2919.                  */
  2920.                 if (state->breakable != FALSE) {
  2921.                     state->break_holder = new_break_holder;
  2922.                 }
  2923.             }
  2924.  
  2925.             /*
  2926.              * Find the end of the word.
  2927.              * Terminate the word, saving the char we replaced
  2928.              * with the terminator so it can be restored later.
  2929.              */
  2930. #ifdef TEXT_CHUNK_LIMIT
  2931.             w_char_cnt = 0;
  2932. #endif /* TEXT_CHUNK_LIMIT */
  2933. #ifdef XP_WIN16
  2934.             ccnt = state->line_buf_len;
  2935.             while ((  ((unsigned char)*tptr < 128) 
  2936.                         || (INTL_KinsokuClass(charset, (unsigned char *)tptr) == PROHIBIT_WORD_BREAK )
  2937.                    ) && (!XP_IS_SPACE(*tptr)) 
  2938.                      && (*tptr != '\0')
  2939.                      && (ccnt < SIZE_LIMIT))
  2940.             {
  2941.                 intn c_len;
  2942.                 char *tptr2;
  2943.  
  2944.                 tptr2 = INTL_NextChar(charset, tptr);
  2945.                 c_len = (intn)(tptr2 - tptr);
  2946.                 tptr = tptr2;
  2947. #ifdef TEXT_CHUNK_LIMIT
  2948.                 w_char_cnt += c_len;
  2949. #endif /* TEXT_CHUNK_LIMIT */
  2950.                 ccnt += c_len;
  2951.             }
  2952.             if (ccnt >= SIZE_LIMIT)
  2953.             {
  2954.                 line_break = TRUE;
  2955.             }
  2956. #else
  2957.  
  2958. #if 0
  2959.             while ( /* Change the order so we have better performance */
  2960.                      (*tptr != '\0')
  2961.                      && (!XP_IS_SPACE(*tptr)) 
  2962.                      && (    ((unsigned char)*tptr < 128)
  2963.                             || ((CS_UTF8 == charset) /* hack, since we know only CS_UTF8 have PROHIBIT_WORD_BREAK*/
  2964.                                 && (INTL_KinsokuClass(charset, (unsigned char *)tptr) == PROHIBIT_WORD_BREAK ))
  2965.                         )
  2966.                      )
  2967. #else
  2968.             while (
  2969.                      (*tptr != '\0')
  2970.                      && (!XP_IS_SPACE(*tptr)) 
  2971.                      && (    ((unsigned char)*tptr < 128)
  2972.                             || ((CS_UTF8 == charset) && (*(unsigned char *)tptr <= 0xE2))                    
  2973.                             /* In case of CS_UTF8, some code range like CJK character baundary is breakable.
  2974.                              * While in range UCS2 < 0x2000 (roman), character baundary is not breakable. 
  2975.                              */
  2976.                         )
  2977.                      )
  2978. #endif
  2979.             {
  2980.                 intn c_len;
  2981.                 char *tptr2;
  2982.  
  2983.                 tptr2 = INTL_NextChar(charset, tptr);
  2984.                 c_len = (intn)(tptr2 - tptr);
  2985.                 tptr = tptr2;
  2986. #ifdef TEXT_CHUNK_LIMIT
  2987.                 w_char_cnt += c_len;
  2988. #endif /* TEXT_CHUNK_LIMIT */
  2989.             }
  2990. #endif /* XP_WIN16 */
  2991.         }  /* multi byte */
  2992.  
  2993. #ifdef TEXT_CHUNK_LIMIT
  2994.         if (w_char_cnt > TEXT_CHUNK_LIMIT)
  2995.         {
  2996.             tptr = (char *)(tptr - (w_char_cnt - TEXT_CHUNK_LIMIT));
  2997.             w_char_cnt = TEXT_CHUNK_LIMIT;
  2998.         }
  2999.  
  3000.         if ((state->line_buf_len + w_char_cnt) > TEXT_CHUNK_LIMIT)
  3001.         {
  3002.             lo_FlushLineBuffer(context, state);
  3003.             if (state->top_state->out_of_memory != FALSE)
  3004.             {
  3005.                 return;
  3006.             }
  3007.             
  3008.             if (state->cur_ele_type != LO_TEXT)
  3009.             {
  3010.                 lo_FreshText(state);
  3011.                 state->cur_ele_type = LO_TEXT;
  3012.             }
  3013.         }
  3014. #endif /* TEXT_CHUNK_LIMIT */
  3015.         if (multi_byte != FALSE)
  3016.         {
  3017.             if ((w_start == tptr)&&((unsigned char)*tptr > 127))
  3018.             {
  3019.                 tptr = INTL_NextChar(charset, tptr);
  3020.             }
  3021.         }
  3022.         w_end = tptr;
  3023.         tchar1 = *w_end;
  3024.         *w_end = '\0';
  3025.  
  3026.         /*
  3027.          * If the "word" is just an empty string, this
  3028.          * is just whitespace that we may wish to compress out.
  3029.          */
  3030.         if (*w_start == '\0')
  3031.         {
  3032.             white_space = TRUE;
  3033.         }
  3034.  
  3035.         /*
  3036.          * compress out whitespace if the last word added was also
  3037.          * whitespace.
  3038.          */
  3039.         if ((white_space != FALSE)&&(state->trailing_space != FALSE))
  3040.         {
  3041.             *w_end = tchar1;
  3042. #ifdef LOCAL_DEBUG
  3043. XP_TRACE(("Discarding(%s)\n", w_start));
  3044. #endif /* LOCAL_DEBUG */
  3045.             continue;
  3046.         }
  3047.  
  3048.         /*
  3049.          * This places the preceeding space in front of
  3050.          * separate words on a line.
  3051.          * Unecessary if last item was trailng space.
  3052.          *
  3053.          * If there was a word break before this word, so we know it
  3054.          * was supposed to be separate, and if we are not at the
  3055.          * beginning of the line, and if the
  3056.          * preceeding word is not already whitespace, then add
  3057.          * a space before this word.
  3058.          */
  3059.         if ((word_break != FALSE)&&
  3060.             (state->at_begin_line == FALSE)&&
  3061.             (state->trailing_space == FALSE))
  3062.         {
  3063.             /*
  3064.              * Since word_break is true, we know
  3065.              * we skipped some spaces previously
  3066.              * so we know there is space to back up
  3067.              * the word pointer inside the buffer.
  3068.              */
  3069.             if ((multi_byte == FALSE)||mb_sp)
  3070.             {
  3071.                 w_start--;
  3072.                 *w_start = ' ';
  3073.             }
  3074.  
  3075.             /*
  3076.              * If we are formatting breakable text
  3077.              * set break position to be just before this word.
  3078.              * This is where we will break this line if the
  3079.              * new word makes it too long.
  3080.              */
  3081.             if (state->breakable != FALSE)
  3082.             {
  3083.                 state->break_pos = state->line_buf_len;
  3084.                 state->break_width = state->width;
  3085.             }
  3086.         }
  3087.  
  3088. #ifdef LOCAL_DEBUG
  3089. XP_TRACE(("Found Word (%s)\n", w_start));
  3090. #endif /* LOCAL_DEBUG */
  3091.         /*
  3092.          * If this is an empty string, just throw it out
  3093.          * and move on
  3094.          */
  3095.         if (*w_start == '\0')
  3096.         {
  3097.             *w_end = tchar1;
  3098. #ifdef LOCAL_DEBUG
  3099. XP_TRACE(("Throwing out empty string!\n"));
  3100. #endif /* LOCAL_DEBUG */
  3101.             continue;
  3102.         }
  3103.  
  3104.         /*
  3105.          * Now we catch those nasty non-breaking space special
  3106.          * characters and make them spaces.  Yuck, so that 
  3107.          * relayout in tables will still see the non-breaking
  3108.          * spaces, we need to copy the buffer here.
  3109.          */
  3110.         if (has_nbsp != FALSE)
  3111.         {
  3112.             char *tmp_ptr;
  3113.             char *to_ptr;
  3114.             char *tmp_buf;
  3115.  
  3116.             nbsp_block = PA_ALLOC(XP_STRLEN(w_start) + 1);
  3117.             if (nbsp_block == NULL)
  3118.             {
  3119.                 *w_end = tchar1;
  3120.                 state->top_state->out_of_memory = TRUE;
  3121.                 break;
  3122.             }
  3123.             PA_LOCK(tmp_buf, char *, nbsp_block);
  3124.  
  3125.             tmp_ptr = w_start;
  3126.             to_ptr = tmp_buf;
  3127.             while (*tmp_ptr != '\0')
  3128.             {
  3129.                 *to_ptr = *tmp_ptr;
  3130.                 if (((unsigned char)*to_ptr == NON_BREAKING_SPACE)
  3131.                     && (CS_USER_DEFINED_ENCODING != charset))
  3132.                 {
  3133.                     *to_ptr = ' ';
  3134.                 }
  3135.                 if(multi_byte) {
  3136.                     int i;
  3137.                     int len = INTL_CharLen(charset,
  3138.                                                                (unsigned char *)tmp_ptr);
  3139.                     to_ptr++;
  3140.                     tmp_ptr++;
  3141.                     for (i=1; (i<len) && (*tmp_ptr != '\0'); i++) {
  3142.                         *to_ptr++  = *tmp_ptr++;
  3143.                     }
  3144.                 }
  3145.                 else {
  3146.                     to_ptr++;
  3147.                     tmp_ptr++;
  3148.                 }
  3149.             }
  3150.             *w_end = tchar1;
  3151.             w_start = tmp_buf;
  3152.             w_end = to_ptr;
  3153.             *w_end = '\0';
  3154.         }
  3155.  
  3156.         /*
  3157.          * Make this Front End specific text, and count
  3158.          * the length of the word.
  3159.          */
  3160.         /* don't need this any more since we're converting
  3161.          * elsewhere -- erik
  3162.         w_start = FE_TranslateISOText(context, charset, w_start);
  3163.          */
  3164.         word_len = XP_STRLEN(w_start);
  3165.  
  3166.         /*
  3167.          * Append this word to the line buffer.
  3168.          * It may be necessary to expand the line
  3169.          * buffer.
  3170.          */
  3171.         if ((state->line_buf_len + word_len + 1) >
  3172.             state->line_buf_size)
  3173.         {
  3174.             state->line_buf = PA_REALLOC( state->line_buf,
  3175.                 (state->line_buf_size +
  3176.                 word_len + LINE_BUF_INC));
  3177.             if (state->line_buf == NULL)
  3178.             {
  3179.                 *w_end = tchar1;
  3180.                 if ((has_nbsp != FALSE)&&(nbsp_block != NULL))
  3181.                 {
  3182.                     PA_UNLOCK(nbsp_block);
  3183.                     PA_FREE(nbsp_block);
  3184.                     nbsp_block = NULL;
  3185.                 }
  3186.                 state->top_state->out_of_memory = TRUE;
  3187.                 break;
  3188.             }
  3189.             state->line_buf_size += (word_len + LINE_BUF_INC);
  3190.         }
  3191.         PA_LOCK(text_buf, char *, state->line_buf);
  3192.         XP_BCOPY(w_start,
  3193.             (char *)(text_buf + state->line_buf_len),
  3194.             (word_len + 1));
  3195.         state->line_buf_len += word_len;
  3196.         PA_UNLOCK(state->line_buf);
  3197.  
  3198.         /*
  3199.          * Having added a word, we cannot be at the start of a line
  3200.          */
  3201.         state->cur_ele_type = LO_TEXT;
  3202.         state->at_begin_line = FALSE;
  3203.  
  3204.         /*
  3205.          * Most common case is appending to the same line.
  3206.          * assume that is what we are doing here.
  3207.          */
  3208.         text_data.text = state->line_buf;
  3209.         text_data.text_len = (int16)state->line_buf_len;
  3210.         text_data.text_attr = state->cur_text_block->text_attr;
  3211.         FE_GetTextInfo(context, &text_data, &(state->text_info));
  3212.         state->width =
  3213.             lo_correct_text_element_width(&(state->text_info));
  3214.         
  3215.         /* udpate the block's font info cache */
  3216.         block->ascent = state->text_info.ascent;
  3217.         block->descent = state->text_info.descent;
  3218.         
  3219.         /*
  3220.          * Set line_break based on document window width
  3221.          */
  3222. #ifdef XP_WIN16
  3223.         if (((state->x + state->width) > state->right_margin)||(line_break != FALSE))
  3224. #else
  3225.         if ((state->x + state->width) > state->right_margin)
  3226. #endif /* XP_WIN16 */
  3227.         {
  3228.             /*  
  3229.              * INTL kinsoku line break, some of characters are not allowed to put 
  3230.              * in the end of line or beginning of line
  3231.              */
  3232.             if (multi_byte && (state->break_pos != -1))
  3233.             {
  3234.                 int cur_wordtype, pre_wordtype, pre_break_pos;
  3235.                 cur_wordtype = INTL_KinsokuClass(charset, (unsigned char *) w_start);
  3236.  
  3237.                 PA_LOCK(text_buf, char *, state->line_buf);
  3238.                 pre_break_pos = INTL_PrevCharIdx(charset, 
  3239.                     (unsigned char *)text_buf, state->break_pos);
  3240.                 pre_wordtype = INTL_KinsokuClass(charset, 
  3241.                     (unsigned char *)(text_buf + pre_break_pos));
  3242.  
  3243.                 if (pre_wordtype == PROHIBIT_END_OF_LINE ||
  3244.                     (cur_wordtype == PROHIBIT_BEGIN_OF_LINE && 
  3245.                      XP_IS_ALPHA(*(text_buf+pre_break_pos)) == FALSE))
  3246.                     state->break_pos = pre_break_pos;
  3247.  
  3248.                 PA_UNLOCK(state->line_buf);
  3249.             }
  3250.             line_break = TRUE;
  3251.         }
  3252.         else
  3253.         {
  3254.             line_break = FALSE;
  3255.         }
  3256.  
  3257.         /*
  3258.          * We cannot break a line if we have no break positions.
  3259.          * Usually happens with a single line of unbreakable text.
  3260.          */
  3261.         if ((line_break != FALSE)&&(state->break_pos == -1))
  3262.         {
  3263.             /*
  3264.              * It may be possible to break a previous
  3265.              * text element on the same line.
  3266.              */
  3267.             if (state->old_break_pos != -1)
  3268.             {
  3269.                 lo_BreakOldElement(context, state);
  3270.                 line_break = FALSE;
  3271.             }
  3272. #ifdef XP_WIN16
  3273.             else if (ccnt >= SIZE_LIMIT)
  3274.             {
  3275.                 state->break_pos = state->line_buf_len - 1;
  3276.             }
  3277.             else
  3278.             {
  3279.                 line_break = FALSE;
  3280.             }
  3281. #else
  3282.             else
  3283.             {
  3284.                 line_break = FALSE;
  3285.             }
  3286. #endif /* XP_WIN16 */
  3287.         }
  3288.  
  3289.         /*
  3290.          * If we are breaking the line here, flush the
  3291.          * line_buf, and then insert a linebreak.
  3292.          */
  3293.         if (line_break != FALSE)
  3294.         {
  3295.             char *break_ptr;
  3296.             char *word_ptr;
  3297.             char *new_buf;
  3298.             PA_Block new_block;
  3299. #ifdef LOCAL_DEBUG
  3300. XP_TRACE(("LineBreak, flush text.\n"));
  3301. #endif /* LOCAL_DEBUG */
  3302.  
  3303.             /*
  3304.              * Find the breaking point, and the pointer
  3305.              * to the remaining word without its leading
  3306.              * space.
  3307.              */
  3308.             PA_LOCK(text_buf, char *, state->line_buf);
  3309.             break_ptr = (char *)(text_buf + state->break_pos);
  3310. /*            word_ptr = (char *)(break_ptr + 1); */
  3311.             word_ptr = break_ptr;
  3312.  
  3313.             if ((multi_byte == FALSE)||mb_sp)
  3314.             {
  3315.                 word_ptr++;
  3316.             }
  3317.  
  3318.             /*
  3319.              * Copy the remaining word into its
  3320.              * own buffer.
  3321.              */
  3322.             word_len = XP_STRLEN(word_ptr);
  3323.             new_block = PA_ALLOC((word_len + 1) *
  3324.                 sizeof(char));
  3325.             if (new_block == NULL)
  3326.             {
  3327.                 PA_UNLOCK(state->line_buf);
  3328.                 *w_end = tchar1;
  3329.                 if ((has_nbsp != FALSE)&&(nbsp_block != NULL))
  3330.                 {
  3331.                     PA_UNLOCK(nbsp_block);
  3332.                     PA_FREE(nbsp_block);
  3333.                     nbsp_block = NULL;
  3334.                 }
  3335.                 state->top_state->out_of_memory = TRUE;
  3336.                 break;
  3337.             }
  3338.             PA_LOCK(new_buf, char *, new_block);
  3339.             XP_BCOPY(word_ptr, new_buf, (word_len + 1));
  3340.  
  3341.             *break_ptr = '\0';
  3342.             state->line_buf_len = state->line_buf_len -
  3343.                 word_len;
  3344.             if ((multi_byte == FALSE)||(word_ptr != break_ptr))
  3345.             {
  3346.                 state->line_buf_len--;
  3347.             }
  3348.             text_data.text = state->line_buf;
  3349.             text_data.text_len = (int16)state->line_buf_len;
  3350.             text_data.text_attr = state->cur_text_block->text_attr;
  3351.             FE_GetTextInfo(context, &text_data,&(state->text_info));
  3352.             PA_UNLOCK(state->line_buf);
  3353.             state->width = lo_correct_text_element_width(
  3354.                 &(state->text_info));
  3355.             
  3356.             lo_FlushLineBuffer(context, state);
  3357. #ifdef EDITOR
  3358.             state->edit_current_offset += (word_ptr - text_buf);
  3359. #endif
  3360.             if (state->top_state->out_of_memory != FALSE)
  3361.             {
  3362.                 PA_UNLOCK(new_block);
  3363.                 PA_FREE(new_block);
  3364.                 if ((has_nbsp != FALSE)&&(nbsp_block != NULL))
  3365.                 {
  3366.                     PA_UNLOCK(nbsp_block);
  3367.                     PA_FREE(nbsp_block);
  3368.                     nbsp_block = NULL;
  3369.                 }
  3370.                 return;
  3371.             }
  3372.  
  3373.             /*
  3374.              * Put on a linefeed element.
  3375.              * This line is finished and will be added
  3376.              * to the line array.
  3377.              */
  3378.             lo_SoftLineBreak(context, state, TRUE);
  3379.  
  3380.             /*
  3381.              * If there was no remaining word, free up
  3382.              * the unnecessary buffer, and empty out
  3383.              * the line buffer.
  3384.              */
  3385.             if (word_len == 0)
  3386.             {
  3387.                 PA_UNLOCK(new_block);
  3388.                 PA_FREE(new_block);
  3389.                 state->line_buf_len = 0;
  3390.                 state->width = 0;
  3391.             }
  3392.             else
  3393.             {
  3394.                 PA_LOCK(text_buf, char *,state->line_buf);
  3395.                 XP_BCOPY(new_buf, text_buf, (word_len + 1));
  3396.                 PA_UNLOCK(state->line_buf);
  3397.                 PA_UNLOCK(new_block);
  3398.                 PA_FREE(new_block);
  3399.                 state->line_buf_len = word_len;
  3400.                 text_data.text = state->line_buf;
  3401.                 text_data.text_len = (int16)state->line_buf_len;
  3402.                 text_data.text_attr =
  3403.                     state->cur_text_block->text_attr;
  3404.                 FE_GetTextInfo(context, &text_data,
  3405.                     &(state->text_info));
  3406.                 state->width = lo_correct_text_element_width(
  3407.                     &(state->text_info));
  3408.  
  3409.                 /*
  3410.                  * Having added text, we are no longer at the
  3411.                  * start of the line.
  3412.                  */
  3413.                 state->at_begin_line = FALSE;
  3414.                 state->cur_ele_type = LO_TEXT;
  3415.             }
  3416.  
  3417.  
  3418.             /*
  3419.              * having just broken the line, we have no break
  3420.              * position.
  3421.              */
  3422.             state->break_pos = -1;
  3423.             state->break_width = 0;
  3424.         }
  3425.         else
  3426.         {
  3427.             /* this word fits, so update the text buffer position */
  3428.             block->buffer_read_index = tptr - (char *) block->text_buffer;
  3429.  
  3430.             if (white_space != FALSE)
  3431.             {
  3432.                 state->trailing_space = TRUE;
  3433.             }
  3434.             else
  3435.             {
  3436.                 state->trailing_space = FALSE;
  3437.             }
  3438.         }
  3439.  
  3440.         *w_end = tchar1;
  3441.         /*
  3442.          * Free up the extra block used for non-breaking
  3443.          * spaces if we had to allocate one.
  3444.          */
  3445.         if ((has_nbsp != FALSE)&&(nbsp_block != NULL))
  3446.         {
  3447.             PA_UNLOCK(nbsp_block);
  3448.             PA_FREE(nbsp_block);
  3449.             nbsp_block = NULL;
  3450.         }
  3451.     }
  3452.     /*  
  3453.      * if last char is multibyte, break position need to be set to end of string
  3454.      */
  3455.     if (multi_byte != FALSE && *tptr == '\0' && prev_word_breakable != FALSE)
  3456.         state->break_pos = state->line_buf_len;
  3457.     
  3458.     if ( ( state->cur_ele_type != LO_TEXT ) || ( state->line_buf_len == 0 ) )
  3459.         {
  3460.         state->cur_text_block = NULL;
  3461.         }
  3462. }
  3463.  
  3464.  
  3465. /*************************************
  3466.  * Function: lo_FlushLineBuffer
  3467.  *
  3468.  * Description: Flush out the current line buffer of text
  3469.  *    into a new text element, and add that element to
  3470.  *    the end of the line list of elements.
  3471.  *
  3472.  * Params: Window context and document state.
  3473.  *
  3474.  * Returns: Nothing
  3475.  *************************************/
  3476. void
  3477. lo_FlushLineBuffer(MWContext *context, lo_DocState *state)
  3478. {
  3479.     LO_TextStruct *text_data;
  3480.     int32 baseline_inc;
  3481.     LO_TextBlock * block;
  3482.     
  3483.     baseline_inc = 0;
  3484. #ifdef DEBUG
  3485.     assert (state);
  3486. #endif
  3487.         
  3488.     block = state->cur_text_block;
  3489.     
  3490.     /* bail if we have nothing to do with text */
  3491.     if ( ( block == NULL ) || ( state->cur_ele_type != LO_TEXT ) )
  3492.         {
  3493.         return;
  3494.         }
  3495.     
  3496.     /*
  3497.      * If we're currently using the new break table layout, then bail to it
  3498.      */
  3499.     if ( lo_UseBreakTable ( block ) )
  3500.         {
  3501.         lo_FlushText ( context, state );
  3502.         return;
  3503.         }
  3504.     
  3505.     /*
  3506.      * Make sure we have some text to flush
  3507.      */
  3508.     if ( state->line_buf_len == 0 )
  3509.         {
  3510.         return;
  3511.         }
  3512.         
  3513.     /* 
  3514.      * LTNOTE: probably should be grabbing state edit_element and offset from
  3515.      * state.
  3516.     */
  3517.     text_data = lo_new_text_element(context, state, NULL, 0);
  3518.  
  3519.     if (text_data == NULL)
  3520.     {
  3521.         state->top_state->out_of_memory = TRUE;
  3522.         return;
  3523.     }
  3524.     state->linefeed_state = 0;
  3525.  
  3526.     /*
  3527.      * Some fonts (particulatly italic ones with curly tails
  3528.      * on letters like 'f') have a left bearing that extends
  3529.      * back into the previous character.  Since in this case the
  3530.      * previous character is probably not in the same font, we
  3531.      * move forward to avoid overlap.
  3532.      *
  3533.      * Those same funny fonts can extend past the last character,
  3534.      * and we also have to catch that, and advance the following text
  3535.      * to eliminate cutoff.
  3536.      */
  3537.     if (state->text_info.lbearing < 0)
  3538.     {
  3539.         text_data->x_offset = state->text_info.lbearing * -1;
  3540.     }
  3541.     text_data->width = state->width;
  3542.     
  3543.     /* 
  3544.      * record the current doc width and text buffer offset for use
  3545.      * during relayout.
  3546.      */
  3547.     text_data->doc_width = state->right_margin - state->x;
  3548.     text_data->block_offset = block->buffer_read_index;
  3549.     XP_ASSERT(block->buffer_read_index <= 65535);
  3550.     
  3551.     baseline_inc = lo_compute_text_basline_inc ( state, block, text_data );
  3552.     
  3553.     lo_AppendToLineList(context, state, (LO_Element *)text_data, baseline_inc);
  3554.  
  3555.     if ( block->startTextElement == NULL )
  3556.         {
  3557.         block->startTextElement = text_data;
  3558.         block->endTextElement = text_data;
  3559.         }
  3560.     else
  3561.         {
  3562.         block->endTextElement = text_data;
  3563.         }
  3564.         
  3565.     text_data->height = state->text_info.ascent +
  3566.         state->text_info.descent;
  3567.  
  3568.     /*
  3569.      * If the element we just flushed had a breakable word
  3570.      * position in it, save that position in case we have
  3571.      * to go back and break this element before we finish
  3572.      * the line.
  3573.      */
  3574.     if (state->break_pos != -1)
  3575.     {
  3576.         state->old_break = text_data;
  3577.         state->old_break_block = block;
  3578.         state->old_break_pos = state->break_pos;
  3579.         state->old_break_width = state->break_width;
  3580.     }
  3581.  
  3582.     state->line_buf_len = 0;
  3583.     state->x += state->width;
  3584.     state->width = 0;
  3585.     state->cur_ele_type = LO_NONE;
  3586. }
  3587.  
  3588. void
  3589. lo_FlushTextBlock ( MWContext *context, lo_DocState *state )
  3590. {
  3591.     lo_FlushLineBuffer ( context, state );
  3592.     
  3593.     state->cur_text_block = NULL;
  3594. }
  3595.  
  3596. void
  3597. lo_ChangeBodyTextFGColor(MWContext *context, lo_DocState *state, 
  3598.                          LO_Color *color)
  3599. {
  3600.     lo_FontStack *fptr;
  3601.     LO_TextAttr *attr;
  3602.  
  3603.     if ((state->top_state->body_attr & BODY_ATTR_TEXT) != 0)
  3604.         return;
  3605.     
  3606.     state->top_state->body_attr |= BODY_ATTR_TEXT;
  3607.  
  3608.     state->text_fg = *color;
  3609.     fptr = state->font_stack;
  3610.  
  3611.     /* 
  3612.      * If we're inside a layer, then we want this color change
  3613.      * to only affect text in the layer. So, we push a font
  3614.      * (a copy of the top of the stack) onto the font stack
  3615.      * and change its color. This font will be popped in the
  3616.      * closing of the layer.
  3617.      */
  3618.     if (lo_InsideLayer(state)) {
  3619.         LO_TextAttr tmp_attr;
  3620.         
  3621.         if (fptr)
  3622.             lo_CopyTextAttr(fptr->text_attr, &tmp_attr);
  3623.         else
  3624.             lo_SetDefaultFontAttr(state, &tmp_attr, context);
  3625.  
  3626.         tmp_attr.fg.red =   STATE_DEFAULT_FG_RED(state);
  3627.         tmp_attr.fg.green = STATE_DEFAULT_FG_GREEN(state);
  3628.         tmp_attr.fg.blue =  STATE_DEFAULT_FG_BLUE(state);
  3629.         attr = lo_FetchTextAttr(state, &tmp_attr);
  3630.         lo_PushFont(state, P_BODY, attr);
  3631.     }
  3632.     else if (fptr != NULL)
  3633.     {
  3634.         attr = fptr->text_attr;
  3635.         attr->fg.red =   STATE_DEFAULT_FG_RED(state);
  3636.         attr->fg.green = STATE_DEFAULT_FG_GREEN(state);
  3637.         attr->fg.blue =  STATE_DEFAULT_FG_BLUE(state);
  3638.     }
  3639. }
  3640.  
  3641.  
  3642. /*
  3643.  * Something has changed (probably the default FG and BG colors)
  3644.  * since the font stack was initialized in this state.
  3645.  * We need to reinitialie it to the new default font.
  3646.  * WARNING: This function depends on the assumption that no
  3647.  *        elements have yet been placed in this state.
  3648.  */
  3649. void
  3650. lo_ResetFontStack(MWContext *context, lo_DocState *state)
  3651. {
  3652.     if (state->font_stack != NULL)
  3653.         {
  3654.                 lo_FontStack *fstack;
  3655.                 lo_FontStack *fptr;
  3656.  
  3657.                 fptr = state->font_stack;
  3658.                 while (fptr != NULL)
  3659.                 {
  3660.                         fstack = fptr;
  3661.                         fptr = fptr->next;
  3662.                         XP_DELETE(fstack);
  3663.                 }
  3664.                 state->font_stack = NULL;
  3665.         }
  3666.     state->font_stack = lo_DefaultFont(state, context);
  3667. }
  3668.  
  3669.  
  3670. /*************************************
  3671.  * Function: lo_PushFont
  3672.  *
  3673.  * Description: Push the text attribute information for a new
  3674.  *    font onto the font stack.  Also save the type of the
  3675.  *    tag that caused the change.
  3676.  *
  3677.  * Params: Document state, tag type, and the text attribute
  3678.  *    structure for the new font.
  3679.  *
  3680.  * Returns: Nothing
  3681.  *************************************/
  3682. void
  3683. lo_PushFont(lo_DocState *state, intn tag_type, LO_TextAttr *attr)
  3684. {
  3685.     lo_FontStack *fptr;
  3686.  
  3687.     fptr = XP_NEW(lo_FontStack);
  3688.     if (fptr == NULL)
  3689.     {
  3690.         return;
  3691.     }
  3692.     fptr->tag_type = tag_type;
  3693.     fptr->text_attr = attr;
  3694.     fptr->next = state->font_stack;
  3695.     state->font_stack = fptr;;
  3696. }
  3697.  
  3698.  
  3699. /*************************************
  3700.  * Function: lo_PopFontStack
  3701.  *
  3702.  * Description: This function pops the next font
  3703.  *    off the font stack, and return the text attribute of the
  3704.  *    previous font.
  3705.  *    The last font on the font stack cannot be popped off.
  3706.  *
  3707.  * Params: Document state, and the tag type that caused the change.
  3708.  *
  3709.  * Returns: The LO_TextAttr structure of the font just passed.
  3710.  *************************************/
  3711. PRIVATE
  3712. LO_TextAttr *
  3713. lo_PopFontStack(lo_DocState *state, intn tag_type)
  3714. {
  3715.     LO_TextAttr *attr;
  3716.     lo_FontStack *fptr;
  3717.  
  3718.     if (state->font_stack->next == NULL)
  3719.     {
  3720. #ifdef LOCAL_DEBUG
  3721. XP_TRACE(("Popped too many fonts!\n"));
  3722. #endif /* LOCAL_DEBUG */
  3723.         return(NULL);
  3724.     }
  3725.  
  3726.     fptr = state->font_stack;
  3727.     attr = fptr->text_attr;
  3728.     if (fptr->tag_type != tag_type)
  3729.     {
  3730. #ifdef LOCAL_DEBUG
  3731. XP_TRACE(("Warning:  Font popped by different TAG than pushed it %d != %d\n", fptr->tag_type, tag_type));
  3732. #endif /* LOCAL_DEBUG */
  3733.     }
  3734.     state->font_stack = fptr->next;
  3735.     XP_DELETE(fptr);
  3736.  
  3737.     return(attr);
  3738. }
  3739.  
  3740.  
  3741. LO_TextAttr *
  3742. lo_PopFont(lo_DocState *state, intn tag_type)
  3743. {
  3744.     LO_TextAttr *attr;
  3745.     lo_FontStack *fptr;
  3746.  
  3747.     /*
  3748.      * This should never happen, but we are patching a
  3749.      * more serious problem that causes us to be called
  3750.      * here after the font stack has been freed.
  3751.      */
  3752.     if ((state->font_stack == NULL)||(state->font_stack->next == NULL))
  3753.     {
  3754. #ifdef LOCAL_DEBUG
  3755. XP_TRACE(("Popped too many fonts!\n"));
  3756. #endif /* LOCAL_DEBUG */
  3757.         return(NULL);
  3758.     }
  3759.  
  3760.     fptr = state->font_stack;
  3761.     attr = NULL;
  3762.  
  3763.     if (fptr->tag_type != P_ANCHOR)
  3764.     {
  3765.         attr = fptr->text_attr;
  3766.         if (fptr->tag_type != tag_type)
  3767.         {
  3768. #ifdef LOCAL_DEBUG
  3769. XP_TRACE(("Warning:  Font popped by different TAG than pushed it %d != %d\n", fptr->tag_type, tag_type));
  3770. #endif /* LOCAL_DEBUG */
  3771.         }
  3772.         state->font_stack = fptr->next;
  3773.         XP_DELETE(fptr);
  3774.     }
  3775.     else
  3776.     {
  3777.         while ((fptr->next != NULL)&&(fptr->next->tag_type == P_ANCHOR))
  3778.         {
  3779.             fptr = fptr->next;
  3780.         }
  3781.         if (fptr->next->next != NULL)
  3782.         {
  3783.             lo_FontStack *f_tmp;
  3784.  
  3785.             f_tmp = fptr->next;
  3786.             fptr->next = fptr->next->next;
  3787.             attr = f_tmp->text_attr;
  3788.             XP_DELETE(f_tmp);
  3789.         }
  3790.     }
  3791.  
  3792.     return(attr);
  3793. }
  3794.  
  3795.  
  3796. void
  3797. lo_PopAllAnchors(lo_DocState *state)
  3798. {
  3799.     lo_FontStack *fptr;
  3800.  
  3801.     if (state->font_stack->next == NULL)
  3802.     {
  3803. #ifdef LOCAL_DEBUG
  3804. XP_TRACE(("Popped too many fonts!\n"));
  3805. #endif /* LOCAL_DEBUG */
  3806.         return;
  3807.     }
  3808.  
  3809.     /*
  3810.      * Remove all anchors on top of the font stack
  3811.      */
  3812.     fptr = state->font_stack;
  3813.     while ((fptr->tag_type == P_ANCHOR)&&(fptr->next != NULL))
  3814.     {
  3815.         lo_FontStack *f_tmp;
  3816.  
  3817.         f_tmp = fptr;
  3818.         fptr = fptr->next;
  3819.         XP_DELETE(f_tmp);
  3820.     }
  3821.     state->font_stack = fptr;
  3822.  
  3823.     /*
  3824.      * Remove all anchors buried in the stack
  3825.      */
  3826.     while (fptr->next != NULL)
  3827.     {
  3828.         /*
  3829.          * Reset spurrious anchor color text entries
  3830.          */
  3831.         if ((fptr->text_attr != NULL)&&
  3832.             (fptr->text_attr->attrmask & LO_ATTR_ANCHOR))
  3833.         {
  3834.             LO_TextAttr tmp_attr;
  3835.  
  3836.             lo_CopyTextAttr(fptr->text_attr, &tmp_attr);
  3837.             tmp_attr.attrmask =
  3838.                 tmp_attr.attrmask & (~LO_ATTR_ANCHOR);
  3839.             tmp_attr.fg.red = STATE_DEFAULT_FG_RED(state);
  3840.             tmp_attr.fg.green = STATE_DEFAULT_FG_GREEN(state);
  3841.             tmp_attr.fg.blue = STATE_DEFAULT_FG_BLUE(state);
  3842.             tmp_attr.bg.red = STATE_DEFAULT_BG_RED(state);
  3843.             tmp_attr.bg.green = STATE_DEFAULT_BG_GREEN(state);
  3844.             tmp_attr.bg.blue = STATE_DEFAULT_BG_BLUE(state);
  3845.             fptr->text_attr = lo_FetchTextAttr(state, &tmp_attr);
  3846.         }
  3847.  
  3848.         if (fptr->next->tag_type == P_ANCHOR)
  3849.         {
  3850.             lo_FontStack *f_tmp;
  3851.  
  3852.             f_tmp = fptr->next;
  3853.             fptr->next = fptr->next->next;
  3854.             XP_DELETE(f_tmp);
  3855.         }
  3856.         else
  3857.         {
  3858.             fptr = fptr->next;
  3859.         }
  3860.     }
  3861. }
  3862.  
  3863. void
  3864. lo_FormatBullet(MWContext *context, lo_DocState *state,
  3865.                 LO_BulletStruct *bullet,
  3866.                 int32 *line_height,
  3867.                 int32 *baseline)
  3868. {
  3869.     LO_TextStruct tmp_text;
  3870.     LO_TextInfo text_info;
  3871.     LO_TextAttr *tptr;
  3872.     PA_Block buff;
  3873.     char *str;
  3874.  
  3875. #define MIN_BULLET_SIZE 5
  3876.  
  3877.     bullet->ele_id = NEXT_ELEMENT;
  3878.  
  3879.     /* bullet = (LO_BulletStruct *)lo_NewElement(context, state, LO_BULLET, NULL, 0); */
  3880.     if (bullet == NULL)
  3881.     {
  3882. #ifdef DEBUG
  3883.         assert (state->top_state->out_of_memory);
  3884. #endif
  3885.         return;
  3886.     }
  3887. /*
  3888.     if(state->font_stack)
  3889.     {
  3890.         lo_CopyTextAttr(state->font_stack->text_attr, &tmp_attr);
  3891.     }
  3892.     else
  3893.     {
  3894.         lo_SetDefaultFontAttr(state, &tmp_attr, context);
  3895.     }
  3896. */
  3897.     tptr = bullet->text_attr;
  3898.  
  3899.     memset (&tmp_text, 0, sizeof (tmp_text));
  3900.     buff = PA_ALLOC(1);
  3901.     if (buff == NULL)
  3902.     {
  3903.         state->top_state->out_of_memory = TRUE;
  3904.         return;
  3905.     }
  3906.     PA_LOCK(str, char *, buff);
  3907.     str[0] = ' ';
  3908.     PA_UNLOCK(buff);
  3909.     tmp_text.text = buff;
  3910.     tmp_text.text_len = 1;
  3911.     tmp_text.text_attr = tptr;
  3912.     FE_GetTextInfo(context, &tmp_text, &text_info);
  3913.     PA_FREE(buff);
  3914. /*
  3915.     bullet->bullet_size = (text_info.ascent + text_info.descent) / 2;
  3916.     bullet->text_attr = tptr;
  3917. */
  3918.     /* contain the bullet size so that it doesn't extend off the
  3919.      * left side of the page since we are using a negative offset
  3920.      * to place the bullet
  3921.      *
  3922.      * also subtract one to avoid the header code at the bottom
  3923.      * from triggering and messing up the alignment
  3924.      */
  3925.     if(bullet->bullet_size*2 >= state->x-state->win_left)
  3926.       bullet->bullet_size = ((state->x-state->win_left)/2)-1;
  3927.  
  3928.     /* enforce a minumum bullet size */
  3929.     if(bullet->bullet_size < 1)
  3930.       bullet->bullet_size = MIN_BULLET_SIZE;
  3931.  
  3932.     bullet->x = state->x - (2 * bullet->bullet_size);
  3933.     if (bullet->x < state->win_left)
  3934.     {
  3935.         bullet->x = state->win_left;
  3936.     }
  3937.     bullet->x_offset = 0;
  3938.     bullet->y = state->y;
  3939.     bullet->y_offset =
  3940.         (text_info.ascent + text_info.descent - bullet->bullet_size) / 2;
  3941.     bullet->width = bullet->bullet_size;
  3942.     bullet->height = bullet->bullet_size;
  3943.  
  3944.     *line_height = text_info.ascent + text_info.descent;
  3945.     *baseline = text_info.ascent;
  3946. }
  3947.  
  3948. void
  3949. lo_UpdateStateAfterBullet(MWContext * context, lo_DocState *state,
  3950.                           LO_BulletStruct *bullet,
  3951.                           int32 line_height,
  3952.                           int32 baseline)
  3953. {
  3954.     state->baseline = baseline;
  3955.     state->line_height = line_height;
  3956.  
  3957.     /*
  3958.      * Clean up state
  3959.      */
  3960. /*
  3961.  * Supporting old mistakes made in some other browsers.
  3962.  * I will put the "correct code" here, but comment it out, since
  3963.  * some other browsers allowed headers inside lists, so we should to, sigh.
  3964.     state->linefeed_state = 0;
  3965.  */
  3966.     state->at_begin_line = TRUE;
  3967.     state->cur_ele_type = LO_BULLET;
  3968.     if (bullet->x == state->win_left)
  3969.     {
  3970.         state->x += (bullet->x_offset + (2 * bullet->width));
  3971.     }
  3972.  
  3973.     /*
  3974.      * Make at_begin_line be accurate
  3975.      * so we can detect the header
  3976.      * linefeed state deception later.
  3977.      */
  3978.     state->at_begin_line = FALSE;
  3979.     
  3980.     /*
  3981.      * After much soul-searching (and brow-beating
  3982.      * by Jamie, I've agreed that really whitespace
  3983.      * should be compressed out at the start of a
  3984.      * list item.  They can always add non-breaking
  3985.      * spaces if they want them.
  3986.      * Setting trailing space true means it won't
  3987.      * let the users add whitespace because it
  3988.      * thinks there already is some.
  3989.      */
  3990.     state->trailing_space = TRUE;
  3991. }
  3992.  
  3993. void
  3994. lo_PlaceBullet(MWContext *context, lo_DocState *state)
  3995. {
  3996.     LO_BulletStruct *bullet = NULL;
  3997.     int32 line_height, baseline;
  3998.     LO_TextAttr tmp_attr;
  3999.     LO_TextStruct tmp_text;
  4000.     LO_TextInfo text_info;
  4001.     LO_TextAttr *tptr;
  4002.     PA_Block buff;
  4003.     char *str;
  4004.  
  4005.     bullet = (LO_BulletStruct *)lo_NewElement(context, state, LO_BULLET, NULL, 0);
  4006.     if (bullet == NULL)
  4007.     {
  4008. #ifdef DEBUG
  4009.         assert (state->top_state->out_of_memory);
  4010. #endif
  4011.         return;
  4012.     }
  4013.  
  4014.     bullet->type = LO_BULLET;
  4015.     bullet->next = NULL;
  4016.     bullet->prev = NULL;
  4017.  
  4018.     bullet->FE_Data = NULL;
  4019.  
  4020.     bullet->level = state->list_stack->level;
  4021.  
  4022.     bullet->bullet_type = state->list_stack->bullet_type;
  4023.  
  4024.     /* try and get a bullet type from style sheets */
  4025.     if(state && state->top_state && state->top_state->style_stack)
  4026.     {
  4027.         StyleStruct *style_struct = STYLESTACK_GetStyleByIndex(state->top_state->style_stack, 0);
  4028.  
  4029.         if(style_struct)
  4030.         {
  4031.             char *list_style_prop = STYLESTRUCT_GetString(style_struct,
  4032.                                                           LIST_STYLE_TYPE_STYLE);
  4033.             if(list_style_prop)
  4034.             {
  4035.                 bullet->bullet_type = lo_list_bullet_type(list_style_prop, P_UNUM_LIST);
  4036.                 XP_FREE(list_style_prop);
  4037.             }
  4038.         }
  4039.     }
  4040.  
  4041.     bullet->ele_attrmask = 0;
  4042.  
  4043.     bullet->sel_start = -1;
  4044.     bullet->sel_end = -1;
  4045.  
  4046.     if(state->font_stack)
  4047.     {
  4048.         lo_CopyTextAttr(state->font_stack->text_attr, &tmp_attr);
  4049.     }
  4050.     else
  4051.     {
  4052.         lo_SetDefaultFontAttr(state, &tmp_attr, context);
  4053.     }
  4054.     tptr = lo_FetchTextAttr(state, &tmp_attr);
  4055.  
  4056.     memset (&tmp_text, 0, sizeof (tmp_text));
  4057.     buff = PA_ALLOC(1);
  4058.     if (buff == NULL)
  4059.     {
  4060.         state->top_state->out_of_memory = TRUE;
  4061.         return;
  4062.     }
  4063.     PA_LOCK(str, char *, buff);
  4064.     str[0] = ' ';
  4065.     PA_UNLOCK(buff);
  4066.     tmp_text.text = buff;
  4067.     tmp_text.text_len = 1;
  4068.     tmp_text.text_attr = tptr;
  4069.     FE_GetTextInfo(context, &tmp_text, &text_info);
  4070.     PA_FREE(buff);
  4071.  
  4072.     bullet->bullet_size = (text_info.ascent + text_info.descent) / 2;
  4073.     bullet->text_attr = tptr;
  4074.  
  4075.     lo_FormatBullet(context, state, bullet, &line_height, &baseline);
  4076.  
  4077.     lo_AppendToLineList(context, state, (LO_Element *)bullet, 0);
  4078.  
  4079.     lo_UpdateStateAfterBullet(context, state, bullet,
  4080.                               line_height,
  4081.                               baseline);
  4082. }
  4083.  
  4084.  
  4085. void
  4086. lo_FormatBulletStr(MWContext *context, lo_DocState *state,
  4087.                    LO_TextStruct *bullet_text,
  4088.                    int32 *line_height,
  4089.                    int32 *baseline)
  4090. {
  4091.     LO_TextInfo text_info;
  4092.  
  4093.     FE_GetTextInfo(context, bullet_text, &text_info);
  4094.  
  4095.     bullet_text->x = state->x - (bullet_text->height / 2) -
  4096.         bullet_text->width;
  4097.     if (bullet_text->x < state->win_left)
  4098.     {
  4099.         bullet_text->x = state->win_left;
  4100.     }
  4101.     bullet_text->x_offset = 0;
  4102.     bullet_text->y = state->y;
  4103.     bullet_text->y_offset = 0;
  4104.  
  4105.     state->baseline = text_info.ascent;
  4106.     state->line_height = (intn) bullet_text->height;
  4107.  
  4108.     *baseline = text_info.ascent;
  4109.     *line_height = bullet_text->height;
  4110. }
  4111.  
  4112. void
  4113. lo_UpdateStateAfterBulletStr(MWContext *context,
  4114.                              lo_DocState *state,
  4115.                              LO_TextStruct *bullet_text,
  4116.                              int32 line_height,
  4117.                              int32 baseline)
  4118. {
  4119.     state->baseline = baseline;
  4120.     state->line_height = line_height;
  4121.  
  4122.     /*
  4123.      * Clean up state
  4124.      */
  4125. /*
  4126.  * Supporting old mistakes made in some other browsers.
  4127.  * I will put the "correct code" here, but comment it out, since
  4128.  * some other browsers allowed headers inside lists, so we should to, sigh.
  4129.     state->linefeed_state = 0;
  4130.     state->at_begin_line = FALSE;
  4131.  */
  4132.     state->at_begin_line = TRUE;
  4133.     state->cur_ele_type = LO_TEXT;
  4134. }
  4135.  
  4136. void
  4137. lo_PlaceBulletStr(MWContext *context, lo_DocState *state)
  4138. {
  4139.     intn len;
  4140.     char str2[22];
  4141.     char *str;
  4142.     char *str3;
  4143.     PA_Block buff;
  4144.     LO_TextStruct *bullet_text = NULL;
  4145.     LO_TextInfo text_info;
  4146.     int bullet_type;
  4147.     int32 line_height, baseline;
  4148.  
  4149.     bullet_text = (LO_TextStruct *)lo_NewElement(context, state, LO_TEXT, NULL, 0);
  4150.     if (bullet_text == NULL)
  4151.     {
  4152. #ifdef DEBUG
  4153.         assert (state->top_state->out_of_memory);
  4154. #endif
  4155.         return;
  4156.     }
  4157.  
  4158.     bullet_type = state->list_stack->bullet_type;
  4159.  
  4160.     /* try and get a bullet type from style sheets */
  4161.     if(state && state->top_state && state->top_state->style_stack)
  4162.     {
  4163.         StyleStruct *style_struct = STYLESTACK_GetStyleByIndex(state->top_state->style_stack, 0);
  4164.  
  4165.         if(style_struct)
  4166.         {
  4167.             char *list_style_prop = STYLESTRUCT_GetString(style_struct,
  4168.                                                           LIST_STYLE_TYPE_STYLE);
  4169.             if(list_style_prop)
  4170.             {
  4171.                 bullet_type = lo_list_bullet_type(list_style_prop, P_NUM_LIST);
  4172.                 XP_FREE(list_style_prop);
  4173.             }
  4174.         }
  4175.     }
  4176.  
  4177.  
  4178.     if( EDT_IS_EDITOR( context )) 
  4179.     {
  4180.         switch( bullet_type ){
  4181.         case BULLET_ALPHA_L:
  4182.             str = "A";
  4183.             break;
  4184.         case BULLET_ALPHA_S:
  4185.             str = "a";
  4186.             break;
  4187.         case BULLET_NUM_S_ROMAN:
  4188.             str = "x";
  4189.             break;
  4190.         case BULLET_NUM_L_ROMAN:
  4191.             str = "X";
  4192.             break;
  4193.         default:
  4194.             str = "#";
  4195.             break;
  4196.         }
  4197.         len = XP_STRLEN(str);
  4198.         buff = PA_ALLOC(len + 1);
  4199.         if (buff != NULL)
  4200.         {
  4201.             PA_LOCK(str3, char *, buff);
  4202.             XP_STRCPY(str3, str);
  4203.             PA_UNLOCK(buff);
  4204.         }
  4205.     }
  4206.     else {
  4207.         if (bullet_type == BULLET_ALPHA_S)
  4208.         {
  4209.             buff = lo_ValueToAlpha(state->list_stack->value, FALSE, &len);
  4210.         }
  4211.         else if (bullet_type == BULLET_ALPHA_L)
  4212.         {
  4213.             buff = lo_ValueToAlpha(state->list_stack->value, TRUE, &len);
  4214.         }
  4215.         else if (bullet_type == BULLET_NUM_S_ROMAN)
  4216.         {
  4217.             buff = lo_ValueToRoman(state->list_stack->value, FALSE, &len);
  4218.         }
  4219.         else if (bullet_type == BULLET_NUM_L_ROMAN)
  4220.         {
  4221.             buff = lo_ValueToRoman(state->list_stack->value, TRUE, &len);
  4222.         }
  4223.         else
  4224.         {
  4225.             XP_SPRINTF(str2, "%d.", (intn)state->list_stack->value);
  4226.             len = XP_STRLEN(str2);
  4227.             buff = PA_ALLOC(len + 1);
  4228.             if (buff != NULL)
  4229.             {
  4230.                 PA_LOCK(str, char *, buff);
  4231.                 XP_STRCPY(str, str2);
  4232.                 PA_UNLOCK(buff);
  4233.             }
  4234.             else
  4235.             {
  4236.                 state->top_state->out_of_memory = TRUE;
  4237.             }
  4238.         }
  4239.     }
  4240.  
  4241.     if (buff == NULL)
  4242.     {
  4243.         return;
  4244.     }
  4245.  
  4246.     bullet_text->bullet_type = bullet_type;
  4247.     bullet_text->text = buff;
  4248.     bullet_text->text_len = len;
  4249.     bullet_text->text_attr = state->font_stack->text_attr;
  4250.     FE_GetTextInfo(context, bullet_text, &text_info);
  4251.     bullet_text->width = lo_correct_text_element_width(&text_info);
  4252.     bullet_text->height = text_info.ascent + text_info.descent;
  4253.  
  4254.     bullet_text->type = LO_TEXT;
  4255.     bullet_text->ele_id = NEXT_ELEMENT;
  4256.  
  4257.     lo_FormatBulletStr(context, state, bullet_text, &line_height, &baseline);
  4258.  
  4259.     bullet_text->anchor_href = state->current_anchor;
  4260.  
  4261.     bullet_text->ele_attrmask = 0;
  4262.     if (state->breakable != FALSE)
  4263.     {
  4264.         bullet_text->ele_attrmask |= LO_ELE_BREAKABLE;
  4265.     }
  4266.  
  4267.     bullet_text->sel_start = -1;
  4268.     bullet_text->sel_end = -1;
  4269.  
  4270.     bullet_text->next = NULL;
  4271.     bullet_text->prev = NULL;
  4272.  
  4273.     bullet_text->FE_Data = NULL;
  4274.  
  4275.     lo_AppendToLineList(context, state, (LO_Element *)bullet_text, 0);
  4276.  
  4277.     state->baseline = text_info.ascent;
  4278.     state->line_height = (intn) bullet_text->height;
  4279.  
  4280.     lo_UpdateStateAfterBulletStr(context, state, bullet_text, line_height, baseline);
  4281. }
  4282.  
  4283.  
  4284. static LO_Element *
  4285. lo_make_quote_text(MWContext *context, lo_DocState *state, int32 margin)
  4286. {
  4287.     PA_Block buff;
  4288.     char *str;
  4289.     LO_TextStruct *quote_text;
  4290.     LO_TextAttr tmp_attr;
  4291.     LO_TextInfo text_info;
  4292.  
  4293.     quote_text = (LO_TextStruct *)lo_NewElement(context, state, LO_TEXT,
  4294.                             NULL, 0);
  4295.     if (quote_text == NULL)
  4296.     {
  4297. #ifdef DEBUG
  4298.         assert (state->top_state->out_of_memory);
  4299. #endif
  4300.         return(NULL);
  4301.     }
  4302.  
  4303.     buff = PA_ALLOC(2);
  4304.     if (buff == NULL)
  4305.     {
  4306.         state->top_state->out_of_memory = TRUE;
  4307.         return(NULL);
  4308.     }
  4309.     PA_LOCK(str, char *, buff);
  4310.     str[0] = '>';
  4311.     str[1] = '\0';
  4312.     PA_UNLOCK(buff);
  4313.  
  4314.     quote_text->text = buff;
  4315.     quote_text->text_len = 1;
  4316.     /*
  4317.      * Fill in default fixed font information.
  4318.      */
  4319.     lo_SetDefaultFontAttr(state, &tmp_attr, context);
  4320.     tmp_attr.fontmask |= LO_FONT_FIXED;
  4321.     quote_text->text_attr = lo_FetchTextAttr(state, &tmp_attr);
  4322.     FE_GetTextInfo(context, quote_text, &text_info);
  4323.     quote_text->width = lo_correct_text_element_width(&text_info);
  4324.     quote_text->height = text_info.ascent + text_info.descent;
  4325.  
  4326.     quote_text->type = LO_TEXT;
  4327.     quote_text->ele_id = 0;
  4328.     quote_text->x = margin;
  4329.     if (quote_text->x < state->win_left)
  4330.     {
  4331.         quote_text->x = state->win_left;
  4332.     }
  4333.     quote_text->x_offset = 0;
  4334.     quote_text->y = state->y;
  4335.     quote_text->y_offset = 0;
  4336.  
  4337.     quote_text->anchor_href = state->current_anchor;
  4338.  
  4339.     quote_text->ele_attrmask = 0;
  4340.     if (state->breakable != FALSE)
  4341.     {
  4342.         quote_text->ele_attrmask |= LO_ELE_BREAKABLE;
  4343.     }
  4344.  
  4345.     quote_text->bullet_type = BULLET_MQUOTE;
  4346.  
  4347.     quote_text->sel_start = -1;
  4348.     quote_text->sel_end = -1;
  4349.  
  4350.     quote_text->next = NULL;
  4351.     quote_text->prev = NULL;
  4352.  
  4353.     quote_text->FE_Data = NULL;
  4354.  
  4355.     state->baseline = text_info.ascent;
  4356.  
  4357.     return((LO_Element *)quote_text);
  4358. }
  4359.  
  4360.  
  4361. static LO_Element *
  4362. lo_make_quote_bullet(MWContext *context, lo_DocState *state, int32 margin)
  4363. {
  4364.     PA_Block buff;
  4365.     char *str;
  4366.     LO_BulletStruct *bullet = NULL;
  4367.     LO_TextAttr tmp_attr;
  4368.     LO_TextInfo text_info;
  4369.     LO_TextStruct tmp_text;
  4370.     LO_TextAttr *tptr;
  4371.     int32 bullet_size;
  4372.  
  4373.     bullet = (LO_BulletStruct *)lo_NewElement(context, state, LO_BULLET, NULL, 0);
  4374.     if (bullet == NULL)
  4375.     {
  4376. #ifdef DEBUG
  4377.         assert (state->top_state->out_of_memory);
  4378. #endif
  4379.         return(NULL);
  4380.     }
  4381.  
  4382.     lo_SetDefaultFontAttr(state, &tmp_attr, context);
  4383.     tmp_attr.fg.red = 0;
  4384.     tmp_attr.fg.green = 0;
  4385.     tmp_attr.fg.blue = 255;
  4386.     tptr = lo_FetchTextAttr(state, &tmp_attr);
  4387.  
  4388.     memset (&tmp_text, 0, sizeof (tmp_text));
  4389.     buff = PA_ALLOC(1);
  4390.     if (buff == NULL)
  4391.     {
  4392.         state->top_state->out_of_memory = TRUE;
  4393.         return(NULL);
  4394.     }
  4395.     PA_LOCK(str, char *, buff);
  4396.     str[0] = ' ';
  4397.     PA_UNLOCK(buff);
  4398.     tmp_text.text = buff;
  4399.     tmp_text.text_len = 1;
  4400.     tmp_text.text_attr = tptr;
  4401.     FE_GetTextInfo(context, &tmp_text, &text_info);
  4402.     PA_FREE(buff);
  4403.  
  4404.     bullet_size = text_info.ascent + text_info.descent;
  4405.  
  4406.     if (bullet_size < 5)
  4407.     {
  4408.         bullet_size = 5;
  4409.     }
  4410.  
  4411.     bullet->type = LO_BULLET;
  4412.     bullet->ele_id = 0;
  4413.     bullet->x = margin;
  4414.     if (bullet->x < state->win_left)
  4415.     {
  4416.         bullet->x = state->win_left;
  4417.     }
  4418.     bullet->x_offset = 0;
  4419.     bullet->y = state->y;
  4420.     bullet->y_offset = 0;
  4421.     bullet->width = 5;
  4422.     bullet->height = bullet_size;
  4423.     bullet->next = NULL;
  4424.     bullet->prev = NULL;
  4425.  
  4426.     bullet->FE_Data = NULL;
  4427.  
  4428.     bullet->level = state->list_stack->level;
  4429.     bullet->bullet_type = BULLET_MQUOTE;
  4430.     bullet->text_attr = tptr;
  4431.  
  4432.     bullet->ele_attrmask = 0;
  4433.  
  4434.     bullet->sel_start = -1;
  4435.     bullet->sel_end = -1;
  4436.  
  4437.     state->baseline = text_info.ascent;
  4438.  
  4439.     return((LO_Element *)bullet);
  4440. }
  4441.  
  4442.  
  4443. static void
  4444. lo_insert_quote_characters(MWContext *context, lo_DocState *state)
  4445. {
  4446.     LO_Element *eptr;
  4447.     LO_Element *elist;
  4448.     lo_ListStack *lptr;
  4449.  
  4450.     elist = NULL;
  4451.     lptr = state->list_stack;
  4452.     while (lptr != NULL)
  4453.     {
  4454.         eptr = NULL;
  4455.         if (lptr->quote_type == QUOTE_JWZ)
  4456.         {
  4457.             eptr = lo_make_quote_text(context, state,
  4458.                     lptr->mquote_x);
  4459.         }
  4460.         else if (lptr->quote_type == QUOTE_CITE)
  4461.         {
  4462.             eptr = lo_make_quote_bullet(context, state,
  4463.                     lptr->mquote_x);
  4464.         }
  4465.         if (eptr != NULL)
  4466.         {
  4467.             eptr->lo_any.next = elist;
  4468.             elist = eptr;
  4469.         }
  4470.         lptr = lptr->next;
  4471.     }
  4472.  
  4473.     eptr = elist;
  4474.     while (eptr != NULL)
  4475.     {
  4476.         LO_Element *tmp_ele;
  4477.  
  4478.         tmp_ele = eptr;
  4479.         eptr = eptr->lo_any.next;
  4480.         tmp_ele->lo_any.next = NULL;
  4481.  
  4482.         tmp_ele->lo_any.ele_id = NEXT_ELEMENT;
  4483.         lo_AppendToLineList(context, state, tmp_ele, 0);
  4484.         state->line_height = (intn)tmp_ele->lo_any.height;
  4485.         state->at_begin_line = TRUE;
  4486.         state->cur_ele_type = LO_TEXT;
  4487.     }
  4488. }
  4489.  
  4490.  
  4491. void
  4492. lo_PlaceQuoteMarker(MWContext *context, lo_DocState *state, lo_ListStack *lptr)
  4493. {
  4494.     LO_Element *eptr;
  4495.  
  4496.     if (lptr != NULL)
  4497.     {
  4498.         eptr = NULL;
  4499.         if (lptr->quote_type == QUOTE_JWZ)
  4500.         {
  4501.             eptr = lo_make_quote_text(context, state,
  4502.                     lptr->mquote_x);
  4503.         }
  4504.         else if (lptr->quote_type == QUOTE_CITE)
  4505.         {
  4506.             eptr = lo_make_quote_bullet(context, state,
  4507.                     lptr->mquote_x);
  4508.         }
  4509.         if (eptr != NULL)
  4510.         {
  4511.             eptr->lo_any.ele_id = NEXT_ELEMENT;
  4512.             lo_AppendToLineList(context, state, eptr, 0);
  4513.             state->line_height = (intn)eptr->lo_any.height;
  4514.             state->at_begin_line = TRUE;
  4515.             if (lptr->quote_type == QUOTE_JWZ)
  4516.             {
  4517.                 state->cur_ele_type = LO_TEXT;
  4518.             }
  4519.             else if (lptr->quote_type == QUOTE_CITE)
  4520.             {
  4521.                 state->cur_ele_type = LO_BULLET;
  4522.             }
  4523.         }
  4524.     }
  4525. }
  4526.  
  4527.  
  4528. void lo_UpdateStateAfterLineBreak( MWContext *context, lo_DocState *state, Bool updateFE )
  4529. {
  4530.     int32 line_width;
  4531.  
  4532.     /*
  4533.      * if this linefeed has a zero height, make it the height
  4534.      * of the current font.
  4535.      */
  4536.     if (state->line_height == 0)
  4537.     {
  4538.         state->line_height = state->text_info.ascent +
  4539.             state->text_info.descent;
  4540.         if ((state->line_height <= 0)&&(state->font_stack != NULL)&&
  4541.             (state->font_stack->text_attr != NULL))
  4542.         {
  4543.             lo_fillin_text_info(context, state);
  4544.             state->line_height = state->text_info.ascent +
  4545.                 state->text_info.descent;
  4546.         }
  4547.         /*
  4548.          * This should never happen, but we have it
  4549.          * covered just in case it does :-)
  4550.          */
  4551.         if (state->line_height <= 0)
  4552.         {
  4553.             state->line_height = state->default_line_height;
  4554.         }
  4555.     }
  4556.  
  4557.     if (state->end_last_line != NULL)
  4558.     {
  4559.         line_width = state->end_last_line->lo_any.x + state->win_right;
  4560.     }
  4561.     else
  4562.     {
  4563.         line_width = state->x + state->win_right;
  4564.     }
  4565.  
  4566.     if (line_width > state->max_width)
  4567.     {
  4568.         state->max_width = line_width;
  4569.     }
  4570.  
  4571.     /* if LineHeightStack exists use it to offset the new Y value */
  4572.     if(state->cur_ele_type != LO_SUBDOC && state->line_height_stack)
  4573.     {
  4574.         state->y += state->line_height_stack->height;
  4575.     }
  4576.     else
  4577.     {
  4578.         state->y = state->y + state->line_height;
  4579.     }
  4580.  
  4581.     state->x = state->left_margin;
  4582.     state->width = 0;
  4583.     state->at_begin_line = TRUE;
  4584.     state->trailing_space = FALSE;
  4585.     state->line_height = 0;
  4586.     state->break_holder = state->x;
  4587.  
  4588.     state->linefeed_state++;
  4589.     if (state->linefeed_state > 2)
  4590.     {
  4591.         state->linefeed_state = 2;
  4592.     }
  4593.  
  4594.     /*
  4595.      * Reset the left and right margins
  4596.      */
  4597.     lo_FindLineMargins(context, state, updateFE);
  4598.     state->x = state->left_margin;
  4599.  
  4600. }
  4601.  
  4602. void lo_UpdateFEProgressBar( MWContext *context, lo_DocState *state )
  4603. {
  4604.     if (state->is_a_subdoc == SUBDOC_NOT)
  4605.     {
  4606.         int32 percent;
  4607.  
  4608.         if (state->top_state->total_bytes < 1)
  4609.         {
  4610.             percent = -1;
  4611.         }
  4612.         else
  4613.         {
  4614.             percent = (100 * state->top_state->layout_bytes) /
  4615.                 state->top_state->total_bytes;
  4616.             if (percent > 100)
  4617.             {
  4618.                 percent = 100;
  4619.             }
  4620.         }
  4621.         if ((percent == 100)||(percent < 0)||
  4622.             (percent > (state->top_state->layout_percent + 1)))
  4623.         {
  4624.             if(!state->top_state->is_binary)
  4625.                 FE_SetProgressBarPercent(context, percent);
  4626.             state->top_state->layout_percent = (intn)percent;
  4627.         }
  4628.     }
  4629. }
  4630.  
  4631. void lo_UpdateFEDocSize( MWContext *context, lo_DocState *state )
  4632. {    
  4633.     /*
  4634.      * Tell the front end how big the document is right now.
  4635.      * Only do this for the top level document.
  4636.      */
  4637.     if ((state->is_a_subdoc == SUBDOC_NOT)
  4638.         &&(state->display_blocked == FALSE)
  4639. #ifdef EDITOR
  4640.         &&(!state->edit_relayout_display_blocked)
  4641. #endif
  4642.         )
  4643.     {
  4644.  
  4645.         /* 
  4646.          * Don't resize the layer if we're laying out a block. This
  4647.          * will be done when the line is added to the block.
  4648.          */
  4649.         if (!lo_InsideLayer(state))
  4650.         {
  4651.             LO_SetDocumentDimensions(context, state->max_width, state->y);
  4652.         }
  4653.     }
  4654. }
  4655.  
  4656.  
  4657. void lo_FillInLineFeed( MWContext *context, lo_DocState *state, int32 break_type, uint32 clear_type, LO_LinefeedStruct *linefeed )
  4658. {
  4659.     linefeed->type = LO_LINEFEED;
  4660.     linefeed->ele_id = NEXT_ELEMENT;
  4661.     linefeed->x = state->x;
  4662.     linefeed->x_offset = 0;
  4663.     linefeed->y = state->y;
  4664.     linefeed->y_offset = 0;
  4665.     /* 
  4666.      * If we're laying out a block, we want the contents of the block
  4667.      * to determine the size of the block. The right margin is nothing
  4668.      * more than a hint for where to wrap the contents. We don't want 
  4669.      * the linefeed to extend out to the right margin, because it 
  4670.      * unnecessarily extends the block contents.
  4671.      */
  4672.     if (state->layer_nest_level > 0) {
  4673.         linefeed->width = 0;
  4674.     }
  4675.     else
  4676.     linefeed->width = state->right_margin - state->x;
  4677.     if (linefeed->width < 0)
  4678.     {
  4679.         linefeed->width = 0;
  4680.     }
  4681.     linefeed->height = state->line_height;
  4682.     /*
  4683.      * if this linefeed has a zero height, make it the height
  4684.      * of the current font.
  4685.      */
  4686.     if (linefeed->height == 0)
  4687.     {
  4688.         linefeed->height = state->text_info.ascent +
  4689.             state->text_info.descent;
  4690.         if ((linefeed->height <= 0)&&(state->font_stack != NULL)&&
  4691.             (state->font_stack->text_attr != NULL))
  4692.         {
  4693.             lo_fillin_text_info(context, state);
  4694.             linefeed->height = state->text_info.ascent +
  4695.                 state->text_info.descent;
  4696.         }
  4697.         /*
  4698.          * This should never happen, but we have it
  4699.          * covered just in case it does :-)
  4700.          */
  4701.         if (linefeed->height <= 0)
  4702.         {
  4703.             linefeed->height = state->default_line_height;
  4704.         }
  4705.     }
  4706.     linefeed->line_height = linefeed->height;
  4707.  
  4708.     linefeed->FE_Data = NULL;
  4709.     linefeed->anchor_href = state->current_anchor;
  4710.  
  4711.     if (state->font_stack == NULL)
  4712.     {
  4713.         LO_TextAttr tmp_attr;
  4714.         LO_TextAttr *tptr;
  4715.  
  4716.         /*
  4717.          * Fill in default font information.
  4718.          */
  4719.         lo_SetDefaultFontAttr(state, &tmp_attr, context);
  4720.         tptr = lo_FetchTextAttr(state, &tmp_attr);
  4721.         linefeed->text_attr = tptr;
  4722.     }
  4723.     else
  4724.     {
  4725.         linefeed->text_attr = state->font_stack->text_attr;
  4726.     }
  4727.  
  4728.     linefeed->baseline = state->baseline;
  4729.  
  4730.     linefeed->ele_attrmask = 0;
  4731.  
  4732.     linefeed->sel_start = -1;
  4733.     linefeed->sel_end = -1;
  4734.  
  4735.     linefeed->next = NULL;
  4736.     linefeed->prev = NULL;
  4737.     linefeed->break_type = (uint8) break_type;
  4738.     linefeed->clear_type = (uint8) clear_type;
  4739. }
  4740.  
  4741. Bool lo_CanUseBreakTable ( lo_DocState * state )
  4742. {
  4743.     Bool    multiByte;
  4744.     Bool    useBreakTable;
  4745.     
  4746.     useBreakTable = TRUE;
  4747.     
  4748.     lo_GetTextParseAtributes ( state, &multiByte );
  4749.  
  4750. #ifndef FAST_MULTI    
  4751.     /*
  4752.      * We also need some sort of check for the script - for example Arabic should go through
  4753.      * the old algorithm for now.
  4754.      */
  4755.     if ( multiByte )
  4756.         {
  4757.         useBreakTable = FALSE;
  4758.         }
  4759. #endif
  4760.     
  4761.     /*
  4762.      * Justified text is currently broken, route it through old layout for now
  4763.      */
  4764.     if ( ( state->align_stack != NULL ) && ( state->align_stack->alignment == LO_ALIGN_JUSTIFY ) )
  4765.         {
  4766.         useBreakTable = FALSE;
  4767.         }
  4768.         
  4769. #ifndef FAST_EDITOR
  4770.     if ( EDT_IS_EDITOR( context ) )
  4771.         {
  4772.         useBreakTable = FALSE;
  4773.         }
  4774. #endif
  4775.         
  4776. #ifdef XP_MAC
  4777.     if ( !gCallNewText )
  4778.         {
  4779.         useBreakTable = FALSE;
  4780.         }
  4781. #endif
  4782.  
  4783.     return useBreakTable;
  4784. }
  4785.  
  4786. /*
  4787.  * Is the current text that's being layed out using the break table layout algorithm?
  4788.  */
  4789. Bool lo_UseBreakTable ( LO_TextBlock * block )
  4790. {
  4791.     Bool            useBreakTable;
  4792.     
  4793.     useBreakTable = FALSE;
  4794.     
  4795.     if ( block != NULL )
  4796.         {
  4797.         if ( block->break_table != NULL )
  4798.             {
  4799.             useBreakTable = TRUE;
  4800.             }
  4801.         }
  4802.     
  4803.     return useBreakTable;
  4804. }
  4805.  
  4806. int32 lo_compute_text_basline_inc ( lo_DocState * state, LO_TextBlock * block, LO_TextStruct * text_data )
  4807. {
  4808.     int32    line_inc;
  4809.     int32    baseline_inc;
  4810.     
  4811.     /*
  4812.      * The baseline of the text element just added to the line may be
  4813.      * less than or greater than the baseline of the rest of the line
  4814.      * due to font changes.  If the baseline is less, this is easy,
  4815.      * we just increase y_offest to move the text down so the baselines
  4816.      * line up.  For greater baselines, we can't move the text up to
  4817.      * line up the baselines because we will overlay the previous line,
  4818.      * so we have to move all the previous elements in this line down.
  4819.      *
  4820.      * If the baseline is zero, we are the first element on the line,
  4821.      * and we get to set the baseline.
  4822.      */
  4823.     
  4824.     line_inc = 0;
  4825.     baseline_inc = 0;
  4826.     
  4827.     if (state->baseline == 0)
  4828.     {
  4829.         state->baseline = block->ascent;
  4830.         if (state->line_height < (state->baseline + block->descent))
  4831.         {
  4832.             state->line_height = state->baseline + block->descent;
  4833.         }
  4834.     }
  4835.     else if (block->ascent < state->baseline)
  4836.     {
  4837.         text_data->y_offset = state->baseline - block->ascent;
  4838.         if ((text_data->y_offset + block->ascent + block->descent) > state->line_height)
  4839.         {
  4840.             line_inc = text_data->y_offset +
  4841.                 block->ascent +
  4842.                 block->descent -
  4843.                 state->line_height;
  4844.         }
  4845.     }
  4846.     else
  4847.     {
  4848.         baseline_inc = block->ascent - state->baseline;
  4849.         if ((text_data->y_offset + block->ascent + block->descent - baseline_inc) > state->line_height)
  4850.         {
  4851.             line_inc = text_data->y_offset +
  4852.                 block->ascent +
  4853.                 block->descent -
  4854.                 state->line_height - baseline_inc;
  4855.         }
  4856.     }
  4857.  
  4858.     state->baseline += (intn) baseline_inc;
  4859.     state->line_height += (intn) (baseline_inc + line_inc);
  4860.     
  4861.     return baseline_inc;
  4862. }
  4863.  
  4864.  
  4865. void lo_FlushTextElement ( MWContext * context, lo_DocState * state, LO_TextBlock * block, LO_TextStruct * element );
  4866. void lo_FlushTextElement ( MWContext * context, lo_DocState * state, LO_TextBlock * block, LO_TextStruct * element )
  4867. {
  4868.     int32    baseline_inc;
  4869.     
  4870.     /* update the text layout state as if we had just layed this element out */
  4871.     block->buffer_read_index = element->block_offset;
  4872.  
  4873.     state->width = element->width;
  4874.  
  4875.     element->ele_id = NEXT_ELEMENT;
  4876.     element->x = state->x;
  4877.     element->y = state->y;
  4878.     element->y_offset = 0;
  4879.  
  4880.     element->sel_start = -1;
  4881.     element->sel_end = -1;
  4882.  
  4883.     baseline_inc = lo_compute_text_basline_inc ( state, block, element );
  4884.         
  4885.     element->prev = NULL;
  4886.     element->next = NULL;
  4887.     lo_AppendToLineList ( context, state, (LO_Element *) element, baseline_inc );
  4888.  
  4889.     state->line_buf_len = 0;
  4890.     state->x += state->width;
  4891.     state->width = 0;
  4892.     state->cur_ele_type = LO_NONE;
  4893.  
  4894.     /* update the element list for this block */
  4895.     if ( block->startTextElement == NULL )
  4896.         {
  4897.         block->startTextElement = element;
  4898.         }
  4899.  
  4900.     block->endTextElement = element;
  4901. }
  4902.  
  4903. uint32 lo_FindBlockOffset ( LO_TextBlock * block, LO_TextStruct * fromElement )
  4904. {
  4905.     uint32            blockOffset;
  4906.     LO_Element *    endElement;
  4907.     LO_Element *    element;
  4908.     
  4909.     blockOffset = 0;
  4910.         
  4911.     if ( fromElement != NULL )
  4912.         {
  4913.         /* run through all elements in this text block. the correct block offset is belongs to */
  4914.         /* the previous element in this list */
  4915.         element = (LO_Element *) block->startTextElement;
  4916.         endElement = (LO_Element *) block->endTextElement;
  4917.         
  4918.         while ( element != NULL )
  4919.             {
  4920.             if ( element == endElement )
  4921.                 {
  4922.                 break;
  4923.                 }
  4924.             
  4925.             /* is it the one we're looking for? */
  4926.             if ( element == (LO_Element *) fromElement )
  4927.                 {
  4928.                 break;
  4929.                 }
  4930.                 
  4931.             if ( element->type == LO_TEXT )
  4932.                 {
  4933.                 blockOffset = element->lo_text.block_offset;
  4934.                 }
  4935.             
  4936.             element = element->lo_any.next;
  4937.             }
  4938.         }
  4939.         
  4940.     return blockOffset;
  4941. }
  4942.  
  4943. void lo_RelayoutTextElements ( MWContext * context, lo_DocState * state, LO_TextBlock * block, LO_TextStruct * fromElement )
  4944. {
  4945.     LO_Element *    next;
  4946.     LO_Element *    element;
  4947.     LO_Element *    startElement;
  4948.     LO_Element *    endElement;
  4949.     uint32            lineWidth;
  4950.     Bool            done;
  4951.     Bool            fastPreformat;
  4952.     
  4953.     /* start at the beginning of this text block */
  4954.     block->buffer_read_index = 0;
  4955.     block->break_read_index = 0;
  4956.     
  4957.     /* we will rebuild the element list for this block as we go */
  4958.     startElement = (LO_Element *) block->startTextElement;
  4959.     endElement = (LO_Element *) block->endTextElement;
  4960.  
  4961.     block->startTextElement = NULL;
  4962.     block->endTextElement = NULL;
  4963.     
  4964.     if ( fromElement == NULL )
  4965.         {
  4966.         fromElement = (LO_TextStruct *) startElement;
  4967.         }
  4968.     
  4969.     /* sanity check */
  4970.     if ( fromElement == NULL )
  4971.         {
  4972.         return;
  4973.         }
  4974.     
  4975.     /*
  4976.      * We need to run through all elements up to start element and place them
  4977.      * back in the line list. We also need to recycle anything that's not text
  4978.      * (linefeeds and bullets)
  4979.      */
  4980.     element = startElement;
  4981.     
  4982.     while ( element != (LO_Element *) fromElement )
  4983.         {
  4984.         next = lo_tv_GetNextLayoutElement ( state, element, FALSE );
  4985.         
  4986.         /* if this element is text, put it on the line list */
  4987.         switch ( element->lo_any.type )
  4988.             {
  4989.             case LO_TEXT:
  4990.                 lo_PrepareElementForReuse ( context, state, element, element->lo_any.edit_element,
  4991.                         element->lo_any.edit_offset );
  4992.                 lo_FlushTextElement ( context, state, block, (LO_TextStruct *) element );
  4993.                 break;
  4994.             
  4995.             case LO_LINEFEED:
  4996.                 /* recycle this element */
  4997.                 element->lo_any.prev = NULL;
  4998.                 element->lo_any.next = NULL;
  4999.                 lo_RecycleElements( context, state, element );
  5000.                 
  5001. #if 0
  5002.                 /* toshok: I'm #if 0'ing these two calls to
  5003.                    lo_rl_AddSoftBreakAndFlushLine, since we shouldn't
  5004.                    be adding these line breaks while we're consuming
  5005.                    the old ones.  They will be added again by the text
  5006.                    layout machinery.  This fixes the problem of blank
  5007.                    regions in <pre>'s growing with every resize.
  5008.  
  5009.                    An alternative for this would be to just remove the
  5010.                    \n's from the pre text once we've inserted the line
  5011.                    breaks.*/
  5012.  
  5013.                 /* and then add a new linefeed */
  5014.                 lo_rl_AddSoftBreakAndFlushLine ( context, state );
  5015. #endif
  5016.                 break;
  5017.             
  5018.             default:
  5019.                 element->lo_any.prev = NULL;
  5020.                 element->lo_any.next = NULL;
  5021.                 lo_RecycleElements( context, state, element );
  5022.                 break;
  5023.             }
  5024.         
  5025.         element = next;
  5026.         }
  5027.     
  5028.     /*
  5029.      * Now, we may not need to lay any of the following elements out as out
  5030.      * layout environment may not have changed. So, run through the remaining
  5031.      * elements until we find the first one that's changed.
  5032.      *
  5033.      * We have to layout the last element using the proper code path so that
  5034.      * we can correctly update the state record with the last break position and
  5035.      * other flags.
  5036.      *
  5037.      * Column and line wrapped preformatted text can always reuse the elements as
  5038.      * it's wrapping will never change. Word wrapped preformatted text may change
  5039.      * if the document width changes.
  5040.      */
  5041.     
  5042.     element = (LO_Element *) fromElement;
  5043.     done = element == endElement;
  5044.     fastPreformat = ( block->format_mode == PRE_TEXT_YES ) || ( block->format_mode == PRE_TEXT_COLS );
  5045.     
  5046.     while ( ( !done ) && ( element != NULL ) )
  5047.         {
  5048.         next = lo_tv_GetNextLayoutElement ( state, element, FALSE );
  5049.         
  5050.         /* if this element is text, see if it will fit. otherwise recycle it */
  5051.         switch ( element->lo_any.type )
  5052.             {
  5053.             case LO_TEXT:
  5054.                 /*
  5055.                  * We only assume this element can be reused if the line width is exactly
  5056.                  * the same as last time. If the line is longer, we could potentially
  5057.                  * reuse this element (the next one may appear on this line as well) but
  5058.                  * we won't be able to set the state's old_break_position, which may be needed!
  5059.                  *
  5060.                  * We can also always flush column or line wrapped preformatted text (word wrapped
  5061.                  * preformatted text may need to be layed out again as it's wrapping may change).
  5062.                  */
  5063.                 lineWidth = state->right_margin - state->x;
  5064.                 
  5065.                 if ( fastPreformat || ( element->lo_text.doc_width == lineWidth ) )
  5066.                     {
  5067.                     lo_PrepareElementForReuse ( context, state, element, element->lo_any.edit_element,
  5068.                             element->lo_any.edit_offset );
  5069.                     lo_FlushTextElement ( context, state, block, (LO_TextStruct *) element );
  5070.                     }
  5071.                 else
  5072.                     {
  5073.                     /* the size has changed, we must relayout this element */
  5074.                     done = TRUE;
  5075.                     }
  5076.                 
  5077.                 break;
  5078.             
  5079.             case LO_LINEFEED:
  5080.                 /* recycle this element */
  5081.                 element->lo_any.prev = NULL;
  5082.                 element->lo_any.next = NULL;
  5083.                 lo_RecycleElements( context, state, element );
  5084.                 
  5085. #if 0
  5086.                 /* toshok: I'm #if 0'ing these two calls to
  5087.                    lo_rl_AddSoftBreakAndFlushLine, since we shouldn't
  5088.                    be adding these line breaks while we're consuming
  5089.                    the old ones.  They will be added again by the text
  5090.                    layout machinery.  This fixes the problem of blank
  5091.                    regions in <pre>'s growing with every resize. 
  5092.  
  5093.                    An alternative for this would be to just remove the
  5094.                    \n's from the pre text once we've inserted the line
  5095.                    breaks.*/
  5096.  
  5097.                 /* and then add a new linefeed */
  5098.                 lo_rl_AddSoftBreakAndFlushLine ( context, state );
  5099. #endif
  5100.                 break;
  5101.             
  5102.             default:
  5103.                 element->lo_any.prev = NULL;
  5104.                 element->lo_any.next = NULL;
  5105.                 lo_RecycleElements( context, state, element );
  5106.                 break;
  5107.             }
  5108.             
  5109.         element = next;
  5110.         
  5111.         /*
  5112.          * if we're at the last element, bail as we always need to layout this
  5113.          * one so that the state record is updated properly
  5114.          */
  5115.         if ( element == endElement )
  5116.             {
  5117.             break;
  5118.             }
  5119.         }
  5120.     
  5121.     /*
  5122.      * now run through and delete all the remaining elements in this text block.
  5123.      */
  5124.     while ( element != NULL )
  5125.         {
  5126.         next = lo_tv_GetNextLayoutElement ( state, element, FALSE );
  5127.  
  5128.         element->lo_any.prev = NULL;
  5129.         element->lo_any.next = NULL;
  5130.         lo_RecycleElements( context, state, element );
  5131.         
  5132.         if ( element == endElement )
  5133.             {
  5134.             break;
  5135.             }
  5136.             
  5137.         element = next;
  5138.         }
  5139. }
  5140.  
  5141. LO_Element * lo_RelayoutTextBlock ( MWContext * context, lo_DocState * state, LO_TextBlock * block, LO_TextStruct * fromElement )
  5142. {
  5143.     LO_Element *    next;
  5144.     LO_Element *    endElement;
  5145.     LO_Element *    lo_ele;
  5146.  
  5147.     state->cur_text_block = block;
  5148.     
  5149.     /*
  5150.      * Update some of the global state information
  5151.      */
  5152.     state->breakable = block->ele_attrmask & LO_ELE_BREAKABLE;
  5153.     
  5154.     /* get the next element for the overall relayout process */
  5155.     if ( block->endTextElement != NULL )
  5156.         {
  5157.         next = lo_tv_GetNextLayoutElement ( state, (LO_Element *) block->endTextElement, FALSE );
  5158.         }
  5159.     else
  5160.         {
  5161.         next = lo_tv_GetNextLayoutElement ( state, (LO_Element *) block, FALSE );
  5162.         }
  5163.  
  5164.     /*
  5165.      * In the relayout case we might be able to skip layout for some elements that have not changed.
  5166.      * This happens frequenty for typing in the editor and occasionaly in table layout (very occasionally
  5167.      * on resizes).
  5168.      *
  5169.      * To do this, we run through the text elements until we come across one whose width does not match the
  5170.      * layout width or whose text has changed. That element and all others are then recycled.
  5171.      */
  5172.     
  5173.     if ( EDT_IS_EDITOR( context ) )
  5174.         {
  5175.         /*
  5176.          * for the editor we don't want to relayout the text elements that precede the current one. we just
  5177.          * want to start laying out afresh from this specified element to the end of the text block. the editor
  5178.          * will take care of merging the elements back in
  5179.          */
  5180.         block->buffer_read_index = lo_FindBlockOffset ( block, fromElement );
  5181.  
  5182.         state->edit_current_element = block->edit_element;
  5183.         state->edit_current_offset = 0;
  5184.         
  5185.         /* if we're laying this element out, then we need to reinsert it on the line list */
  5186.         if ( ( block->startTextElement == fromElement ) || ( fromElement == NULL ) )
  5187.             {
  5188.             /* record the current edit element for later use */
  5189.             lo_PrepareElementForReuse ( context, state, (LO_Element *) block, block->edit_element,
  5190.                     block->edit_offset );
  5191.             
  5192.             block->ele_id = NEXT_ELEMENT;
  5193.             block->x = state->x;
  5194.             block->y = state->y;
  5195.             block->x_offset = 0;
  5196.             block->y_offset = 0;
  5197.             
  5198.             /* free all the text elements that we're going to reflow */
  5199.             endElement = (LO_Element *) block->endTextElement;
  5200.             lo_ele = (LO_Element *) block->startTextElement;
  5201.             while ( lo_ele != NULL )
  5202.                 {
  5203.                 LO_Element * next_ele;
  5204.                 
  5205.                 next_ele = lo_ele->lo_any.next;
  5206.                 
  5207.                 lo_ele->lo_any.next = NULL;
  5208.                 lo_ele->lo_any.prev = NULL;
  5209.                 lo_RecycleElements( context, state, lo_ele );
  5210.                 
  5211.                 if ( lo_ele == endElement )
  5212.                     {
  5213.                     break;
  5214.                     }
  5215.                 
  5216.                 lo_ele = next_ele;
  5217.                 }
  5218.  
  5219.             block->startTextElement = NULL;
  5220.             block->endTextElement = NULL;
  5221.             
  5222.             block->prev = NULL;
  5223.             block->next = NULL;
  5224.             lo_AppendToLineList ( context, state, (LO_Element *) block, 0 );
  5225.             }
  5226.         else
  5227.             {
  5228.             LO_TextStruct * lastText;
  5229.             Bool            hitFromElement;
  5230.             
  5231.             /*
  5232.              * We're reflowing from somewhere within the text block (past the first element). We need
  5233.              * to reset the endTextElement as well as recycle from the fromElement to the end of the text block
  5234.              */
  5235.             
  5236.             hitFromElement = FALSE;
  5237.             endElement = (LO_Element *) block->endTextElement;
  5238.             lo_ele = (LO_Element *) block->startTextElement;
  5239.             lastText = block->startTextElement;
  5240.  
  5241.             while ( lo_ele != NULL )
  5242.                 {
  5243.                 LO_Element * next_ele;
  5244.                 
  5245.                 next_ele = lo_ele->lo_any.next;
  5246.                 
  5247.                 if ( lo_ele == (LO_Element *) fromElement )
  5248.                     {
  5249.                     hitFromElement = TRUE;
  5250.                     }
  5251.                     
  5252.                 /* if we've found the fromElement, we need to start recycling */
  5253.                 if ( hitFromElement )
  5254.                     {
  5255.                     lo_ele->lo_any.next = NULL;
  5256.                     lo_ele->lo_any.prev = NULL;
  5257.                     lo_RecycleElements( context, state, lo_ele );
  5258.                     }
  5259.                 
  5260.                 if ( lo_ele == endElement )
  5261.                     {
  5262.                     break;
  5263.                     }
  5264.                 
  5265.                 /* if we haven't hit the from element and this is a text element, it may be the new end */
  5266.                 /* element for the block */
  5267.                 if ( !hitFromElement && ( lo_ele->type == LO_TEXT ) )
  5268.                     {
  5269.                     lastText = &lo_ele->lo_text;
  5270.                     }
  5271.                 
  5272.                 lo_ele = next_ele;
  5273.                 }
  5274.             
  5275.             /* reset the endElement for the block */
  5276.             block->endTextElement = lastText;
  5277.             }
  5278.         }
  5279.     else
  5280.         {
  5281.         /* put the text block back in the line list and then add any existing elements that we can */
  5282.         block->prev = NULL;
  5283.         block->next = NULL;
  5284.         block->ele_id = NEXT_ELEMENT;
  5285.         block->x = state->x;
  5286.         block->y = state->y;
  5287.         lo_AppendToLineList ( context, state, (LO_Element *) block, 0 );
  5288.  
  5289.         lo_RelayoutTextElements ( context, state, block, fromElement );
  5290.         }
  5291.         
  5292.     /*
  5293.      * Because we're lame for now we just delete all the old linefeeds and lay the text out afresh
  5294.      */
  5295.     
  5296.     /* Tell everybody we're laying out text */
  5297.     if (state->cur_ele_type != LO_TEXT)
  5298.         {
  5299.         lo_FreshText(state);
  5300.         state->cur_ele_type = LO_TEXT;
  5301.         }
  5302.  
  5303.     /* actually layout the text */
  5304.     state->preformatted = block->format_mode;
  5305.     
  5306.     if ( lo_UseBreakTable ( block ) )
  5307.         {
  5308.         lo_SetupBreakState ( block );
  5309.         
  5310.         /* be sure to set up the editor offset */
  5311.         if ( EDT_IS_EDITOR( context ) )
  5312.             {
  5313.             state->edit_force_offset = TRUE;
  5314.             state->edit_current_offset = block->buffer_read_index;
  5315.             }
  5316.             
  5317.         lo_LayoutTextBlock ( context, state, TRUE );
  5318.         }
  5319.     else
  5320.     if ( block->format_mode == PRE_TEXT_NO )
  5321.         {
  5322.         lo_LayoutFormattedText ( context, state, block );
  5323.         }
  5324.     else
  5325.         {
  5326.         lo_LayoutPreformattedText ( context, state, block );
  5327.         }
  5328.     
  5329.     /*
  5330.      * If there's text left in the line buffer, then flush it.
  5331.      *
  5332.      * BRAIN DAMAGE: We don't want to do that here - there may be a following text block
  5333.      * that continues this same text buffer.
  5334.      */
  5335.     lo_FlushLineBuffer(context, state);
  5336.         
  5337.     return next;
  5338. }
  5339.  
  5340. Bool lo_ChangeText ( LO_TextBlock * block, char * text )
  5341. {
  5342.     uint32    length;
  5343.     /*
  5344.      * Reset the text contents for this text block. If we have a break table,
  5345.      * then we need to rebuild it.
  5346.      */
  5347.     
  5348. #ifdef LOCAL_DEBUG
  5349.     XP_TRACE( ("Setting text for text block %lx to %s", block, text) );
  5350. #endif
  5351.     
  5352.     length = XP_STRLEN ( text ) + 1;
  5353.     if ( length > block->buffer_write_index )
  5354.         {
  5355.         if ( !lo_GrowTextBlock ( block, length - block->buffer_write_index ) )
  5356.             {
  5357.             return FALSE;
  5358.             }
  5359.         }
  5360.         
  5361.     if ( lo_UseBreakTable ( block ) )
  5362.         {
  5363.         block->buffer_write_index = 0;
  5364.         block->last_buffer_write_index = 0;
  5365.         block->break_write_index = 0;
  5366.         block->last_break_offset = 0;
  5367.         
  5368.         lo_AppendTextToBlock ( NULL, NULL, block, text );
  5369.         }
  5370.     else
  5371.         {
  5372.         /* for old style text we just want to replace the buffer */
  5373.         XP_BCOPY ( text, (char *) block->text_buffer, length );
  5374.         block->buffer_write_index = length;
  5375.         }
  5376.     
  5377.     return TRUE;
  5378. }
  5379.  
  5380. /*
  5381.  *
  5382.  * ======================================================================================================
  5383.  * 
  5384.  *                                            New text layout
  5385.  *
  5386.  * ======================================================================================================
  5387.  */
  5388.  
  5389. /*
  5390.  * Break Table constants
  5391.  */
  5392.  
  5393. #define    MAX_NATURAL_LENGTH        0xAL
  5394. #define    LINE_FEED                0xBL
  5395. #define    BYTE_LENGTH                0xCL
  5396. #define    WORD_LENGTH                0xDL
  5397. #define    LONG_LENGTH                0xEL
  5398. #define    MULTI_BYTE                0xFL
  5399.  
  5400. #define    MULTI_BYTE_DATA_SIZE    16
  5401.  
  5402. /*
  5403.  * Break Position Magic Constants
  5404.  */
  5405. #define    OVERRAN_BREAK_TABLE    -1
  5406. #define    BREAK_LINEFEED        -2
  5407.  
  5408. #define    TEXT_BUFFER_INC        256
  5409. #define    BREAK_TABLE_INC        64
  5410.  
  5411. typedef struct BreakState
  5412. {
  5413.     uint32        buffer_read_index;
  5414.     uint32        break_read_index;
  5415.     uint32        multibyte_index;
  5416.     uint32        multibyte_length;
  5417.     uint32        last_line_break;
  5418.     uint32        lineLength;
  5419. } BreakState;
  5420.  
  5421. static LO_TextBlock * lo_CurrentTextBlock ( MWContext * context, lo_DocState * state );
  5422.  
  5423. /* routines to walk through our break table */
  5424. static uint8 * lo_GetNextTextPosition ( LO_TextBlock * block, uint32 * outWordLength, uint32 * outLineLength, Bool * canBreak );
  5425. static uint8 * lo_GetPrevTextPosition ( LO_TextBlock * block, uint32 * outWordLength, uint32 * outLineLength, Bool * canBreak );
  5426. static uint8 * lo_RestoreBreakState ( LO_TextBlock * block, BreakState * state, uint32 * lineLength );
  5427. static void lo_SetLineBreak ( LO_TextBlock * block, Bool skipSpace );
  5428. static uint8 * lo_GetLineStart ( LO_TextBlock * block );
  5429. static void lo_SkipCharacter ( LO_TextBlock * block );
  5430. static Bool lo_SkipInitialSpace ( LO_TextBlock * block );
  5431.  
  5432. static Bool lo_SetBreakPosition ( LO_TextBlock * block );
  5433. static Bool lo_SetMultiByteRun ( LO_TextBlock * block, int32 charSize, Bool breakable, Bool eachCharBreakable );
  5434. static Bool lo_SetBreakCommand ( LO_TextBlock * block, uint32 command, uint32 commandLength );
  5435. static void lo_CopyText ( uint8 * src, uint8 * dst, uint32 length );
  5436. static void lo_CopyTextToLineBuffer ( lo_DocState * state, uint8 * src, uint32 length );
  5437.  
  5438. static uint32  lo_FindLineBreak ( MWContext * context, lo_DocState * state, LO_TextBlock * block, uint8 * text,
  5439.     uint16 * widthTable, uint32 * width, int32 * minWidth, Bool * allTextFits );
  5440.  
  5441. /* the parsers */
  5442. static void lo_ParseSingleText ( lo_DocState * state, LO_TextBlock * block, Bool parseAllText, char * text );
  5443. static void lo_ParseSinglePreformattedText ( lo_DocState * state, LO_TextBlock * block, Bool parseAllText, char * text );
  5444. static void lo_ParseDoubleText ( lo_DocState * state, LO_TextBlock * block, Bool parseAllText, char * text );
  5445. static void lo_ParseDoublePreformattedText ( lo_DocState * state, LO_TextBlock * block, Bool parseAllText, char * text );
  5446.  
  5447. extern int32 lo_correct_text_element_width(LO_TextInfo *text_info);
  5448.  
  5449. #ifdef LOG
  5450. static Bool gHaveLog = FALSE;
  5451. #endif
  5452.  
  5453.  
  5454. /*
  5455.  * Some helpful macros for code inlining
  5456.  */
  5457.  
  5458. #define    SAVE_BREAK_STATE(block,state,line_length)                \
  5459.     (state)->buffer_read_index = (block)->buffer_read_index;    \
  5460.     (state)->break_read_index = (block)->break_read_index;        \
  5461.     (state)->multibyte_index = (block)->multibyte_index;        \
  5462.     (state)->multibyte_length = (block)->multibyte_length;        \
  5463.     (state)->last_line_break = (block)->last_line_break;        \
  5464.     (state)->lineLength = line_length;
  5465.     
  5466.  
  5467. static LO_TextBlock *
  5468. lo_CurrentTextBlock ( MWContext * context, lo_DocState * state )
  5469. {
  5470.     LO_TextBlock * textBlock;
  5471.     
  5472.     textBlock = state->cur_text_block;
  5473.     if ( textBlock == NULL )
  5474.         {
  5475.         textBlock = (LO_TextBlock *)lo_NewElement ( context, state, LO_TEXTBLOCK, NULL, 0 );
  5476.  
  5477.         textBlock->type = LO_TEXTBLOCK;
  5478.         textBlock->x_offset = 0;
  5479.         textBlock->ele_id = NEXT_ELEMENT;
  5480.         textBlock->x = state->x;
  5481.         textBlock->y = state->y;
  5482.         textBlock->y_offset = 0;
  5483.         textBlock->width = 0;
  5484.         textBlock->height = 0;
  5485.         textBlock->line_height = 0;
  5486.         textBlock->next = NULL;
  5487.         textBlock->prev = NULL;
  5488.         textBlock->text_attr = NULL;
  5489.         textBlock->anchor_href = NULL;
  5490.         textBlock->ele_attrmask = 0;
  5491.         textBlock->format_mode = 0;
  5492.         
  5493.         textBlock->startTextElement = NULL;
  5494.         textBlock->endTextElement = NULL;
  5495.         
  5496.         textBlock->text_buffer = NULL;
  5497.         textBlock->buffer_length = 0;
  5498.         textBlock->buffer_write_index = 0;
  5499.         textBlock->last_buffer_write_index = 0;
  5500.         textBlock->buffer_read_index = 0;
  5501.         textBlock->last_line_break = 0;
  5502.         
  5503.         textBlock->break_table = NULL;
  5504.         textBlock->break_length = 0;
  5505.         textBlock->break_write_index = 0;
  5506.         textBlock->break_read_index = 0;
  5507.         textBlock->last_break_offset = 0;
  5508.         textBlock->multibyte_index = 0;
  5509.         textBlock->multibyte_length = 0;
  5510.         
  5511.         textBlock->old_break = NULL;
  5512.         textBlock->old_break_pos = 0;
  5513.         textBlock->old_break_width = 0;
  5514.         
  5515.         textBlock->totalWidth = 0;
  5516.         textBlock->totalChars = 0;
  5517.         textBlock->break_pending = 0;
  5518.         textBlock->last_char_is_whitespace = 0;
  5519.         
  5520.         textBlock->ascent = 0;
  5521.         textBlock->descent = 0;
  5522.         
  5523.         /* BRAIN DAMAGE: Add this enum to lo_ele.h! */
  5524.         textBlock->text_buffer = XP_ALLOC ( TEXT_BUFFER_INC );
  5525.         textBlock->buffer_length = TEXT_BUFFER_INC;
  5526.         
  5527.         textBlock->break_table = XP_ALLOC ( BREAK_TABLE_INC );
  5528.         textBlock->break_length = BREAK_TABLE_INC * 2;
  5529.         
  5530.         /*
  5531.          * Since we're creating a new text block, grab some of the text state out of the
  5532.          * layout state.
  5533.          */
  5534.         textBlock->anchor_href = state->current_anchor;
  5535.         
  5536.         if ( state->font_stack != NULL )
  5537.             {
  5538.             textBlock->text_attr = state->font_stack->text_attr;
  5539.             }
  5540.             
  5541.         if (state->breakable != FALSE)
  5542.             {
  5543.             textBlock->ele_attrmask |= LO_ELE_BREAKABLE;
  5544.             }
  5545.         
  5546.         state->cur_text_block = textBlock;        
  5547.         
  5548.         lo_AppendToLineList ( context, state, (LO_Element *) textBlock, 0 );
  5549.         }
  5550.     
  5551.     return textBlock;
  5552. }
  5553.  
  5554. void lo_AppendTextToBlock ( MWContext *context, lo_DocState *state, LO_TextBlock * block, char *text )
  5555. {
  5556.     Bool    parseAllText;
  5557.     Bool    multiByte;
  5558.     
  5559.     /*
  5560.      * We have several cases in which we can just bail:
  5561.      *    1. The text string and the line buffer is empty.
  5562.      *    2. The text string is all whitespace and we already have a trailing space.
  5563.      */
  5564.     
  5565.     if ( ( state != NULL ) && ( state->line_buf_len == 0 ) )
  5566.         {
  5567.         char *    t_ptr;
  5568.  
  5569.         /* if this string is empty, bail */
  5570.         if ( *text == '\0' )
  5571.             {
  5572.             return;
  5573.             }
  5574.         
  5575.         /* if it's only whitespace and we have a trailing space, then bail */
  5576.         if ( state->trailing_space )
  5577.             {
  5578.             t_ptr = text;
  5579.             
  5580.             while ( *t_ptr != '\0' )
  5581.                 {
  5582.                 if ( !XP_IS_SPACE( *t_ptr ) )
  5583.                     {
  5584.                     break;
  5585.                     }
  5586.                 
  5587.                 ++t_ptr;
  5588.                 }
  5589.             
  5590.             if ( ( *t_ptr == '\0' ) && ( t_ptr != text ) )
  5591.                 {
  5592.                 return;
  5593.                 }
  5594.             }
  5595.         }
  5596.     
  5597.     /*
  5598.      * If we don't have a block, create one if we have a valid state record. Otherwise we
  5599.      * have an error
  5600.      */
  5601.     
  5602.     if ( block == NULL )
  5603.         {
  5604.         /* the editor may call us with a NULL state and context record, in this case we must always have a block */
  5605.         XP_ASSERT(( state != NULL ) && ( context != NULL ));
  5606.         if ( ( state != NULL ) && ( context != NULL ) )
  5607.             {
  5608.             block = lo_CurrentTextBlock ( context, state );
  5609.             }
  5610.         }
  5611.     
  5612.     /*
  5613.      * OPTIMIZATION: If the parser could tell us if we have a split buffer then we could intelligently set
  5614.      * parseAllText here and not buffer words that we think may be split across a buffer but in reality are
  5615.      * whole.
  5616.      */
  5617.      
  5618.     /*
  5619.      * If we're in an editor context, then we can always parse the whole buffer of text, we never need to
  5620.      * worry about partial buffers being passed to us.
  5621.      */
  5622.     parseAllText = EDT_IS_EDITOR( context );
  5623.         
  5624.     /*
  5625.      * Scan through the text, removing whitespace and adding words to the text buffer as we come across
  5626.      * them.
  5627.      *
  5628.      * Particular things we have to deal with:
  5629.      *        - Preformatted text (normal, word wrapped and column wrapped)
  5630.      *        - Normal single byte text
  5631.      *        - Multibyte text
  5632.      *        - non-breaking spaces
  5633.      */
  5634.     
  5635.     lo_GetTextParseAtributes ( state, &multiByte );
  5636.  
  5637.     if ( FALSE || multiByte )
  5638.         {
  5639.         lo_ParseDoubleText ( state, block, parseAllText, text );
  5640.         }
  5641.     else
  5642.         {
  5643.         lo_ParseSingleText ( state, block, parseAllText, text );
  5644.         }
  5645. }
  5646.  
  5647. static void
  5648. lo_GetTextParseAtributes ( lo_DocState * state, Bool * multiByte )
  5649. {
  5650.     int16    charset;
  5651.     
  5652.     *multiByte = FALSE;
  5653.     
  5654.     if ( state != NULL )
  5655.         {
  5656.         charset = state->font_stack->text_attr->charset;
  5657.         if ( (INTL_CharSetType ( charset ) != SINGLEBYTE ) && !( INTL_CharSetType ( charset ) & CS_SPACE ) )
  5658.             {
  5659.             *multiByte = TRUE;
  5660.             }
  5661.         }
  5662. }
  5663.  
  5664. static void
  5665. lo_ParseSingleText ( lo_DocState * state, LO_TextBlock * block, Bool parseAllText, char * text )
  5666. {
  5667.     uint8 *            t_ptr;
  5668.     uint8 *            w_start;
  5669.     uint8 *            w_end;
  5670.     uint8 *            line_buff;
  5671.     uint32            w_length;
  5672.     Bool            skipped_space;
  5673.     uint32            textLength;
  5674.     
  5675.     /* check for textTransform properties and aply them */
  5676.     if( ( state != NULL ) && ( state->top_state && state->top_state->style_stack ) )
  5677.         {
  5678.         char * property;
  5679.         
  5680.         StyleStruct *style_struct = STYLESTACK_GetStyleByIndex( state->top_state->style_stack, 0);
  5681.  
  5682.         if( style_struct )
  5683.             {
  5684.             property = STYLESTRUCT_GetString(style_struct, TEXT_TRANSFORM_STYLE);
  5685.             if(property)
  5686.                 {
  5687.                 lo_transform_text_from_string_method(text, property);
  5688.                 }
  5689.             }
  5690.         }
  5691.  
  5692.     t_ptr = (uint8 *) text;
  5693.     skipped_space = FALSE;    
  5694.     
  5695.     if ( ( state != NULL ) && ( state->line_buf_len > 0 ) )
  5696.         {
  5697.         PA_LOCK(line_buff, uint8 *, state->line_buf);
  5698.         textLength = state->line_buf_len;
  5699.         
  5700.         if ( *line_buff == '\0' )
  5701.             {
  5702.             line_buff = NULL;
  5703.             textLength = 0;
  5704.             state->line_buf_len = 0;
  5705.             }
  5706.         }
  5707.     else
  5708.         {
  5709.         line_buff = NULL;
  5710.         textLength = 0;
  5711.         }
  5712.         
  5713.     /* Make sure the text block has enough space to hold this block of text */
  5714.     textLength += XP_STRLEN ( text );
  5715.     lo_GrowTextBlock ( block, textLength + 1 );
  5716.     
  5717.     /*
  5718.      * If there's anything in the line buffer, then pull that out now
  5719.      */
  5720.     if ( line_buff != NULL )
  5721.         {
  5722.         /* was there any trailing space left for us? */
  5723.         skipped_space = state->trailing_space;
  5724.             
  5725.         /* skip any white space at the head of our text */
  5726.         while ( ( *line_buff != '\0' ) && XP_IS_SPACE ( *line_buff ) )
  5727.             {
  5728.             line_buff++;
  5729.             state->line_buf_len--;
  5730.             skipped_space = TRUE;
  5731.             }
  5732.         
  5733.         /*
  5734.          * if we skipped any space and we're not at the end of the buffer, 
  5735.          * then insert a break position
  5736.          */
  5737.         if ( *line_buff != '\0' )
  5738.             {
  5739.             if ( skipped_space )
  5740.                 {
  5741.                 if ( !lo_SetBreakPosition ( block ) )
  5742.                     {
  5743.                     state->top_state->out_of_memory = TRUE;
  5744.                     return;
  5745.                     }
  5746.                 
  5747.                 /*
  5748.                  * If the space was real whitespace and not a trailing space from a previous layout,
  5749.                  * then copy the space to the text block.
  5750.                  */
  5751.                 if ( !state->trailing_space )
  5752.                     {
  5753.                     block->text_buffer[ block->buffer_write_index ] = ' ';
  5754.                     block->buffer_write_index++;
  5755.                     block->last_buffer_write_index++;
  5756.                     skipped_space = TRUE;
  5757.                     }
  5758.  
  5759.                 skipped_space = FALSE;
  5760.                 }
  5761.             
  5762.             /* copy in the line buffer. it is only allowed to be one word */
  5763.             lo_CopyText ( line_buff, &block->text_buffer[ block->buffer_write_index ], state->line_buf_len );
  5764.             block->buffer_write_index += state->line_buf_len;
  5765.             state->line_buf_len = 0;
  5766.             }
  5767.         }
  5768.     
  5769.     /*
  5770.      * The last chunk of text may have left a single piece of whitespace at the end of the buffer. If so,
  5771.      * we need to skip any whitespace at the front of the buffer so we don't have to worry about this inside
  5772.      * the main loop.
  5773.      *
  5774.      * When called from the editor, we may not have a state record. However, we also won't need to worry
  5775.      * about this case as it will have taken care of it for us.
  5776.      */
  5777.     if ( ( state != NULL ) && ( state->trailing_space ) && ( *t_ptr != '\0' ) )
  5778.         {
  5779.         skipped_space = TRUE;
  5780.         
  5781.         while ( ( *t_ptr != '\0' ) && XP_IS_SPACE ( *t_ptr ) )
  5782.             {
  5783.             t_ptr++;
  5784.             }
  5785.         }
  5786.     
  5787.     while ( *t_ptr != '\0' )
  5788.         {
  5789.         w_start = t_ptr;
  5790.         
  5791.         /* skip past any whitespace before this word. */
  5792.         while ( ( *w_start != '\0' ) && XP_IS_SPACE ( *w_start ) )
  5793.             {
  5794.             w_start++;
  5795.             }
  5796.                         
  5797.         /* run through the text and find the end of the word */
  5798.         w_end = w_start;
  5799.         w_length = 0;
  5800.         
  5801.         while ( ( *w_end != '\0' ) && !XP_IS_SPACE ( *w_end ) )
  5802.             {
  5803.             w_end++;
  5804.             w_length++;
  5805.             }
  5806.  
  5807.         /*
  5808.          * If we hit the end of the buffer then we may be inside a partial word. This can cause
  5809.          * problems with interword kerning, multibyte characters and other contextually sensitive
  5810.          * script systems.
  5811.          *
  5812.          * We buffer this word in the line_buff. If this is truly the end of the text, then we'll
  5813.          * be called to flush the last line. We'll do this by appending this word to our text block
  5814.          * and then laying out the last of the text.
  5815.          *
  5816.          * If this word is just split across a buffer, then it will be inserted to the beginning of
  5817.          * the next text block.
  5818.          *
  5819.          * If the caller tells us to parse the whole buffer, then we don't care.
  5820.          */
  5821.         
  5822.         if ( !parseAllText && ( *w_end == '\0' ) && ( w_length > 0 ) && ( state != NULL ) )
  5823.             {
  5824.             if ( w_start != t_ptr )
  5825.                 {
  5826.                 /* put a space in the line buffer */
  5827.                 lo_CopyTextToLineBuffer ( state, (uint8 *) " ", 1 );
  5828.                 if ( state->top_state->out_of_memory )
  5829.                     {
  5830.                     return;
  5831.                     }
  5832.                 }
  5833.                 
  5834.             /* put this text in the line buffer */
  5835.             lo_CopyTextToLineBuffer ( state, w_start, w_length );
  5836.             if ( state->top_state->out_of_memory )
  5837.                 {
  5838.                 return;
  5839.                 }
  5840.                 
  5841.             /* we now have text after the whitespace */
  5842.             skipped_space = FALSE;
  5843.             
  5844.             break;
  5845.             }
  5846.                     
  5847.         /*
  5848.          * If we skipped some white space, then we know that we can put a break here.
  5849.          */
  5850.         if ( w_start != t_ptr )
  5851.             {
  5852.             skipped_space = TRUE;
  5853.             
  5854.             /* add the break position */
  5855.             if ( !lo_SetBreakPosition ( block ) )
  5856.                 {
  5857.                 if ( state != NULL )
  5858.                     {
  5859.                     state->top_state->out_of_memory = TRUE;
  5860.                     }
  5861.                 return;
  5862.                 }
  5863.  
  5864.             if ( block->buffer_length < ( block->buffer_write_index + 1 ) )
  5865.                 {
  5866.                 if ( !lo_GrowTextBlock ( block, 1 ) )
  5867.                     {
  5868.                     if ( state != NULL )
  5869.                         {
  5870.                         state->top_state->out_of_memory = TRUE;
  5871.                         }
  5872.                     return;
  5873.                     }
  5874.                 }
  5875.             
  5876.             block->text_buffer[ block->buffer_write_index ] = ' ';
  5877.             block->buffer_write_index++;
  5878.             
  5879.             /*
  5880.              * BRAIN DAMAGE: Add a new field to the text block struct to indicate how many
  5881.              * chars to skip when calculating the length of the next run.
  5882.              */
  5883.             block->last_buffer_write_index++;
  5884.             }
  5885.         else
  5886.             {
  5887.             skipped_space = FALSE;
  5888.             }
  5889.         
  5890.         /* if we found anything, add it to the buffer */
  5891.         if ( w_length > 0 )
  5892.             {
  5893.             if ( block->buffer_length < ( block->buffer_write_index + w_length ) )
  5894.                 {
  5895.                 if ( !lo_GrowTextBlock ( block, w_length ) )
  5896.                     {
  5897.                     if ( state != NULL )
  5898.                         {
  5899.                         state->top_state->out_of_memory = TRUE;
  5900.                         }
  5901.                     return;
  5902.                     }
  5903.                 }
  5904.             
  5905.             lo_CopyText ( w_start, &block->text_buffer[ block->buffer_write_index ], w_length );
  5906.             block->buffer_write_index += w_length;
  5907.             
  5908.             /* we now have text after the whitespace */
  5909.             skipped_space = FALSE;
  5910.             }
  5911.         
  5912.         t_ptr = w_end;
  5913.         }
  5914.         
  5915.         /*
  5916.          * Remember whether the last thing we added was whitespace
  5917.          */
  5918.         block->last_char_is_whitespace = skipped_space;
  5919. }
  5920.  
  5921.  
  5922. /*
  5923.  * Parse State Table for two byte text
  5924.  */
  5925.  
  5926. typedef enum {
  5927.     kUnprohibited = PROHIBIT_NOWHERE,
  5928.     kBeginProhibited = PROHIBIT_BEGIN_OF_LINE,
  5929.     kEndProhibited = PROHIBIT_END_OF_LINE,
  5930.     kWordBreakProhibited = PROHIBIT_WORD_BREAK,
  5931.     kSingleByte,
  5932.     kBreakableSpace,
  5933.     kFlushFinalRun,
  5934.     kNumCharTypes
  5935. } ParseState;
  5936.  
  5937. /*
  5938.  * Flags for the state table command
  5939.  */
  5940.  
  5941. #define    SET_BREAKABLE            0x01        /* dump the current run as a single breakable run */
  5942. #define    SET_MULTI_BREAKABLE        0x02        /* dump the current run as a mutibyte breakable run */
  5943. #define    DUMP_TEXT_AND_BREAK        0x04        /* dump the text to the buffer with no break point and then stop processing */
  5944. #define    CARRY_LAST_CHAR            0x08        /* move the last char of this run into the next state */
  5945. #define    INC_RUN_LENGTH            0x10        /* inc the length of this run (we could do without this one) */
  5946. #define    INSERT_WHITESPACE        0x20        /* insert a single whitespace into the text buffer */
  5947. #define    SKIP_CHAR                0x40        /* skip the current char */
  5948. #define    MAINTAIN_CHAR_TYPE        0x80        /* don't change the curCharType */
  5949.  
  5950. /*
  5951.  * Things to Note:
  5952.  *
  5953.  * 1. SET_BREAKABLE means to insert a normal break command. This run can be broken at the end of the run.
  5954.  * 2. SET_MULTI_BREAKABLE means that we have a run which can be broken at the end of every character.
  5955.  *
  5956.  */
  5957.  
  5958. /*
  5959.  * The mutibyte breakable run data word is 16 bits big. It is organized as:
  5960.  *
  5961.  *    BIT:    15    14    13            12    11    10    9    8    7    6    5    4    3    2    1    0
  5962.  *    FIELD:    CHAR SIZE            RUN LENGTH
  5963.  */
  5964.  
  5965. #define    MULTI_CHAR_SIZE_MASK    0xE000
  5966. #define    MULTI_CHAR_SIZE_SHIFT    12
  5967.  
  5968. #define    MULTI_LENGTH_MASK        0x1FFF
  5969.  
  5970. /*
  5971.  * The state table
  5972.  */
  5973.  
  5974. static uint8 gParseTable[ kNumCharTypes ][ kNumCharTypes ] = 
  5975. {
  5976.     /*    current char            next char                    operations */
  5977.     
  5978. /* Unprohibited two byte text */
  5979.     /*    kUnprohibited,            kUnprohibited,            */    INC_RUN_LENGTH,
  5980.     /*    kUnprohibited,            kBeginProhibited,        */    SET_MULTI_BREAKABLE + CARRY_LAST_CHAR,
  5981.     /*    kUnprohibited,            kEndProhibited,            */    SET_MULTI_BREAKABLE,
  5982.     /*    kUnprohibited,            kWordBreakProhibited,    */    SET_MULTI_BREAKABLE,
  5983.     /*    kUnprohibited,            kSingleByte,            */    SET_MULTI_BREAKABLE,
  5984.     /*    kUnprohibited,            kBreakableSpace,        */    SET_MULTI_BREAKABLE + INSERT_WHITESPACE + SKIP_CHAR,
  5985.     /*    kUnprohibited,            kFlushFinalRun,            */    SET_MULTI_BREAKABLE,
  5986.     
  5987. /* Begin Line Prohibited two byte text */
  5988.     /*    kBeginProhibited,        kUnprohibited,            */    SET_BREAKABLE,
  5989.     /*    kBeginProhibited,        kBeginProhibited,        */    INC_RUN_LENGTH,
  5990.     /*    kBeginProhibited,        kEndProhibited,            */    SET_BREAKABLE,
  5991.     /*    kBeginProhibited,        kWordBreakProhibited,    */    SET_BREAKABLE,
  5992.     /*    kBeginProhibited,        kSingleByte,            */    SET_BREAKABLE,
  5993.     /*    kBeginProhibited,        kBreakableSpace,        */    SET_BREAKABLE + INSERT_WHITESPACE + SKIP_CHAR,
  5994.     /*    kBeginProhibited,        kFlushFinalRun,            */    DUMP_TEXT_AND_BREAK,
  5995.     
  5996. /* End Line Prohibited two byte text */
  5997.     /*    kEndProhibited,            kUnprohibited,            */    INC_RUN_LENGTH + SET_BREAKABLE,
  5998.     /*    kEndProhibited,            kBeginProhibited,        */    INC_RUN_LENGTH + SET_BREAKABLE,
  5999.     /*    kEndProhibited,            kEndProhibited,            */    INC_RUN_LENGTH,
  6000.     /*    kEndProhibited,            kWordBreakProhibited,    */    INC_RUN_LENGTH,
  6001.     /*    kEndProhibited,            kSingleByte,            */    INC_RUN_LENGTH,
  6002.     /*    kEndProhibited,            kBreakableSpace,        */    INC_RUN_LENGTH + MAINTAIN_CHAR_TYPE,    /* BIZZARE CASE! */
  6003.     /*    kEndProhibited,            kFlushFinalRun,            */    DUMP_TEXT_AND_BREAK,                    /* not much we can do here */
  6004.     
  6005. /* Word Break Prohibited two byte text */
  6006.     /*    kWordBreakProhibited,    kUnprohibited,            */    SET_BREAKABLE,
  6007.     /*    kWordBreakProhibited,    kBeginProhibited,        */    INC_RUN_LENGTH,
  6008.     /*    kWordBreakProhibited,    kEndProhibited,            */    SET_BREAKABLE,
  6009.     /*    kWordBreakProhibited,    kWordBreakProhibited,    */    INC_RUN_LENGTH,
  6010.     /*    kWordBreakProhibited,    kSingleByte,            */    INC_RUN_LENGTH,
  6011.     /*    kWordBreakProhibited,    kBreakableSpace,        */    SET_BREAKABLE + INSERT_WHITESPACE + SKIP_CHAR,
  6012.     /*    kWordBreakProhibited,    kFlushFinalRun,            */    DUMP_TEXT_AND_BREAK,
  6013.     
  6014. /* Single byte text */
  6015.     /*    kSingleByte,            kUnprohibited,            */    SET_BREAKABLE,
  6016.     /*    kSingleByte,            kBeginProhibited,        */    INC_RUN_LENGTH,
  6017.     /*    kSingleByte,            kEndProhibited,            */    SET_BREAKABLE,
  6018.     /*    kSingleByte,            kWordBreakProhibited,    */    SET_BREAKABLE,
  6019.     /*    kSingleByte,            kSingleByte,            */    INC_RUN_LENGTH,
  6020.     /*    kSingleByte,            kBreakableSpace,        */    SET_BREAKABLE + INSERT_WHITESPACE + SKIP_CHAR,
  6021.     /*    kSingleByte,            kFlushFinalRun,            */    DUMP_TEXT_AND_BREAK,
  6022.     
  6023. /* Single byte breakable space */
  6024.     /*    kBreakableSpace,        kUnprohibited,            */    0,
  6025.     /*    kBreakableSpace,        kBeginProhibited,        */    0,
  6026.     /*    kBreakableSpace,        kEndProhibited,            */    0,
  6027.     /*    kBreakableSpace,        kWordBreakProhibited,    */    0,
  6028.     /*    kBreakableSpace,        kSingleByte,            */    0,
  6029.     /*    kBreakableSpace,        kBreakableSpace,        */    SKIP_CHAR,
  6030.     /*    kBreakableSpace,        kFlushFinalRun,            */    0,
  6031.     
  6032. /* These are never hit */
  6033.     /*    kFlushFinalRun,            kUnprohibited,            */    0,
  6034.     /*    kFlushFinalRun,            kBeginProhibited,        */    0,
  6035.     /*    kFlushFinalRun,            kEndProhibited,            */    0,
  6036.     /*    kFlushFinalRun,            kWordBreakProhibited,    */    0,
  6037.     /*    kFlushFinalRun,            kSingleByte,            */    0,
  6038.     /*    kFlushFinalRun,            kBreakableSpace,        */    0,
  6039.     /*    kFlushFinalRun,            kFlushFinalRun,            */    0,
  6040. };
  6041.  
  6042. static void
  6043. lo_ParseDoubleText ( lo_DocState * state, LO_TextBlock * block, Bool parseAllText, char * text )
  6044. {
  6045.     char *            tptr;
  6046.     char *            wordStart;
  6047.     char *            nextWordStart;
  6048.     int32            runLength;
  6049.     int32            nextRunLength;
  6050.     int32            curCharBytes;
  6051.     int32            nextCharBytes;
  6052.     int16            charset;
  6053.     ParseState        curCharType;
  6054.     ParseState        nextCharType;
  6055.     uint8            parseCommand;
  6056.     uint32            textLength;
  6057.     Bool            startNewRun;
  6058.     Bool            eachCharBreakable;
  6059.     Bool            processLastRun;
  6060.     
  6061.     tptr = text;
  6062.     wordStart = text;
  6063.     runLength = 0;
  6064.     
  6065.     textLength = 0;
  6066.     
  6067.     processLastRun = FALSE;
  6068.     
  6069.     /* BRAIN DAMAGE: We need to see if there's anything in the line buffer for us */
  6070.     
  6071.     /* Make sure the text block has enough space to hold this block of text */
  6072.     textLength += XP_STRLEN ( text );
  6073.     lo_GrowTextBlock ( block, textLength + 1 );
  6074.  
  6075.     charset = block->text_attr->charset;
  6076.     curCharType = kSingleByte;
  6077.     curCharBytes = 1;
  6078.     
  6079.     eachCharBreakable = FALSE;
  6080.     
  6081.     /* if we have a trailing space, then set our state to be a space */
  6082.     if ( state->trailing_space )
  6083.         {
  6084.         curCharType = kBreakableSpace;
  6085.         runLength = curCharBytes;
  6086.         }
  6087.     
  6088.     startNewRun = FALSE;
  6089.         
  6090.     while ( ( *tptr != '\0' ) || processLastRun )
  6091.         {
  6092.         if ( processLastRun )
  6093.             {
  6094.             /* force the last run to be flushed */
  6095.             nextCharType = kFlushFinalRun;
  6096.             nextCharBytes = 0;
  6097.             }
  6098.         else
  6099.             {        
  6100.             /* do we have an ascii char? */
  6101.             if ( ( (unsigned char) *tptr ) <= 0x7F )
  6102.                 {
  6103.                 nextCharBytes = 1;
  6104.                 
  6105.                 /* is the next char a breakable space? */
  6106.                 if ( XP_IS_SPACE( *tptr ) )
  6107.                     {
  6108.                     nextCharType = kBreakableSpace;
  6109.                     }
  6110.                 else
  6111.                 /* it's a normal char */
  6112.                     {
  6113.                     nextCharType = kSingleByte;
  6114.                     }
  6115.                 }
  6116.             else
  6117.                 {
  6118.                 /* multibyte, do that international thing */
  6119.                 nextCharBytes = INTL_CharLen( charset, (unsigned char *) tptr);
  6120.                 nextCharType = (ParseState) INTL_KinsokuClass( charset, (unsigned char *) tptr );
  6121.                 }
  6122.             }
  6123.         
  6124.         /* now get our parse command */
  6125.         parseCommand = gParseTable[ curCharType ][ nextCharType ];
  6126.         nextRunLength = nextCharBytes;
  6127.         nextWordStart = tptr;
  6128.         
  6129.         /*
  6130.          * Unprohibited multibyte check - we need to catch cases where our byte size changes. We might
  6131.          * want to add a command bit for this check, for now I shall do it this way.
  6132.          */
  6133.         
  6134.         if ( ( curCharType == kUnprohibited ) && ( nextCharType == kUnprohibited ) )
  6135.             {
  6136.             if ( nextCharBytes != curCharBytes )
  6137.                 {
  6138.                 /* ok, our char size changed. we need to dump this run */
  6139.                 parseCommand |= SET_BREAKABLE;
  6140.                 }
  6141.             }
  6142.             
  6143.         /* process the command */
  6144.         if ( parseCommand & CARRY_LAST_CHAR )
  6145.             {
  6146.             /* move the last char of this run into the next run */
  6147.             runLength -= curCharBytes;
  6148.             nextWordStart -= curCharBytes;
  6149.             
  6150.             /* and move the carried char of the previous run to the next one */
  6151.             nextRunLength = curCharBytes + nextCharBytes;
  6152.             
  6153.             /* if the old run is empty, we don't want to do anything else  */
  6154.             if ( runLength == 0 )
  6155.                 {
  6156.                 parseCommand = 0;
  6157.                 startNewRun = TRUE;
  6158.                 }
  6159.             }
  6160.             
  6161.         if ( parseCommand & INC_RUN_LENGTH )
  6162.             {
  6163.             runLength += nextCharBytes;
  6164.             }
  6165.  
  6166.         if ( parseCommand & SET_BREAKABLE )
  6167.             {
  6168.             lo_CopyText ( (uint8 *) wordStart, &block->text_buffer[ block->buffer_write_index ], runLength );
  6169.             block->buffer_write_index += runLength;
  6170.             
  6171.             /* set a breakable run */
  6172.             lo_SetBreakPosition ( block );
  6173.                 
  6174.             startNewRun = TRUE;
  6175.             }
  6176.  
  6177.         if ( parseCommand & SET_MULTI_BREAKABLE )
  6178.             {
  6179.             lo_CopyText ( (uint8 *) wordStart, &block->text_buffer[ block->buffer_write_index ], runLength );
  6180.             block->buffer_write_index += runLength;
  6181.             
  6182.             /* set an unbreakable run */
  6183.             lo_SetMultiByteRun ( block, curCharBytes, TRUE, eachCharBreakable );
  6184.             startNewRun = TRUE;
  6185.             }
  6186.         
  6187.         if ( parseCommand & DUMP_TEXT_AND_BREAK )
  6188.             {
  6189.             /* copy the text out but don't set a break (only happens when flushing at the end) */
  6190.             lo_CopyText ( (uint8 *) wordStart, &block->text_buffer[ block->buffer_write_index ], runLength );
  6191.             block->buffer_write_index += runLength;
  6192.             
  6193.             /* now break out of the loop - we're done */
  6194.             break;
  6195.             }
  6196.             
  6197.         if ( startNewRun )
  6198.             {
  6199.             wordStart = nextWordStart;
  6200.             runLength = nextRunLength;
  6201.             startNewRun = FALSE;
  6202.             }
  6203.         
  6204.         if ( parseCommand & SKIP_CHAR )
  6205.             {
  6206.             wordStart++;
  6207.             }
  6208.         
  6209.         if ( parseCommand & INSERT_WHITESPACE )
  6210.             {
  6211.             block->text_buffer[ block->buffer_write_index ] = ' ';
  6212.             block->buffer_write_index++;
  6213.             block->last_buffer_write_index++;
  6214.             }
  6215.         
  6216.         curCharBytes = nextCharBytes;
  6217.         
  6218.         if ( !( parseCommand & MAINTAIN_CHAR_TYPE ) )
  6219.             {
  6220.             curCharType = nextCharType;
  6221.             }
  6222.             
  6223.         eachCharBreakable = curCharType == kUnprohibited;
  6224.         
  6225.         tptr += nextCharBytes;
  6226.         
  6227.         /* if we got here with processLastRun, then we need to bail */
  6228.         if ( processLastRun )
  6229.             {
  6230.             break;
  6231.             }
  6232.             
  6233.         /*
  6234.          * if we're on the last character, then we may have a partial run left over. if we're parsing all text
  6235.          * then we need to dump it.
  6236.          */
  6237.         if ( parseAllText && ( *tptr == 0 ) && ( runLength > 0 ) )
  6238.             {
  6239.             processLastRun = TRUE;
  6240.             }
  6241.         }
  6242.     
  6243.     /* if we've ended and we have a run, then we need to save it in the line buffer */
  6244.     if ( ( runLength > 0 ) && ( *wordStart != 0 ) )
  6245.         {
  6246.         lo_CopyTextToLineBuffer ( state,(uint8 *)  wordStart, runLength );
  6247.         }
  6248. }
  6249.  
  6250. static void
  6251. lo_FlushText ( MWContext * context, lo_DocState * state )
  6252. {
  6253.     LO_TextBlock *    block;
  6254.     Bool            multiByte;
  6255.     char *            text_buf;
  6256.     
  6257.     block = state->cur_text_block;
  6258.     
  6259.     if ( block != NULL )
  6260.         {
  6261.         lo_GetTextParseAtributes ( state, &multiByte );
  6262.         
  6263.         /* add any text to the text block that may be sitting in the line buffer */
  6264.         /* BRAIN DAMAGE: These should both be handled the same way */
  6265.         if ( multiByte )
  6266.             {
  6267.             if ( state->line_buf_len > 0 )
  6268.                 {
  6269.                 PA_LOCK(text_buf, char *, state->line_buf);
  6270.                 lo_ParseDoubleText ( state, block, TRUE, text_buf );
  6271.                 PA_UNLOCK(state->line_buf);
  6272.                 }
  6273.             }
  6274.         else
  6275.             {
  6276.             lo_AppendTextToBlock ( context, state, block, "" );
  6277.             }
  6278.             
  6279.         lo_LayoutTextBlock ( context, state, TRUE );
  6280.         }
  6281. }
  6282.  
  6283. static void
  6284. lo_SetupBreakState ( LO_TextBlock * block )
  6285. {
  6286.     Bool            canBreak;
  6287.     uint32            wordLength;
  6288.     uint32            lineLength;
  6289.     uint8 *            runEnd;
  6290.     uint32            searchReadIndex;
  6291.     BreakState        breakState;
  6292.     
  6293.     searchReadIndex = block->buffer_read_index;
  6294.     
  6295.     block->buffer_read_index = 0;
  6296.     block->break_read_index = 0;
  6297.     block->last_line_break = 0;
  6298.  
  6299.     /*
  6300.      * We need to update our state to be at the current text position (specified by buffer_read_index)
  6301.      */
  6302.     
  6303.     lineLength = 0;
  6304.  
  6305.     /* run through the break table until we get to the correct read index */
  6306.     while ( block->buffer_read_index < searchReadIndex )
  6307.         {
  6308.         SAVE_BREAK_STATE ( block, &breakState, lineLength );            
  6309.                 
  6310.         runEnd = lo_GetNextTextPosition ( block, &wordLength, &lineLength, &canBreak );
  6311.         if ( runEnd == NULL )
  6312.             {
  6313.             /* this should not happen, but just in case, let's do something kinda reasonable */
  6314.             lo_RestoreBreakState ( block, &breakState, NULL );
  6315.             break;
  6316.             }
  6317.         }
  6318.  
  6319.     /*
  6320.      * If we're not at the beginning of the text buffer, then we need to increment buffer_read_index so
  6321.      * that we skip the breakable space we're currently at.
  6322.      */
  6323.     if ( ( block->buffer_read_index > 0 ) && XP_IS_SPACE ( block->text_buffer[ block->buffer_read_index ] ) )
  6324.         {
  6325.         block->buffer_read_index++;
  6326.         }
  6327.         
  6328.     block->last_line_break = block->buffer_read_index;
  6329. }
  6330.  
  6331. void lo_LayoutTextBlock ( MWContext * context, lo_DocState * state, Bool flushLastLine )
  6332. {
  6333.     LO_TextBlock *    block;
  6334.     Bool            allTextFits;
  6335.     Bool            canBreakAtStart;
  6336.     LO_TextStruct *    text_data;
  6337.     LO_TextStruct    msTextData;
  6338.     uint32            width;
  6339.     uint32            lineLength;
  6340.     uint8 *            text;
  6341.     int32            baseline_inc;
  6342.     int32            line_inc;
  6343.     int32            minWidth;
  6344.     int32 *            minWidthPtr;
  6345.     BreakState        breakState;
  6346.     uint16 *        charLocs;
  6347.     Bool            freeMeasureBuffer;
  6348.     Bool            justify;
  6349.     
  6350.     block = state->cur_text_block;
  6351.     if ( block == NULL )
  6352.         {
  6353.         return;
  6354.         }
  6355.     
  6356.     justify = ( state->align_stack != NULL ) && ( state->align_stack->alignment == LO_ALIGN_JUSTIFY );
  6357.         
  6358.     /* bail if we're at the end of this block */
  6359.     if ( block->buffer_read_index == block->buffer_write_index )
  6360.         {
  6361.         /* if there's also no text in the line buffer, clear this block */
  6362.         if ( state->line_buf_len == 0 )
  6363.             {
  6364.             state->cur_ele_type = LO_NONE;
  6365.             state->cur_text_block = NULL;
  6366.             state->trailing_space = block->last_char_is_whitespace;
  6367.             }
  6368.         return;
  6369.         }
  6370.     
  6371.     lineLength = 0;
  6372.     
  6373.     charLocs = NULL;
  6374.     freeMeasureBuffer = FALSE;
  6375.     
  6376.     /* find the width of an average character */
  6377.     if ( block->totalWidth == 0 )
  6378.         {
  6379.         memset (&msTextData, 0, sizeof (LO_TextStruct));
  6380.         msTextData.text = (PA_Block) "aeiou";
  6381.         msTextData.text_attr = block->text_attr;
  6382.         msTextData.text_len = 5;
  6383.         
  6384.         FE_GetTextInfo ( context, &msTextData, &state->text_info );
  6385.         block->totalWidth = state->text_info.max_width;
  6386.         block->totalChars = msTextData.text_len;
  6387.         }
  6388.  
  6389. #ifdef XP_MAC
  6390.     /* do a quick test to see if we could overflow a UInt16 */
  6391.     if ( ( block->buffer_write_index * block->totalWidth / block->totalChars ) < 65535 )
  6392.         {
  6393.         /* measure the text using the fast measure text */
  6394.         if ( ( block->buffer_write_index + 1 ) < kStaticMeasureTextBufferSize )
  6395.             {
  6396.             charLocs = gMeasureTextBuffer;
  6397.             }
  6398.         else
  6399.             {
  6400.             charLocs = XP_ALLOC( ( block->buffer_write_index + 1 ) * sizeof(uint16) );
  6401.             freeMeasureBuffer = TRUE;
  6402.             }
  6403.             
  6404.         if ( charLocs != NULL )
  6405.             {
  6406.             memset (&msTextData, 0, sizeof (LO_TextStruct));
  6407.             msTextData.text = (PA_Block) block->text_buffer;
  6408.             msTextData.text_attr = block->text_attr;
  6409.             msTextData.text_len = block->buffer_write_index;
  6410.             FE_MeasureText ( context, &msTextData, (int16 *) charLocs );
  6411.             
  6412.             /* BRAIN DAMAGE: We need to figure out for sure if the width has ever exceeded a uint16! */
  6413.             /* the actual interface for MeasureText says it takes an array of signed ints, however */
  6414.             /* the data stuffed in there is unsigned (we quickly overflow a int16 with an 8Kb buffer) */
  6415.             }
  6416.         }
  6417. #endif
  6418.     
  6419.     /*
  6420.      * Given the current layout state, run through this text block and convert all that we can into text elements.
  6421.      * If flushLastLine is set, we want to flush all the text, rather than keeping the last bit in case more text
  6422.      * comes.
  6423.      */
  6424.     
  6425.     allTextFits = FALSE;
  6426.     
  6427.     /* if we're laying out a table, then we need to calculate min widths */
  6428.     minWidthPtr = state->need_min_width ? &minWidth : NULL;
  6429.     
  6430.     while ( !allTextFits )
  6431.         {
  6432.         canBreakAtStart = FALSE;
  6433.  
  6434.         /* Save the current break state in case we need to delay on the last line */
  6435.         SAVE_BREAK_STATE ( block, &breakState, lineLength );
  6436.                 
  6437.         /*
  6438.          * We need to handle the case where we're at the beginning of the line and the
  6439.          * first character is a breakable space.
  6440.          */
  6441.         if ( state->at_begin_line )
  6442.             {
  6443.             canBreakAtStart = lo_SkipInitialSpace ( block );
  6444. #ifdef LOG
  6445.             PR_LogPrint ( "canBreakAtStart: %d\n", canBreakAtStart );
  6446.             PR_LogFlush();
  6447. #endif
  6448.             }
  6449.         
  6450.         /* we're looking for a new break position */
  6451.         state->break_pos = -1;
  6452.         state->break_width = -1;
  6453.  
  6454.         text = lo_GetLineStart ( block );
  6455.                 
  6456.         lineLength = lo_FindLineBreak ( context, state, block, text, charLocs, &width, minWidthPtr, &allTextFits );
  6457.  
  6458.         /* update the state's min_width if we need to - this will be constant even if we don't flush this line */
  6459.         if ( minWidthPtr != NULL )
  6460.             {
  6461.             if ( minWidth > state->min_width )
  6462.                 {
  6463.                 state->min_width = minWidth;
  6464.                 }
  6465.             }
  6466.         
  6467.         /*
  6468.          * if this line is too long, we either need to break at the beginning of the run (if we can)
  6469.          * or break at an old element. If we can't do either of those then we just have to make the line
  6470.          * too big
  6471.          */
  6472.         
  6473.         if ( ( width > ( state->right_margin - state->x ) ) && ( state->x > state->left_margin ) )
  6474.             {
  6475.             /* could we have broken at the start of this line? */
  6476.             if ( canBreakAtStart )
  6477.                 {
  6478. #ifdef LOG
  6479.                 PR_LogPrint ( "Too long - Breaking at the start of the line\n" );
  6480.                 PR_LogFlush();
  6481. #endif
  6482.  
  6483.                 /* break the line here */
  6484.                 lo_SoftLineBreak( context, state, TRUE );
  6485.                 
  6486.                 /*
  6487.                  * BUG BUG: We're restoring the break state to the beginning of the buffer - ie
  6488.                  * to before the space we skipped above. We need to fix the space skipping mechanism
  6489.                  * to remove this case (we can go into an infinite loop here if there's not enough space
  6490.                  * for the first word).
  6491.                  *
  6492.                  * Should be able to have a new flag "canSkipSpace" but then actually don't skip it. Then
  6493.                  * if the line does fit and it's at the beginning, we can skip the space. lo_FindLineBreak
  6494.                  * should probably be the one to do this work so that the width we get back is correct.
  6495.                  */
  6496.                 lo_RestoreBreakState ( block, &breakState, NULL );
  6497.                 
  6498.                 /*
  6499.                  * if all the text fits (ie there was only an unbreakable run left in this block), then
  6500.                  * we need to stick with this break position. Otherwise we can go find a new one
  6501.                  */
  6502.                 if ( !allTextFits )
  6503.                     {
  6504.                     continue;
  6505.                     }
  6506.                 }
  6507.             else
  6508.             /* do we have an old break position we can use? */
  6509.             if ( state->old_break_pos != -1 )
  6510.                 {
  6511. #ifdef LOG
  6512.                 PR_LogPrint ( "Too long - Breaking at the old_break_pos: %ld, width %ld\n"
  6513.                     state->old_break_pos, state->old_break_width );
  6514.                 PR_LogFlush();
  6515. #endif
  6516.                 lo_BreakOldElement ( context, state );
  6517.                 lo_RestoreBreakState ( block, &breakState, NULL );
  6518.                 
  6519.                 /*
  6520.                  * if all the text fits (ie there was only an unbreakable run left in this block), then
  6521.                  * we need to stick with this break position. Otherwise we can go find a new one
  6522.                  */
  6523.                 if ( !allTextFits )
  6524.                     {
  6525.                     continue;
  6526.                     }
  6527.                 }
  6528.             
  6529.             /* we're screwed, we just have to make this line too long */
  6530.             }
  6531.                         
  6532.         /*
  6533.          * We may not necessarily want to flush the whole buffer out to layout elements (the case where were
  6534.          * processing part of a text chunk based on what netlib has streamed to us).
  6535.          *
  6536.          * We know we do want to flush this next line out if we still have more text in this buffer to process
  6537.          * or layout really does want us to flush this whole buffer (because some other element is after us).
  6538.          */
  6539.         if ( !allTextFits || flushLastLine )
  6540.             {
  6541.             state->width = width;
  6542.  
  6543.             if ( lineLength > 0 )
  6544.                 {
  6545.                 text_data = (LO_TextStruct *)lo_NewElement ( context, state, LO_TEXT, NULL, 0 );
  6546.                 if (text_data == NULL)
  6547.                     {
  6548. #ifdef DEBUG
  6549.                     assert (state->top_state->out_of_memory);
  6550. #endif
  6551.                     break;
  6552.                     }
  6553.  
  6554.                 text_data->type = LO_TEXT;
  6555.                 text_data->ele_id = NEXT_ELEMENT;
  6556.                 text_data->x = state->x;
  6557.                 text_data->x_offset = 0;
  6558.                 text_data->y = state->y;
  6559.                 text_data->y_offset = 0;
  6560.                 text_data->width = width;
  6561.                 text_data->height = 0;
  6562.                 text_data->next = NULL;
  6563.                 text_data->prev = NULL;
  6564.  
  6565.                 text_data->text = (PA_Block) text;
  6566.                 text_data->text_len = lineLength;
  6567.  
  6568.                 text_data->anchor_href = block->anchor_href;
  6569.                 text_data->text_attr = block->text_attr;
  6570.                 text_data->ele_attrmask = block->ele_attrmask;
  6571.                 
  6572.                 /* BRAIN DAMAGE: Set LO_ELE_INVISIBLE to mark the element as not having a valid text ptr */
  6573.                 XP_ASSERT ( !(text_data->ele_attrmask & LO_ELE_INVISIBLE ) );
  6574.                 text_data->ele_attrmask |= LO_ELE_INVISIBLE;
  6575.                 
  6576.                 text_data->sel_start = -1;
  6577.                 text_data->sel_end = -1;
  6578.  
  6579.                 text_data->doc_width = state->right_margin - state->x;
  6580.                 text_data->doc_width = 0;
  6581.                 text_data->block_offset = block->buffer_read_index;
  6582.                 XP_ASSERT(block->buffer_read_index <= 65535);
  6583.                     
  6584.                 /*
  6585.                  * Some fonts (particulatly italic ones with curly tails
  6586.                  * on letters like 'f') have a left bearing that extends
  6587.                  * back into the previous character.  Since in this case the
  6588.                  * previous character is probably not in the same font, we
  6589.                  * move forward to avoid overlap.
  6590.                  *
  6591.                  * Those same funny fonts can extend past the last character,
  6592.                  * and we also have to catch that, and advance the following text
  6593.                  * to eliminate cutoff.
  6594.                  */
  6595.                 if ( state->text_info.lbearing < 0 )
  6596.                     {
  6597.                     text_data->x_offset = state->text_info.lbearing * -1;
  6598.                     }
  6599.                     
  6600.                 baseline_inc = 0;
  6601.                 line_inc = 0;
  6602.                 
  6603.                 /*
  6604.                  * The baseline of the text element just added to the line may be
  6605.                  * less than or greater than the baseline of the rest of the line
  6606.                  * due to font changes.  If the baseline is less, this is easy,
  6607.                  * we just increase y_offest to move the text down so the baselines
  6608.                  * line up.  For greater baselines, we can't move the text up to
  6609.                  * line up the baselines because we will overlay the previous line,
  6610.                  * so we have to move all the previous elements in this line down.
  6611.                  *
  6612.                  * If the baseline is zero, we are the first element on the line,
  6613.                  * and we get to set the baseline.
  6614.                  */
  6615.                 if ( state->baseline == 0 )
  6616.                     {
  6617.                     state->baseline = state->text_info.ascent;
  6618.                     if (state->line_height < 
  6619.                         (state->baseline + state->text_info.descent))
  6620.                         {
  6621.                         state->line_height = state->baseline +
  6622.                             state->text_info.descent;
  6623.                         }
  6624.                     }
  6625.                 else if ( state->text_info.ascent < state->baseline )
  6626.                     {
  6627.                     text_data->y_offset = state->baseline - state->text_info.ascent;
  6628.                     if ( ( text_data->y_offset + state->text_info.ascent + state->text_info.descent ) > state->line_height )
  6629.                         {
  6630.                         line_inc = text_data->y_offset + state->text_info.ascent + state->text_info.descent -
  6631.                                     state->line_height;
  6632.                         }
  6633.                     }
  6634.                 else
  6635.                     {
  6636.                     baseline_inc = state->text_info.ascent - state->baseline;
  6637.                     if ( ( text_data->y_offset + state->text_info.ascent + state->text_info.descent - baseline_inc ) >
  6638.                             state->line_height)
  6639.                         {
  6640.                         line_inc = text_data->y_offset + state->text_info.ascent + state->text_info.descent -
  6641.                                     state->line_height - baseline_inc;
  6642.                         }
  6643.                     }
  6644.                 
  6645.                 /*
  6646.                  * Append this element to layout's linelist and our own list of text elements belonging to this block
  6647.                  */
  6648.                 lo_AppendToLineList ( context, state, (LO_Element *) text_data, baseline_inc );
  6649.                 if ( block->startTextElement == NULL )
  6650.                     {
  6651.                     block->startTextElement = text_data;
  6652.                     block->endTextElement = text_data;
  6653.                     }
  6654.                 else
  6655.                     {
  6656.                     block->endTextElement = text_data;
  6657.                     }
  6658.                 
  6659.                 /* we know we're not at the beginning of the line anymore */
  6660.                 state->at_begin_line = FALSE;
  6661.                 
  6662.                 state->baseline += (intn) baseline_inc;
  6663.                 state->line_height += (intn) (baseline_inc + line_inc);
  6664.                 text_data->height = state->text_info.ascent + state->text_info.descent;
  6665.  
  6666.                 /*
  6667.                  * If the element we just flushed had a breakable word
  6668.                  * position in it, save that position in case we have
  6669.                  * to go back and break this element before we finish
  6670.                  * the line.
  6671.                  */
  6672.                 if ( state->break_pos != -1 )
  6673.                     {
  6674.                     state->old_break = text_data;
  6675.                     state->old_break_block = block;
  6676.                     state->old_break_pos = state->break_pos;
  6677.                     state->old_break_width = state->break_width;
  6678.                     }
  6679.  
  6680.                 state->linefeed_state = 0;
  6681.                 state->x += state->width;
  6682.                 state->width = 0;
  6683.                 }
  6684.                 
  6685.             /*
  6686.              * If we're still processing text in this buffer, put a linebreak out there
  6687.              */
  6688.             if ( !allTextFits && !justify )
  6689.                 {
  6690.                 lo_SoftLineBreak(context, state, TRUE);
  6691.                 }
  6692.             
  6693.             /* tell the break engine that we broke the line here */
  6694.             lo_SetLineBreak ( block, !justify );
  6695.  
  6696.  
  6697. #ifdef EDITOR
  6698.             /* tell the editor where we are */
  6699.             state->edit_current_offset = block->last_line_break;
  6700. #endif
  6701.                 
  6702.             if ( !( allTextFits && !flushLastLine ) )
  6703.                 {
  6704.                 /*
  6705.                  * Skip the break character if it's whitespace. We don't need to worry about non-breaking spaces
  6706.                  * here as if the space was non-breaking, we would not have broken the line here
  6707.                  */
  6708.  
  6709.                 if ( XP_IS_SPACE ( *text ) && !allTextFits )
  6710.                     {
  6711.                     /* BRAIN DAMAGE: We should be able to do this at the start of the line */
  6712. /*                    lo_SkipCharacter ( block ); */
  6713.                     }
  6714.                 }
  6715.             
  6716.             }
  6717.         }
  6718.     
  6719.     if ( flushLastLine )
  6720.         {
  6721.         state->cur_ele_type = LO_NONE;
  6722.         state->cur_text_block = NULL;
  6723.         state->trailing_space = block->last_char_is_whitespace;
  6724.         }
  6725.     else
  6726.     if ( allTextFits )
  6727.         {
  6728.         /* we're still inside a group of text elements */
  6729.         state->cur_ele_type = LO_TEXT;
  6730.         state->trailing_space = block->last_char_is_whitespace;
  6731.         
  6732.         /* We're not going to flush the last line, so restore our break state to the start of the line */
  6733.         lo_RestoreBreakState ( block, &breakState, NULL );
  6734.         }
  6735.     
  6736.     if ( freeMeasureBuffer )
  6737.         {
  6738.         XP_FREE( charLocs );
  6739.         }
  6740. }
  6741.  
  6742. int32 lo_ComputeTextMinWidth ( lo_DocState * state, int32 wordWidth, Bool canBreak );
  6743. int32 lo_ComputeTextMinWidth ( lo_DocState * state, int32 wordWidth, Bool canBreak )
  6744. {
  6745.     int32 new_break_holder;
  6746.     int32 min_width;
  6747.     int32 indent;
  6748.  
  6749.     new_break_holder = state->x + wordWidth;
  6750.     min_width = new_break_holder - state->break_holder;
  6751.     indent = state->list_stack->old_left_margin - state->win_left;
  6752.     min_width += indent;
  6753.  
  6754.     /* If we are not within <NOBR> content, allow break_holder
  6755.      * to be set to the new position where a line break can occur.
  6756.      * This fixes BUG #70782
  6757.      */
  6758.     if ( ( state->breakable != FALSE ) && canBreak) {
  6759.         state->break_holder = new_break_holder;
  6760.     }
  6761.     
  6762.     return min_width;
  6763. }
  6764.  
  6765. static uint32
  6766. lo_FindLineBreak ( MWContext * context, lo_DocState * state, LO_TextBlock * block, uint8 * text,
  6767.     uint16 * widthTable, uint32 * width, int32 * minWidth, Bool * allTextFits )
  6768. {
  6769.     LO_TextStruct    text_data;
  6770.     uint32            breakCount;
  6771.     Bool            skipEndSpace;
  6772.     Bool            haveTooShort;
  6773.     Bool            canBreak;
  6774.     BreakState        tooShortBreak;
  6775.     Bool            haveTooLong;
  6776.     uint32            wordLength;
  6777.     uint32            breakChar;
  6778.     uint32            lineLength;
  6779.     uint32            prevLineLength;
  6780.     uint8 *            wordStart;
  6781.     LO_TextInfo        text_info;
  6782.     uint32            runLength;
  6783.     int32            docWidth;
  6784.     BreakState        breakState;
  6785.     uint8 *            runEnd;
  6786.     int32            oldBreakPos;
  6787.     int32            oldBreakWidth;
  6788.     int32            lineWidth;
  6789.     Bool            justify;
  6790. #ifdef BREAK_GUESS_TRACK
  6791.     uint32            numForwardMoves;
  6792.     uint32            numBackwardMoves;
  6793.     
  6794.     numForwardMoves = 0;
  6795.     numBackwardMoves = 0;
  6796. #endif
  6797.     
  6798.     *allTextFits = FALSE;
  6799.     
  6800.     memset (&text_data, 0, sizeof (LO_TextStruct));
  6801.     text_data.text = (PA_Block) text;
  6802.     text_data.text_attr = block->text_attr;
  6803.     
  6804.     if ( minWidth != NULL )
  6805.         {
  6806.         *minWidth = 0;
  6807.         }
  6808.  
  6809.     justify = ( state->align_stack != NULL ) && ( state->align_stack->alignment == LO_ALIGN_JUSTIFY );
  6810.     
  6811.     /* guess where we want to break this line */
  6812.     docWidth = state->right_margin - state->x;
  6813.     if ( docWidth < 0 )
  6814.         {
  6815.         /* we should never get here - the line before us needs to have been broken */
  6816.         docWidth = 0;
  6817.         }
  6818.     
  6819.     lineLength = block->buffer_write_index - block->buffer_read_index;
  6820.     if ( state->breakable )
  6821.         {
  6822.         breakChar = docWidth * block->totalChars / block->totalWidth;
  6823.         if ( breakChar > lineLength )
  6824.             {
  6825.             breakChar = lineLength;
  6826.             }
  6827.         }
  6828.     else
  6829.         {
  6830.         breakChar = lineLength;
  6831.         }
  6832.     
  6833.     /*
  6834.      * We first need to walk through the word runs until we get to the first one before our
  6835.      * break character.
  6836.      *
  6837.      * OPTIMIZATION: Make lo_GetNextTextPosition take a breakChar and have it walk forward to that
  6838.      * position in an inner loop. This will save us a ton of calls (we currently spend about 5% if our
  6839.      * time in lo_GetNextTextPosition - not huge but significant).
  6840.      */
  6841.  
  6842.     lineLength = 0;
  6843.     
  6844.     breakCount = 0;
  6845.     skipEndSpace = FALSE;
  6846.     
  6847.     wordStart = text;
  6848.     
  6849.     haveTooShort = FALSE;
  6850.     haveTooLong = FALSE;
  6851.  
  6852.     oldBreakPos = -1;
  6853.     oldBreakWidth = -1;
  6854.     
  6855.     lineWidth = 0;
  6856. #ifdef LOG
  6857.     if ( !gHaveLog )
  6858.         {
  6859.         PR_SetLogFile ( "TextLog" );
  6860.         gHaveLog = true;
  6861.         }
  6862.  
  6863.     PR_LogPrint ( "Finding initial break position\n" );
  6864. #endif
  6865.  
  6866.     /* get the next break position */
  6867.     while ( TRUE )
  6868.         {
  6869.         prevLineLength = lineLength;
  6870.         
  6871. #ifdef LOG
  6872.         PR_LogPrint ( "Get next break position\n" );
  6873.         PR_LogFlush();
  6874. #endif
  6875.         /* Save the current break position in case it ends up being the one we need */
  6876.         if ( breakCount > 0 && canBreak )
  6877.             {
  6878.             oldBreakPos = lineLength;
  6879.             }
  6880.         
  6881.         SAVE_BREAK_STATE ( block, &breakState, lineLength );            
  6882.         runEnd = lo_GetNextTextPosition ( block, &wordLength, &lineLength, &canBreak );
  6883.         if ( runEnd == NULL )
  6884.             {
  6885. #ifdef LOG
  6886.             PR_LogPrint ( "End of break table\n" );
  6887.             PR_LogFlush();
  6888. #endif
  6889.             
  6890.             /* we hit the end of the break table */
  6891.             runEnd = lo_RestoreBreakState ( block, &breakState, &lineLength );
  6892.             break;
  6893.             }
  6894.  
  6895.         /* do we need to calculate min_width's? */
  6896.         if ( minWidth != NULL )
  6897.             {
  6898.             int32 min_width;
  6899.             
  6900.             if ( widthTable != NULL )
  6901.                 {
  6902.                 uint32    startWordWidth;
  6903.                 uint32    endWordWidth;
  6904.                 
  6905.                 startWordWidth = widthTable[ block->last_line_break + prevLineLength ];
  6906.                 endWordWidth = widthTable[ block->last_line_break + lineLength ];
  6907.                 
  6908.                 runLength = endWordWidth - startWordWidth;
  6909.                 }
  6910.             else
  6911.                 {
  6912.                 text_data.text = (PA_Block) wordStart;
  6913.                 text_data.text_len = lineLength - prevLineLength;
  6914.                 FE_GetTextInfo ( context, &text_data, &text_info );
  6915.                 
  6916.                 runLength = text_info.max_width;
  6917.                 }
  6918.             
  6919.             /* add the width of this word into our line width */
  6920.             lineWidth += runLength;
  6921.             
  6922.             /* compute the real min width based on the last break position */
  6923.             min_width = lo_ComputeTextMinWidth ( state, lineWidth, canBreak );
  6924.             if ( min_width > *minWidth )
  6925.                 {
  6926.                 *minWidth = min_width;
  6927.                 }
  6928.             
  6929.             wordStart = runEnd;
  6930.             }
  6931.  
  6932. #ifdef LOG
  6933.         PR_LogPrint ( "wordlen: %lu, lineLength: %lu, canBreak: %d, runEnd: %s\n", wordLength, lineLength, canBreak, runEnd );
  6934.         PR_LogFlush();
  6935. #endif
  6936.         
  6937.         /* Are we where we want to be yet? */
  6938.         if ( lineLength >= breakChar )
  6939.             {
  6940. #ifdef LOG
  6941.             PR_LogPrint ( "Moved past, back up\n" );
  6942.             PR_LogFlush();
  6943. #endif
  6944.             
  6945.             /* if we moved past it then back up if we can */
  6946.             if ( ( lineLength > breakChar ) && ( breakCount > 0 ) )
  6947.                 {
  6948.                 runEnd = lo_RestoreBreakState ( block, &breakState, &lineLength );            
  6949.                 --breakCount;
  6950.                 }
  6951.                 
  6952.             break;
  6953.             }
  6954.         
  6955.         /* if we're justifying text, then we just dump the next break position */
  6956.         if ( justify )
  6957.             {
  6958.             break;
  6959.             }
  6960.             
  6961.         /* we can now back up to something */
  6962.         ++breakCount;
  6963.         }
  6964.  
  6965.     /*
  6966.      * So now we're looking at the nearest break position to where we guessed we'd want to be.
  6967.      * Now we loop measuring this line of text until we find the best break position
  6968.      */
  6969.     
  6970. #ifdef LOG
  6971.     PR_LogPrint ( "Finding actual break position\n" );
  6972.     PR_LogFlush();
  6973. #endif
  6974.     
  6975.     while ( TRUE )
  6976.         {
  6977.         if ( widthTable != NULL )
  6978.             {
  6979.             uint32    startLineWidth;
  6980.             uint32    endLineWidth;
  6981.             
  6982.             startLineWidth = widthTable[ block->last_line_break ];
  6983.             endLineWidth = widthTable[ block->last_line_break + lineLength ];
  6984.             
  6985.             runLength = endLineWidth - startLineWidth;
  6986.             }
  6987.         else
  6988.             {
  6989.             text_data.text = (PA_Block) text;
  6990.             text_data.text_len = lineLength;
  6991.             FE_GetTextInfo ( context, &text_data, &text_info );
  6992.                 
  6993.             runLength = text_info.max_width;
  6994.             }
  6995.  
  6996. #ifdef LOG
  6997.         PR_LogPrint ( "lineLength: %lu, width: %lu, docWidth: %lu, text: %s\n", lineLength, runLength, docWidth, text );
  6998.         PR_LogFlush();
  6999. #endif
  7000.         /* if we're justified text, then we just dump the word we have now */
  7001.         if ( justify )
  7002.             {
  7003.             /* save this break position */
  7004.             if ( canBreak )
  7005.                 {
  7006.                 oldBreakPos = lineLength;
  7007.                 oldBreakWidth = runLength;
  7008.                 
  7009.                 /* if the next char along is a space, then we need to include it */
  7010.                 if ( ( runEnd != NULL ) && XP_IS_SPACE ( *runEnd )  )
  7011.                     {
  7012.                     ++lineLength;
  7013.                     }
  7014.                 }
  7015.                 
  7016.             break;
  7017.             }
  7018.         else
  7019.         /* are we non-breakable text? */
  7020.         if ( !state->breakable )
  7021.             {
  7022.             /*
  7023.              * We always want to move to the end of the text block. We should already be
  7024.              * there from the loop above.
  7025.              */
  7026. #ifdef LOG
  7027.             PR_LogPrint ( "Non-breakable, always get next break point\n" );
  7028.             PR_LogFlush();
  7029. #endif
  7030.             
  7031.             /* go forward one break position and measure again (including min width) */
  7032.             SAVE_BREAK_STATE ( block, &tooShortBreak, lineLength );
  7033.             haveTooShort = TRUE;
  7034.             
  7035. #ifdef BREAK_GUESS_TRACK
  7036.             ++numForwardMoves;
  7037. #endif            
  7038.             wordStart = runEnd;
  7039.             runEnd = lo_GetNextTextPosition ( block, &wordLength, &lineLength, &canBreak );
  7040.             if ( runEnd == NULL )
  7041.                 {                
  7042.                 /* we've already at the end of the line */
  7043.                 runEnd = lo_RestoreBreakState ( block, &tooShortBreak, &lineLength );
  7044. #ifdef LOG
  7045.                 PR_LogPrint ( "Non-breakable text, hit end of block: %lu, runEnd: %s\n", lineLength, runEnd );
  7046.                 PR_LogFlush();
  7047. #endif
  7048.                 /* update min_width */
  7049.                 if ( minWidth != NULL )
  7050.                     {
  7051.                     int32 min_width;
  7052.                                         
  7053.                     /* compute the real min width based on the last break position - we cannot break here */
  7054.                     min_width = lo_ComputeTextMinWidth ( state, runLength, FALSE );
  7055.                     if ( min_width > *minWidth )
  7056.                         {
  7057.                         *minWidth = min_width;
  7058.                         }
  7059.                     }
  7060.                 break;
  7061.                 }
  7062.             }
  7063.         else
  7064.         /* have we gone too far? */
  7065.         if ( runLength > docWidth )
  7066.             {
  7067. #ifdef LOG
  7068.             PR_LogPrint ( "Too long\n" );
  7069.             PR_LogFlush();
  7070. #endif
  7071.             
  7072.             /* if we found a break position before this one that was too short, choose the too short one */
  7073.             if ( haveTooShort )
  7074.                 {
  7075. #ifdef LOG
  7076.                 PR_LogPrint ( "Using too short\n" );
  7077.                 PR_LogFlush();
  7078. #endif
  7079.                 runEnd = lo_RestoreBreakState ( block, &tooShortBreak, &lineLength );
  7080.                 break;
  7081.                 }
  7082.             
  7083.             /* if we have something to back up to, then do so. Otherwise we have to break here */
  7084.             if ( breakCount > 0 )
  7085.                 {
  7086. #ifdef LOG
  7087.                 PR_LogPrint ( "Backing up to previous break position\n" );
  7088.                 PR_LogFlush();
  7089. #endif
  7090. #ifdef BREAK_GUESS_TRACK
  7091.                 ++numBackwardMoves;
  7092. #endif            
  7093.  
  7094.                 /* mark that we've been to far and back up one */
  7095.                 haveTooLong = TRUE;
  7096.                 runEnd = lo_GetPrevTextPosition ( block, &wordLength, &lineLength, &canBreak );
  7097.                 if ( runEnd == NULL )
  7098.                     {
  7099.                     /* we need to break at a previous break position on this line... */
  7100.                     break;
  7101.                     }
  7102.                     
  7103.                 wordStart = runEnd;
  7104.                 --breakCount;
  7105.                 continue;
  7106.                 }
  7107.             else
  7108.                 {
  7109. #ifdef LOG
  7110.                 PR_LogPrint ( "Nothing to back up to, bailing\n" );
  7111.                 PR_LogFlush();
  7112. #endif
  7113.                 break;
  7114.                 }
  7115.             }
  7116.         else
  7117.         /* have we not gone far enough? */
  7118.         if ( runLength < docWidth )
  7119.             {
  7120. #ifdef LOG
  7121.             PR_LogPrint ( "Too short\n" );
  7122.             PR_LogFlush();
  7123. #endif
  7124.             /* if we have a too long break position, then we know we're straddling the break point, choose */
  7125.             /* this one */
  7126.             if ( haveTooLong )
  7127.                 {
  7128. #ifdef LOG
  7129.                 PR_LogPrint ( "Using this break, next is too long\n" );
  7130.                 PR_LogFlush();
  7131. #endif
  7132.                 break;
  7133.                 }
  7134.  
  7135.             /* save this break position */
  7136.             if ( canBreak )
  7137.                 {
  7138.                 oldBreakPos = lineLength;
  7139.                 oldBreakWidth = runLength;
  7140.                 }
  7141.             
  7142.             /* go forward one break position and measure again (including min width) */
  7143.             SAVE_BREAK_STATE ( block, &tooShortBreak, lineLength );
  7144.             haveTooShort = TRUE;
  7145.             
  7146. #ifdef BREAK_GUESS_TRACK
  7147.             ++numForwardMoves;
  7148. #endif            
  7149.             
  7150.             wordStart = runEnd;
  7151.             runEnd = lo_GetNextTextPosition ( block, &wordLength, &lineLength, &canBreak );
  7152.             if ( runEnd == NULL )
  7153.                 {                
  7154.                 /* we've already at the end of the line */
  7155.                 runEnd = lo_RestoreBreakState ( block, &tooShortBreak, &lineLength );
  7156. #ifdef LOG
  7157.                 PR_LogPrint ( "No next break position, use this one. length: %lu, runEnd: %s\n", lineLength, runEnd );
  7158.                 PR_LogFlush();
  7159. #endif
  7160.                 break;
  7161.                 }
  7162.             
  7163.             /*
  7164.              * Update min width.
  7165.              */
  7166.             if ( minWidth != NULL )
  7167.                 {
  7168.                 int32 min_width;
  7169.  
  7170.                 if ( widthTable != NULL )
  7171.                     {
  7172.                     uint32    startLineWidth;
  7173.                     uint32    endLineWidth;
  7174.                     
  7175.                     startLineWidth = widthTable[ block->last_line_break ];
  7176.                     endLineWidth = widthTable[ block->last_line_break + lineLength ];
  7177.                     
  7178.                     runLength = endLineWidth - startLineWidth;
  7179.                     }
  7180.                 else
  7181.                     {
  7182.                     text_data.text = (PA_Block) wordStart;
  7183.                     text_data.text_len = wordLength;
  7184.                     FE_GetTextInfo ( context, &text_data, &text_info );
  7185.                     
  7186.                     /* add the length of this word into the length of the line */
  7187.                     runLength += text_info.max_width;
  7188.                     }
  7189.                                     
  7190.                 /* compute the real min width based on the last break position */
  7191.                 min_width = lo_ComputeTextMinWidth ( state, runLength, canBreak );
  7192.                 if ( min_width > *minWidth )
  7193.                     {
  7194.                     *minWidth = min_width;
  7195.                     }
  7196.                 }
  7197.                 
  7198.             ++breakCount;
  7199.             continue;
  7200.             }
  7201.         else
  7202.             {
  7203.             /* we're spot on! */
  7204.             break;
  7205.             }
  7206.         }
  7207.     
  7208.     /*
  7209.      * We may be in a nasty case where our current break position is before our
  7210.      * last saved one in oldBreakPos. This can happen when we move backwards
  7211.      * from our initial break guess. To correct this, we need to back up from our
  7212.      * current break point, get the new position and then move forward again.
  7213.      */
  7214.     if ( ( oldBreakPos != -1 ) && ( oldBreakPos >= lineLength ) )
  7215.         {
  7216.         uint32    dummyWordLength;
  7217.         uint32    prevBreakPos;
  7218.         Bool    dummyCanBreak;
  7219.         uint8 *    prevRunEnd;
  7220.             
  7221. #ifdef BREAK_GUESS_TRACK
  7222.             ++numBackwardMoves;
  7223. #endif            
  7224.         prevBreakPos = lineLength;
  7225.         
  7226.         SAVE_BREAK_STATE ( block, &breakState, prevBreakPos );
  7227.         prevRunEnd = lo_GetPrevTextPosition ( block, &dummyWordLength, &prevBreakPos, &dummyCanBreak );
  7228.         if ( prevRunEnd != NULL )
  7229.             {
  7230.             /* we found a valid previous break, so use it */
  7231.             oldBreakPos = prevBreakPos;
  7232.             oldBreakWidth = -1;
  7233.             }
  7234.         else
  7235.             {
  7236.             /* nothing to back up to, so don't record any old break */
  7237.             oldBreakPos = -1;
  7238.             oldBreakWidth = -1;
  7239.             }
  7240.         
  7241.         /* restore the current break state */
  7242.         lo_RestoreBreakState ( block, &breakState, &prevBreakPos );
  7243.         }
  7244.             
  7245.     text_data.text = (PA_Block) text;
  7246.  
  7247.     /* If we don't have a width for the oldBreakPos, measure one now */
  7248.     if ( ( oldBreakPos != -1 ) && ( oldBreakWidth == -1 ) )
  7249.         {
  7250.         if ( widthTable != NULL )
  7251.             {
  7252.             uint32    startLineWidth;
  7253.             uint32    endLineWidth;
  7254.             
  7255.             startLineWidth = widthTable[ block->last_line_break ];
  7256.             endLineWidth = widthTable[ block->last_line_break + oldBreakPos ];
  7257.             
  7258.             oldBreakWidth = endLineWidth - startLineWidth;
  7259.             }
  7260.         else
  7261.             {
  7262.             text_data.text_len = oldBreakPos;
  7263.             FE_GetTextInfo ( context, &text_data, &text_info );
  7264.             oldBreakWidth = text_info.max_width;
  7265.             }
  7266.         }
  7267.     
  7268.     text_data.text_len = lineLength;
  7269.     
  7270.     /* if we're breaking at a space at the end of this line, don't measure it */
  7271.     if ( skipEndSpace )
  7272.         {
  7273.         --text_data.text_len;
  7274.         }
  7275.     
  7276.     /* BRAIN DAMAGE: We don't need this - already got all the info */
  7277.     if ( widthTable != NULL )
  7278.         {
  7279.         uint32    startLineWidth;
  7280.         uint32    endLineWidth;
  7281.         
  7282.         /* this is really lame */
  7283.         text_data.text = (PA_Block) text;
  7284.         text_data.text_len = 1;
  7285.         FE_GetTextInfo ( context, &text_data, &text_info );
  7286.         
  7287.         startLineWidth = widthTable[ block->last_line_break ];
  7288.         endLineWidth = widthTable[ block->last_line_break + lineLength ];
  7289.         
  7290.         text_info.max_width = endLineWidth - startLineWidth;
  7291.         }
  7292.     else
  7293.         {
  7294.         text_data.text = (PA_Block) text;
  7295.         text_data.text_len = lineLength;
  7296.         FE_GetTextInfo ( context, &text_data, &text_info );
  7297.         }
  7298.     
  7299.     /* update our char width average */
  7300.     block->totalWidth += text_info.max_width;
  7301.     block->totalChars += lineLength;
  7302.     
  7303.     *width = lo_correct_text_element_width( &text_info );
  7304.     
  7305.     /* BRAIN DAMAGE: Pass this into the FE call */
  7306.     state->text_info = text_info;
  7307.     
  7308.     /* check to see if we're at the end of the buffer */
  7309.     if ( block->buffer_read_index == block->buffer_write_index )
  7310.         {
  7311.         *allTextFits = TRUE;
  7312.         }
  7313.     
  7314.     /* save the last break position */
  7315.     if ( oldBreakPos != -1 )
  7316.         {
  7317.         state->break_pos = oldBreakPos;
  7318.         state->break_width = oldBreakWidth;
  7319.         }
  7320.     
  7321. #ifdef LOG
  7322.     PR_LogPrint ( "Final lineLength: %lu, allTextFits: %d, text: %s\n", text_data.text_len, *allTextFits, text );
  7323.     PR_LogFlush();
  7324. #endif
  7325.  
  7326. #ifdef BREAK_GUESS_TRACK
  7327.     XP_TRACE(("Num forward, backward break moves after initial guess: %ld, %ld", numForwardMoves, numBackwardMoves ));
  7328. #endif            
  7329.  
  7330.     return lineLength;
  7331. }
  7332.  
  7333.  
  7334. static uint8 *
  7335. lo_GetNextTextPosition ( LO_TextBlock * block, uint32 * outWordLength, uint32 * outLineLength, Bool * canBreak )
  7336. {
  7337.     uint32        breakCommand;
  7338.     uint32        breakLong;
  7339.     uint32        breakIndex;
  7340.     uint32        lineLength;
  7341.     uint32        nibbleCount;
  7342.     uint32        dataNibbles;
  7343.     int32        wordLength;
  7344.     uint32 *    breakTable;
  7345.     uint8 *        endTextRun;
  7346.     
  7347.     /*
  7348.      * Sanity check for already being at the end of the buffer
  7349.      */
  7350.     
  7351.     if ( block->buffer_read_index == block->buffer_write_index )
  7352.         {
  7353.         *outWordLength = 0;
  7354.         *canBreak = FALSE;
  7355.         return NULL;
  7356.         }
  7357.     
  7358.     /* are we in a run of breakable multibyte characters? */
  7359.     if ( block->multibyte_length > 0 )
  7360.         {
  7361.         uint16    charSize;
  7362.         
  7363.         /* BRAIN DAMAGE */
  7364.         /* turn this next line on when the uint16 multibyte_char_size field is added to LO_TextBlock */
  7365. #if 0
  7366.         charSize = block->multibyte_char_size;
  7367. #else
  7368.         charSize = 2;
  7369. #endif
  7370.         block->multibyte_index += charSize;
  7371.         
  7372.         /* are we at the end of this run? */
  7373.         if ( block->multibyte_index == block->multibyte_length )
  7374.             {
  7375.             block->multibyte_length = 0;
  7376.             block->multibyte_index = 0;
  7377.             }
  7378.         
  7379.         /* bump by one character */
  7380.         *outWordLength = charSize;
  7381.         (*outLineLength) += charSize;
  7382.         *canBreak = TRUE;
  7383.  
  7384.         block->buffer_read_index += 2;
  7385.         endTextRun = &block->text_buffer[ block->buffer_read_index ];
  7386.     
  7387.         return endTextRun;
  7388.         }
  7389.         
  7390.     /* assume we will be able to break */
  7391.     *canBreak = TRUE;
  7392.     
  7393.     lineLength = block->buffer_read_index;
  7394.     breakIndex = block->break_read_index;
  7395.     
  7396.     /* are we at the end of the break table? */
  7397.     if ( breakIndex < block->break_write_index )
  7398.         {
  7399.         /* nope, so grab the next break position */
  7400.         breakTable = &block->break_table[ breakIndex >> 3 ];
  7401.         
  7402.         /* cache this in the TextBlock */
  7403.         breakLong = ( *breakTable++ ) << ( ( breakIndex & 0x7 ) << 2 );
  7404.         wordLength = 0;
  7405.         
  7406.         /* get the next break command */
  7407.         breakCommand = breakLong >> 28;
  7408.         breakLong <<= 4;
  7409.         if ( ( ++breakIndex & 0x7 ) == 0 )
  7410.             {
  7411.             breakLong = *breakTable++;
  7412.             }
  7413.         
  7414.         if ( breakCommand <= MAX_NATURAL_LENGTH )
  7415.             {
  7416.             /* a nibble of length data, we already have all the info we need */
  7417.             wordLength = breakCommand;
  7418.             dataNibbles = 0;
  7419.             }
  7420.         else
  7421.         if ( breakCommand == LINE_FEED )
  7422.             {
  7423.             /* we should only get this when parsing preformatted text */
  7424.             wordLength = BREAK_LINEFEED;
  7425.             dataNibbles = 0;
  7426.             }
  7427.         else
  7428.         if ( breakCommand == BYTE_LENGTH )
  7429.             {
  7430.             dataNibbles = 2;
  7431.             }
  7432.         else
  7433.         if ( breakCommand == WORD_LENGTH )
  7434.             {
  7435.             dataNibbles = 4;
  7436.             }
  7437.         else
  7438.         if ( breakCommand == MULTI_BYTE )
  7439.             {
  7440.             dataNibbles = 4;
  7441.             }
  7442.         else
  7443.             {
  7444.             /* a 24 bits of data */
  7445.             dataNibbles = 6;
  7446.             }
  7447.         
  7448.         if ( dataNibbles > 0 )
  7449.             {
  7450.             /* read in the actual count and the tail command header */
  7451.             for ( nibbleCount = dataNibbles; nibbleCount > 0; --nibbleCount )
  7452.                 {
  7453.                 wordLength <<= 4;
  7454.                 wordLength |= breakLong >> 28;
  7455.                 
  7456.                 breakLong <<= 4;
  7457.                 if ( ( ++breakIndex & 0x7 ) == 0 )
  7458.                     {
  7459.                     breakLong = *breakTable++;
  7460.                     }
  7461.                 }
  7462.             
  7463.             /* now skip the tail end of the command */
  7464.             ++breakIndex;
  7465.             }
  7466.  
  7467.         /* if multi byte, then extract the real data */
  7468.         if ( breakCommand == MULTI_BYTE )
  7469.             {
  7470.             int32    runLength;
  7471.             
  7472.             *canBreak = TRUE;
  7473.  
  7474.             runLength = wordLength & MULTI_LENGTH_MASK;
  7475.                             
  7476.             block->multibyte_length = runLength;
  7477.             block->multibyte_index = 2;
  7478.                 
  7479.             /* make sure we're not already at the end of the run */
  7480.             if ( block->multibyte_index == block->multibyte_length )
  7481.                 {
  7482.                 block->multibyte_index = 0;
  7483.                 block->multibyte_length = 0;
  7484.                 }
  7485.                 
  7486.             /* extract the real word length from the command */
  7487.             wordLength = ( wordLength & MULTI_CHAR_SIZE_MASK ) >> MULTI_CHAR_SIZE_SHIFT;
  7488.                 
  7489.             /* MAJOR BRAIN DAMAGE: WE NEED TO STORE THIS IN THE TEXT BLOCK AS IT WILL NOT */
  7490.             /* ALWAYS BE TWO BYTE!!!! */
  7491.             XP_ASSERT( wordLength == 2 );
  7492.             }
  7493.  
  7494.         lineLength += wordLength;
  7495.  
  7496.         /*
  7497.          * if we actually have a word here and are not the first word on the line, then 
  7498.          * add one to the length to account for the interword space. We know we're not the
  7499.          * first word on the line if we're not at the linebreak or if we're at the start of the
  7500.          * buffer but have already skipped a break position (this happens when the first
  7501.          * character of the buffer is a breaking space).
  7502.          */
  7503.         if ( ( wordLength > 0 ) && ( ( block->last_line_break != block->buffer_read_index ) ||
  7504.                 ( ( block->buffer_read_index == 0 ) && ( block->break_read_index > 0 ) ) ) )
  7505.             {
  7506.             /* only true if there's a space here */
  7507.             if ( XP_IS_SPACE ( block->text_buffer[ block->buffer_read_index ] ) )
  7508.                 {
  7509.                 lineLength++;
  7510.                 (*outLineLength)++;
  7511.                 }
  7512.             }
  7513.         }
  7514.     else
  7515.         {
  7516.         /*
  7517.          * We're at the end of the break table. We may have some text after this last break.
  7518.          * Either way, we cannot break here
  7519.          */
  7520.         
  7521.         *canBreak = FALSE;
  7522.         
  7523.         if ( lineLength < block->buffer_write_index )
  7524.             {
  7525.             lineLength = block->buffer_write_index;
  7526.             wordLength = lineLength - block->buffer_read_index;
  7527.             }
  7528.         }
  7529.         
  7530.     block->break_read_index = breakIndex;
  7531.     block->buffer_read_index = lineLength;
  7532.  
  7533.     endTextRun = &block->text_buffer[ lineLength ];
  7534.     
  7535.     *outLineLength += wordLength;
  7536.     *outWordLength = wordLength;
  7537.     
  7538.     return endTextRun;
  7539. }
  7540.  
  7541.  
  7542. static Bool
  7543. lo_ExtractPrevBreakCommand ( LO_TextBlock * block, Bool * multiByte, uint32 * command )
  7544. {
  7545.     Bool        hasPrev;
  7546.     uint32        breakCommand;
  7547.     uint32        commandData;
  7548.     uint32        breakLong;
  7549.     uint32        breakIndex;
  7550.     uint32 *    breakTable;
  7551.     uint32        nibbleCount;
  7552.     uint32        dataNibbles;
  7553.     Bool        readPrevCommand;
  7554.     
  7555.     commandData = 0;
  7556.     *multiByte = FALSE;
  7557.     
  7558.     breakIndex = block->break_read_index;
  7559.  
  7560.     hasPrev = block->break_read_index > 0;
  7561.     if ( hasPrev )
  7562.         {
  7563.         readPrevCommand = TRUE;
  7564.         
  7565.         /* are we at the absolute end of the buffer? */
  7566.         if ( block->buffer_read_index == block->buffer_write_index )
  7567.             {
  7568.             /* Yup, so back up to the last break offset. */
  7569.             commandData = block->buffer_read_index - block->last_break_offset;
  7570.             
  7571.             /* was there really a true ending break command? */
  7572.             if ( commandData > 0 )
  7573.                 {
  7574.                 readPrevCommand = FALSE;
  7575.  
  7576.                 /* this length was before the breaking space, add it back in */
  7577.                 --commandData;
  7578.                 }
  7579.             }
  7580.         
  7581.         /* extract the previous command from the table if we need to */
  7582.         if ( readPrevCommand )
  7583.             {
  7584.             /* nope, so back up within the break table */
  7585.             --breakIndex;
  7586.             breakTable = &block->break_table[ breakIndex >> 3 ];
  7587.             
  7588.             breakLong = *breakTable;
  7589.             
  7590.             /* shift the command down and extract the data */
  7591.             breakCommand = ( breakLong >> ( ( 7 - ( breakIndex & 0x7 ) ) << 2 ) ) & 0xF;    
  7592.             if ( breakCommand <= MAX_NATURAL_LENGTH )
  7593.                 {
  7594.                 /* a nibble of length data, we already have all the info we need */
  7595.                 dataNibbles = 0;
  7596.                 commandData = breakCommand;
  7597.                 }
  7598.             else
  7599.             if ( breakCommand == LINE_FEED )
  7600.                 {
  7601.                 /* we should only get this when parsing preformatted text */
  7602.                 dataNibbles = 0;
  7603.                 commandData = breakCommand;
  7604.                 }
  7605.             else
  7606.             if ( breakCommand == BYTE_LENGTH )
  7607.                 {
  7608.                 /* a byte of length data */
  7609.                 dataNibbles = 2;
  7610.                 }
  7611.             else
  7612.             if ( breakCommand == WORD_LENGTH )
  7613.                 {
  7614.                 /* a short of length data */
  7615.                 dataNibbles = 4;
  7616.                 }
  7617.             else
  7618.             if ( breakCommand == MULTI_BYTE )
  7619.                 {
  7620.                 /* 16 bits of data */
  7621.                 dataNibbles = 4;
  7622.                 *multiByte = TRUE;
  7623.                 }
  7624.             else
  7625.                 {
  7626.                 /* a 24 bits of data */
  7627.                 dataNibbles = 6;
  7628.                 }
  7629.  
  7630.             if ( dataNibbles > 0 )
  7631.                 {
  7632.                 /* skip the command tail */
  7633.                 if ( ( --breakIndex & 0x7 ) == 7 )
  7634.                     {
  7635.                     breakLong = *--breakTable;
  7636.                     }
  7637.                 
  7638.                 /* read in the actual count and the tail command header */
  7639.                 for ( nibbleCount = 0; nibbleCount < dataNibbles; ++nibbleCount )
  7640.                     {
  7641.                     uint32    nibble;
  7642.                     
  7643.                     /* grab the next nibble */
  7644.                     nibble = ( breakLong >> ( ( 7 - ( breakIndex & 0x7 ) ) << 2 ) ) & 0xF;
  7645.                     commandData |= nibble << ( nibbleCount << 2 );
  7646.                     
  7647.                     if ( ( --breakIndex & 0x7 ) == 7 )
  7648.                         {
  7649.                         breakLong = *--breakTable;
  7650.                         }
  7651.                     }
  7652.                 }
  7653.             }
  7654.         }
  7655.     
  7656.     block->break_read_index = breakIndex;
  7657.  
  7658.     *command = commandData;
  7659.     
  7660.     return hasPrev;
  7661. }
  7662.  
  7663. static uint8 *
  7664. lo_GetPrevTextPosition ( LO_TextBlock * block, uint32 * outWordLength, uint32 * outLineLength, Bool * canBreak )
  7665. {
  7666.     uint32        wordLength;
  7667.     uint32        breakCommand;
  7668.     uint32        lineLength;
  7669.     uint8 *        endTextRun;
  7670.     uint32        totalSkip;
  7671.     Bool        havePrevCommand;
  7672.     Bool        multiByte;
  7673.     
  7674.     *canBreak = FALSE;
  7675.     totalSkip = 0;
  7676.     
  7677.     /*
  7678.      * Sanity check for already being at the beginning of the line
  7679.      */
  7680.     if ( block->buffer_read_index == block->last_line_break )
  7681.         {
  7682.         *outWordLength = 0;
  7683.         return NULL;
  7684.         }
  7685.     
  7686.     /* are we in a run of breakable multibyte characters? */
  7687.     if ( block->multibyte_length > 0 )
  7688.         {
  7689.         uint16    charSize;
  7690.         
  7691.         /* BRAIN DAMAGE */
  7692.         /* turn this next line on when the uint16 multibyte_char_size field is added to LO_TextBlock */
  7693. #if 0
  7694.         charSize = block->multibyte_char_size;
  7695. #else
  7696.         charSize = 2;
  7697. #endif
  7698.         block->multibyte_index -= charSize;
  7699.         
  7700.         /* are we at the end of this run? */
  7701.         if ( block->multibyte_index == 0 )
  7702.             {
  7703.             block->multibyte_length = 0;
  7704.             }
  7705.         
  7706.         /* bump by one character */
  7707.         *outWordLength = charSize;
  7708.         (*outLineLength) -= charSize;
  7709.         *canBreak = TRUE;
  7710.  
  7711.         block->buffer_read_index -= 2;
  7712.         endTextRun = &block->text_buffer[ block->buffer_read_index ];
  7713.     
  7714.         return endTextRun;
  7715.         }
  7716.     
  7717.     /* back up to the previous command */
  7718.     havePrevCommand = lo_ExtractPrevBreakCommand ( block, &multiByte, &breakCommand );
  7719.     if ( !havePrevCommand )
  7720.         {
  7721.         *outWordLength = 0;
  7722.         return NULL;
  7723.         }
  7724.     
  7725.     /* back ourselves up in the buffer */
  7726.     if ( multiByte )
  7727.         {
  7728.         uint32    charSize;
  7729.         
  7730.         *canBreak = TRUE;
  7731.             
  7732.         /* extract the real word length from the command */
  7733.         charSize = ( breakCommand & MULTI_CHAR_SIZE_MASK ) >> MULTI_CHAR_SIZE_SHIFT;
  7734.                         
  7735.         block->multibyte_length = breakCommand & MULTI_LENGTH_MASK;
  7736.         block->multibyte_index = block->multibyte_length - charSize;
  7737.             
  7738.         /* make sure we're not already at the beginning of the run */
  7739.         if ( block->multibyte_index == 0 )
  7740.             {
  7741.             block->multibyte_length = 0;
  7742.             }
  7743.             
  7744.         /* MAJOR BRAIN DAMAGE: WE NEED TO STORE THIS IN THE TEXT BLOCK AS IT WILL NOT */
  7745.         /* ALWAYS BE TWO BYTE!!!! */
  7746.         XP_ASSERT( charSize == 2 );
  7747.         
  7748.         /* the length of this run is the char size */
  7749.         wordLength = charSize;
  7750.         }
  7751.     else
  7752.         {
  7753.         wordLength = breakCommand;
  7754.         }
  7755.     
  7756.     lineLength = block->buffer_read_index;
  7757.     
  7758.     /*
  7759.      * if we actually have a word here and are not the first word on the line, then 
  7760.      * subtract one to the length to account for the interword space.
  7761.      */
  7762.     if ( ( wordLength > 0 ) && ( block->last_line_break != ( lineLength - wordLength )) )
  7763.         {
  7764.         /* only true if there's a space here */
  7765.         if ( XP_IS_SPACE ( block->text_buffer[ lineLength - wordLength - 1 ] ) )
  7766.             {
  7767.             lineLength--;
  7768.             (*outLineLength)--;
  7769.             }
  7770.         }
  7771.  
  7772.     lineLength -= wordLength;
  7773.     
  7774.     block->buffer_read_index = lineLength;
  7775.  
  7776.     endTextRun = &block->text_buffer[ lineLength ];
  7777.     
  7778.     *outLineLength -= wordLength;
  7779.     *outWordLength = wordLength;
  7780.  
  7781.     /* We can't break if we've backed up all the way to the start of the buffer and there is */
  7782.     /* no break position there */
  7783.     if ( ( block->break_read_index == 0 ) && ( wordLength == 0 ) )
  7784.         {
  7785.         *canBreak = FALSE;
  7786.         }
  7787.     else
  7788.         {
  7789.         *canBreak = TRUE;
  7790.         }
  7791.                 
  7792.     return endTextRun;
  7793. }
  7794.  
  7795. static Bool
  7796. lo_SetBreakCommand ( LO_TextBlock * block, uint32 command, uint32 commandLength )
  7797. {
  7798.     uint32        break_write_index;
  7799.     uint32 *    break_table;
  7800.     
  7801.     /* record the current break position as it may be the last entry in the break table */
  7802.     block->last_break_offset = block->buffer_write_index;
  7803.     block->last_buffer_write_index = block->buffer_write_index;
  7804.     
  7805.     break_write_index = block->break_write_index;
  7806.     
  7807.     /* grow the break table if we need to - always have space for one long of data */
  7808.     if ( ( break_write_index + 8 ) > block->break_length )
  7809.         {
  7810.         /* allocate in bytes, count in nibbles */
  7811.         block->break_length += BREAK_TABLE_INC * 2;
  7812.         block->break_table = XP_REALLOC ( block->break_table, block->break_length / 2 );
  7813.         }
  7814.     
  7815.     break_table = block->break_table;
  7816.     
  7817.     if ( break_table != NULL )
  7818.         {
  7819.         uint32    nibble_index;
  7820.         
  7821.         nibble_index = break_write_index & 0x7;        
  7822.         
  7823.         /* write the command */
  7824.         if ( nibble_index == 0 )
  7825.             {
  7826.             /* we're long aligned, write the sucker */
  7827.             break_table[ break_write_index >> 3 ] = command;
  7828.             }
  7829.         else
  7830.             {
  7831.             uint32    table_long;
  7832.             uint32    table_index;
  7833.             
  7834.             table_index = break_write_index >> 3;
  7835.             table_long = break_table[ table_index ];
  7836.             
  7837.             table_long |= command >> ( nibble_index << 2 );
  7838.             break_table[ table_index ] = table_long;
  7839.             
  7840.             /* how many nibbles did we write, and were they enough? */
  7841.             nibble_index = 0x8 - nibble_index;
  7842.             if ( commandLength > nibble_index )
  7843.                 {
  7844.                 /* need to write out some more data */
  7845.                 break_table[ table_index + 1 ] = command << ( nibble_index << 2 );
  7846.                 }
  7847.             }
  7848.         
  7849.         break_write_index += commandLength;
  7850.         }
  7851.     
  7852.     block->break_write_index = break_write_index;
  7853.     
  7854.     return break_table != NULL;
  7855. }
  7856.  
  7857. static Bool
  7858. lo_SetBreakPosition ( LO_TextBlock * block )
  7859. {
  7860.     uint32        w_length;
  7861.     uint32        data_long;
  7862.     uint32        command_size;
  7863.     Bool        success;
  7864.     
  7865.     /* how many characters were added for this run */
  7866.     w_length = block->buffer_write_index - block->last_buffer_write_index;
  7867.     XP_ASSERT ( w_length >= 0 );
  7868.     
  7869.     if ( w_length <= MAX_NATURAL_LENGTH )
  7870.         {
  7871.         /* one data nibble */
  7872.         command_size = 1;
  7873.         data_long = w_length << 28;
  7874.         }
  7875.     else
  7876.     if ( w_length <= 255 )
  7877.         {
  7878.         /* a command nibble, two data nibbles and then a terminator nibble */
  7879.         command_size = 4;
  7880.         data_long = ( BYTE_LENGTH << 28 ) | ( w_length << 20 ) | ( BYTE_LENGTH << 16 );
  7881.         }
  7882.     else
  7883.     if ( w_length <= 65535 )
  7884.         {
  7885.         /* a command nibble, four data nibbles and then a terminator nibble */
  7886.         command_size = 6;
  7887.         data_long = ( WORD_LENGTH << 28 ) | ( w_length << 12 ) | ( WORD_LENGTH << 8 );
  7888.         }
  7889.     else
  7890.         {
  7891.         /* we can have 24 bits of data at most... */
  7892.         XP_ASSERT ( w_length <= ( ( 1 << 24 ) - 1 ) );
  7893.         
  7894.         /* a command nibble, six data nibbles and then a terminator nibble */
  7895.         command_size = 8;
  7896.         data_long = ( LONG_LENGTH << 28 ) | ( w_length << 4 ) | ( LONG_LENGTH );
  7897.         }
  7898.     
  7899.     success = lo_SetBreakCommand ( block, data_long, command_size );
  7900.  
  7901.     return success;
  7902. }
  7903.  
  7904. static Bool
  7905. lo_SetMultiByteRun ( LO_TextBlock * block, int32 charSize, Bool breakable, Bool eachCharBreakable )
  7906. {
  7907.     uint32        w_length;
  7908.     uint32        data_long;
  7909.     uint32        command_size;
  7910.     Bool        success;
  7911.     
  7912.     /* how many characters were added for this run */
  7913.     w_length = block->buffer_write_index - block->last_buffer_write_index;
  7914.     XP_ASSERT ( w_length >= 0 );
  7915.     
  7916.     /*
  7917.      * There are two cases where we can use a normal simple break entry:
  7918.      *    1. We are not breakable on every char and the text is breakable (the normal break table case).
  7919.      *    2. We can break on every char, but we only have one char in the run
  7920.      */
  7921.     if ( breakable )
  7922.         {
  7923.         if ( !eachCharBreakable || ( charSize == w_length ) )
  7924.             {
  7925.             return lo_SetBreakPosition ( block );
  7926.             }
  7927.         }
  7928.         
  7929.     /* BRAIN DAMAGE: We need to handle overflow of our data word */
  7930.     
  7931.     /* this break command always has 16 bits of data and so it is 24 bits big */
  7932.     command_size = 6;
  7933.     
  7934.     data_long = w_length;
  7935.     
  7936.     data_long |= ( charSize << MULTI_CHAR_SIZE_SHIFT ) & MULTI_CHAR_SIZE_MASK;
  7937.  
  7938.     XP_ASSERT( w_length <= MULTI_LENGTH_MASK );
  7939.     data_long |= ( w_length & MULTI_LENGTH_MASK );
  7940.     
  7941.     /* set the command nibbles */
  7942.     data_long = ( (uint32) MULTI_BYTE << ( MULTI_BYTE_DATA_SIZE + 4 ) ) | (uint32) ( data_long << 4 ) | (uint32) MULTI_BYTE;
  7943.     data_long <<= 8;
  7944.     
  7945.     success = lo_SetBreakCommand ( block, data_long, command_size );
  7946.  
  7947.     return success;
  7948. }
  7949.  
  7950. void lo_BreakOldTextBlockElement(MWContext *context, lo_DocState *state)
  7951. {
  7952.     LO_TextBlock *    block;
  7953.     LO_TextStruct *    text_data;
  7954.     LO_TextStruct *    new_text_data;
  7955.     char *            text;
  7956.     char *            breakPtr;
  7957.     int32            save_width;
  7958.     uint32            newTextlength;
  7959.     LO_TextInfo        text_info;
  7960.     int32            base_change;
  7961.     int32            old_baseline;
  7962.     int32            old_line_height;
  7963.     int32            adjust;
  7964.     int32            baseline_inc;
  7965.     LO_Element *    tptr;
  7966.     LO_Element *    eptr;
  7967.     LO_Element *    line_ptr;
  7968.     
  7969.     /* note that the block can be null for a word break element */
  7970.     block = state->old_break_block;
  7971.         
  7972.     /* Move to the element we will break */
  7973.     text_data = state->old_break;
  7974.  
  7975.     /* If there is no text there to break it is an error. */
  7976.     if ( text_data == NULL )
  7977.         {
  7978.         return;
  7979.         }
  7980.     
  7981.     new_text_data = NULL;
  7982.     
  7983.     /*
  7984.      * Later operations will trash the width field.
  7985.      * So save it now to restore later.
  7986.      */
  7987.     save_width = state->width;
  7988.     
  7989.     /*
  7990.      * If this element has no text, then it's a special word break
  7991.      * element. We can simply remove it and then add a linefeed and then
  7992.      * insert the remaining text on the line buffer
  7993.      */
  7994.     if ( text_data->text == NULL )
  7995.         {
  7996.         /*
  7997.          * Back up the state to this element's location
  7998.          */
  7999.         state->x = text_data->x;
  8000.         state->y = text_data->y;
  8001.  
  8002.         tptr = text_data->next;
  8003.         text_data->next = NULL;
  8004.  
  8005.         state->width = text_data->width;
  8006.         state->x += state->width;
  8007.                 
  8008.         /* add a line feed */
  8009.         lo_SoftLineBreak(context, state, TRUE);
  8010.         }
  8011.     else
  8012.         {
  8013.         /*
  8014.          * We're trying to break inside an actual text element. We need to shorten
  8015.          * this element to point up to this break position, then add a linefeed, then
  8016.          * create a new element that contains the remaining text and place it on the
  8017.          * line buffer
  8018.          */
  8019.          
  8020.          /* if we're breaking an element, we must have a text block */
  8021.         if ( block == NULL )
  8022.             {
  8023.             return;
  8024.             }
  8025.  
  8026.         PA_LOCK(text, char *, text_data->text);
  8027.         
  8028.         /*
  8029.          * Back the state up to this element's
  8030.          * location, break off the rest of the elements
  8031.          * and save them for later.
  8032.          * Flush this line, and insert a linebreak.
  8033.          */
  8034.         state->x = text_data->x;
  8035.         state->y = text_data->y;
  8036.         tptr = text_data->next;
  8037.         text_data->next = NULL;
  8038.         
  8039.         breakPtr = &text[ state->old_break_pos ];
  8040.         newTextlength = text_data->text_len - state->old_break_pos;
  8041.  
  8042.         text_data->block_offset = text_data->block_offset - text_data->text_len + state->old_break_pos;
  8043.         text_data->text_len = state->old_break_pos;
  8044.  
  8045.         FE_GetTextInfo(context, text_data, &text_info);
  8046.         state->width = lo_correct_text_element_width( &text_info );
  8047.         
  8048.         text_data->width = state->width;
  8049.         PA_UNLOCK(text_data->text);
  8050.         state->x += state->width;
  8051.         
  8052.         /* this element should point at the space before the next word, so skip it */
  8053.         if ( XP_IS_SPACE ( *breakPtr ) )
  8054.             {
  8055.             ++breakPtr;
  8056.             --newTextlength;
  8057.             }
  8058.  
  8059.         /*
  8060.          * If the element that caused this break has a different
  8061.          * baseline than the element we are breaking, we need to
  8062.          * preserve that difference after the break.
  8063.          */
  8064.         base_change = state->baseline - (text_data->y_offset + text_info.ascent);
  8065.  
  8066.         old_baseline = state->baseline;
  8067.         old_line_height = state->line_height;
  8068.  
  8069.         /*
  8070.          * Reset element_id so they are still sequencial.
  8071.          */
  8072.         state->top_state->element_id = text_data->ele_id + 1;
  8073.  
  8074.         /*
  8075.          * If we are breaking an anchor, we need to make sure the
  8076.          * linefeed gets its anchor href set properly.
  8077.          */
  8078.         if (text_data->anchor_href != NULL)
  8079.             {
  8080.             LO_AnchorData *tmp_anchor;
  8081.  
  8082.             tmp_anchor = state->current_anchor;
  8083.             state->current_anchor = text_data->anchor_href;
  8084.             lo_SoftLineBreak(context, state, TRUE);
  8085.             state->current_anchor = tmp_anchor;
  8086.             }
  8087.         else
  8088.             {
  8089.             lo_SoftLineBreak(context, state, TRUE);
  8090.             }
  8091.  
  8092.         adjust = lo_baseline_adjust( context, state, tptr, old_baseline, old_line_height );
  8093.         state->baseline = old_baseline - adjust;
  8094.         state->line_height = (intn) old_line_height - adjust;
  8095.         
  8096.         /* now create a new text element */
  8097.         baseline_inc = -1 * adjust;
  8098.         
  8099.         new_text_data = (LO_TextStruct *)lo_NewElement ( context, state, LO_TEXT, text_data->edit_element, text_data->edit_offset+text_data->text_len+1 );
  8100.         if (text_data == NULL)
  8101.             {
  8102. #ifdef DEBUG
  8103.             assert (state->top_state->out_of_memory);
  8104. #endif
  8105.             return;
  8106.             }
  8107.  
  8108.         new_text_data->type = LO_TEXT;
  8109.         new_text_data->ele_id = NEXT_ELEMENT;
  8110.         new_text_data->x = state->x;
  8111.         new_text_data->x_offset = 0;
  8112.         new_text_data->y = state->y;
  8113.         new_text_data->y_offset = 0;
  8114.         new_text_data->height = 0;
  8115.         new_text_data->next = NULL;
  8116.         new_text_data->prev = NULL;
  8117.  
  8118.         new_text_data->anchor_href = block->anchor_href;
  8119.         new_text_data->text_attr = block->text_attr;
  8120.         new_text_data->ele_attrmask = block->ele_attrmask;
  8121.         
  8122.         /* BRAIN DAMAGE: Set LO_ELE_INVISIBLE to mark the element as not having a valid text ptr */
  8123.         XP_ASSERT ( !(new_text_data->ele_attrmask & LO_ELE_INVISIBLE ) );
  8124.         new_text_data->ele_attrmask |= LO_ELE_INVISIBLE;
  8125.         
  8126.         new_text_data->sel_start = -1;
  8127.         new_text_data->sel_end = -1;
  8128.  
  8129.         new_text_data->doc_width = state->right_margin - state->x;
  8130.         new_text_data->doc_width = 0;
  8131.         new_text_data->block_offset = text_data->block_offset + text_data->text_len;
  8132.         XP_ASSERT(new_text_data->block_offset <= 65535);
  8133.             
  8134.         new_text_data->text = (PA_Block) breakPtr;
  8135.         new_text_data->text_len = newTextlength;
  8136.         FE_GetTextInfo(context, new_text_data, &text_info);
  8137.         new_text_data->width = lo_correct_text_element_width(&text_info);
  8138.  
  8139.         /*
  8140.          * Some fonts (particulatly italic ones with curly
  8141.          * tails on letters like 'f') have a left bearing
  8142.          * that extends back into the previous character.
  8143.          * Since in this case the previous character is
  8144.          * probably not in the same font, we move forward
  8145.          * to avoid overlap.
  8146.          */
  8147.         if (text_info.lbearing < 0)
  8148.             {
  8149.             new_text_data->x_offset = text_info.lbearing * -1;
  8150.             }
  8151.  
  8152.         /*
  8153.          * The baseline of the text element just inserted in
  8154.          * the line may be less than or greater than the
  8155.          * baseline of the rest of the line due to font
  8156.          * changes.  If the baseline is less, this is easy,
  8157.          * we just increase y_offest to move the text down
  8158.          * so the baselines line up.  For greater baselines,
  8159.          * we can't move the text up to line up the baselines
  8160.          * because we will overlay the previous line, so we
  8161.          * have to move all rest of the elements in this line
  8162.          * down.
  8163.          *
  8164.          * If the baseline is zero, we are the first element
  8165.          * on the line, and we get to set the baseline.
  8166.          */
  8167.         if (state->baseline == 0)
  8168.             {
  8169.             state->baseline = text_info.ascent;
  8170.             }
  8171.         else
  8172.         if (text_info.ascent < state->baseline)
  8173.             {
  8174.             new_text_data->y_offset = state->baseline - text_info.ascent;
  8175.             }
  8176.         else
  8177.             {
  8178.             baseline_inc = baseline_inc + (text_info.ascent - state->baseline);
  8179.             state->baseline = text_info.ascent;
  8180.             }
  8181.  
  8182.         /*
  8183.          * Now that we have broken, and added the new
  8184.          * element, we need to move it down to restore the
  8185.          * baseline difference that previously existed.
  8186.          */
  8187.         new_text_data->y_offset -= base_change;
  8188.  
  8189.         /*
  8190.          * Calculate the height of this new
  8191.          * text element.
  8192.          */
  8193.         new_text_data->height = text_info.ascent + text_info.descent;
  8194.         state->x += new_text_data->width;
  8195.         }
  8196.     
  8197.     /*
  8198.      * if our previous element was the last text element for this block, then our
  8199.      * new element is the new end
  8200.      */
  8201.     if ( block->endTextElement == text_data )
  8202.         {
  8203.         block->endTextElement = new_text_data;
  8204.         }
  8205.         
  8206.     /*
  8207.      * Now add the remaining elements to the line list
  8208.      */
  8209.      
  8210.     /* first find the end of the line list */
  8211.     line_ptr = state->line_list;
  8212.     while ((line_ptr != NULL)&&(line_ptr->lo_any.next != NULL))
  8213.         {
  8214.         line_ptr = line_ptr->lo_any.next;
  8215.         }
  8216.     
  8217.     /* now add the new_text_data if there is one */
  8218.     if ( new_text_data != NULL )
  8219.         {
  8220.         if (line_ptr == NULL)
  8221.             {
  8222.             state->line_list = (LO_Element *) new_text_data;
  8223.             new_text_data->prev = NULL;
  8224.             line_ptr = (LO_Element *) new_text_data;
  8225.             }
  8226.         else
  8227.             {
  8228.             line_ptr->lo_any.next = (LO_Element *) new_text_data;
  8229.             new_text_data->prev = line_ptr;
  8230.             line_ptr = (LO_Element *) new_text_data;
  8231.             }
  8232.         }
  8233.     
  8234.     /* and then add tptr */
  8235.     if ( tptr != NULL )
  8236.         {
  8237.         if (line_ptr == NULL)
  8238.             {
  8239.             state->line_list = tptr;
  8240.             tptr->lo_any.prev = NULL;
  8241.             line_ptr = tptr;
  8242.             }
  8243.         else
  8244.             {
  8245.             line_ptr->lo_any.next = tptr;
  8246.             tptr->lo_any.prev = line_ptr;
  8247.             line_ptr = tptr;
  8248.             }
  8249.         }
  8250.     
  8251.     /* if we've added a new element then increment the element id's of the following elements */
  8252.     if ( new_text_data != NULL )
  8253.         {
  8254.         eptr = tptr;
  8255.  
  8256.         while (eptr != NULL)
  8257.             {
  8258.             eptr->lo_any.ele_id = NEXT_ELEMENT;
  8259.             eptr->lo_any.y_offset += baseline_inc;
  8260.             eptr = eptr->lo_any.next;
  8261.             }
  8262.         
  8263.         /* and bump the line height if we need to */
  8264.         if ( ( new_text_data->y_offset + new_text_data->height ) > state->line_height )
  8265.             {
  8266.             state->line_height = (intn) new_text_data->y_offset + new_text_data->height;
  8267.             }
  8268.         }
  8269.  
  8270.     /*
  8271.      * Upgrade forward the x and y text positions in the document
  8272.      * state.
  8273.      */
  8274.     while ( tptr != NULL )
  8275.         {
  8276.         lo_UpdateElementPosition ( state, tptr );
  8277.         tptr = tptr->lo_any.next;
  8278.         }
  8279.  
  8280.     state->at_begin_line = FALSE;
  8281.     state->width = save_width;
  8282. }
  8283.  
  8284. static uint8 *
  8285. lo_GetLineStart ( LO_TextBlock * block )
  8286. {
  8287.     return &block->text_buffer[ block->last_line_break ];
  8288. }
  8289.  
  8290.  
  8291. static void
  8292. lo_SkipCharacter ( LO_TextBlock * block )
  8293. {
  8294.     ++block->buffer_read_index;
  8295. }
  8296.  
  8297.  
  8298. static void
  8299. lo_SetLineBreak ( LO_TextBlock * block, Bool skipSpace )
  8300. {
  8301.     /* if the current character is a space, we can skip it as we're breaking here */
  8302.     /* NOT FOR PREFORMATTED TEXT THOUGH */
  8303.     if ( skipSpace && ( XP_IS_SPACE ( block->text_buffer[ block->buffer_read_index ] ) ) )
  8304.         {
  8305.         ++block->buffer_read_index;
  8306.         }
  8307.  
  8308.     block->last_line_break = block->buffer_read_index;
  8309. }
  8310.  
  8311. static uint8 *
  8312. lo_RestoreBreakState ( LO_TextBlock * block, BreakState * state, uint32 * lineLength )
  8313. {
  8314.     uint8 *    runEnd;
  8315.     
  8316.     block->buffer_read_index = state->buffer_read_index;
  8317.     block->break_read_index = state->break_read_index;
  8318.     block->multibyte_index = state->multibyte_index;
  8319.     block->multibyte_length = state->multibyte_length;
  8320.     block->last_line_break = state->last_line_break;
  8321.     
  8322.     if ( lineLength != NULL )
  8323.         {
  8324.         *lineLength = state->lineLength;
  8325.         }
  8326.     
  8327.     runEnd = &block->text_buffer[ block->buffer_read_index ];
  8328.     
  8329.     return runEnd;
  8330. }
  8331.  
  8332.  
  8333. static Bool
  8334. lo_SkipInitialSpace ( LO_TextBlock * block )
  8335. {
  8336.     Bool    canBreak;
  8337.     uint32    breakLong;
  8338.     uint32    breakIndex;
  8339.     
  8340.     canBreak = FALSE;
  8341.     breakIndex = block->break_read_index;
  8342.     
  8343.     /* do we have a break point at the current text offset */
  8344.     if ( ( breakIndex == 0 ) && ( breakIndex < block->break_write_index ) && XP_IS_SPACE ( block->text_buffer[ 0 ] ) )
  8345.         {
  8346.         breakLong = block->break_table[ breakIndex >> 3 ];
  8347.         breakLong >>= ( 7 - breakIndex & 0x7 ) << 2;
  8348.         
  8349.         if ( breakLong == 0 )
  8350.             {
  8351.             block->buffer_read_index++;
  8352.             block->break_read_index++;
  8353.             block->last_line_break++;
  8354.             canBreak = TRUE;
  8355.             }
  8356.         }
  8357.     
  8358.     return canBreak;
  8359. }
  8360.  
  8361.  
  8362. Bool lo_GrowTextBlock ( LO_TextBlock * block, uint32 length )
  8363. {
  8364.     Bool            success = TRUE;
  8365.     uint32            growBy;
  8366.     uint32            oldTextBase;
  8367.     uint32            offset;
  8368.     LO_TextStruct *    textElement;
  8369.     LO_TextStruct *    endElement;
  8370.     
  8371.     if ( block->buffer_length < ( block->buffer_write_index + length ) )
  8372.         {
  8373.         /* need to make sure that the new size is enough to contain the new data */
  8374.         growBy = TEXT_BUFFER_INC;
  8375.         if ( growBy < length )
  8376.             {
  8377.             growBy = length;
  8378.             }
  8379.         
  8380.         oldTextBase = (uint32) block->text_buffer;
  8381.         
  8382.         growBy += block->buffer_length;
  8383.         block->text_buffer = XP_REALLOC ( block->text_buffer, growBy );
  8384.         
  8385.         block->buffer_length = growBy;
  8386.         success = block->text_buffer != NULL;
  8387.         
  8388.         /* update any break table related information */
  8389.         if ( success && ( block->break_table != NULL ) )
  8390.             {
  8391.             /*
  8392.              * Run through all the text elements and adjust their text addresses to point to the newly
  8393.              * relocated block
  8394.              */
  8395.             
  8396.             textElement = block->startTextElement;
  8397.             endElement = block->endTextElement;
  8398.             
  8399.             while ( textElement != NULL )
  8400.                 {
  8401.                 if ( textElement->type == LO_TEXT )
  8402.                     {
  8403.                     offset = (uint32) textElement->text - oldTextBase;
  8404.                     textElement->text = (PA_Block) ( (uint32) block->text_buffer + offset );
  8405.                     }
  8406.                 
  8407.                 if ( textElement == endElement )
  8408.                     {
  8409.                     break;
  8410.                     }
  8411.                 
  8412.                 textElement = (LO_TextStruct *) textElement->next;
  8413.                 }
  8414.             }
  8415.         }
  8416.     
  8417.     return success;
  8418. }
  8419.  
  8420. static void
  8421. lo_CopyText ( uint8 * src, uint8 * dst, uint32 length )
  8422. {
  8423.     char    c;
  8424.     
  8425.     /* copy a text string, converting non breaking spaces to normal spaces as we go */
  8426.     while ( length-- )
  8427.         {
  8428.         c = *src++;
  8429.         
  8430.         if ( c == NON_BREAKING_SPACE )
  8431.             {
  8432.             c = ' ';
  8433.             }
  8434.         
  8435.         *dst++ = c;
  8436.         }
  8437. }
  8438.  
  8439. static void
  8440. lo_CopyTextToLineBuffer ( lo_DocState * state, uint8 * src, uint32 length )
  8441. {
  8442.     char *    text_buf;
  8443.  
  8444.     /* do we need to grow the buffer? */
  8445.     if ( ( state->line_buf_len + length + 1 ) > state->line_buf_size )
  8446.         {
  8447.         state->line_buf = PA_REALLOC ( state->line_buf, ( state->line_buf_size + length + LINE_BUF_INC ) );
  8448.         if ( state->line_buf == NULL )
  8449.             {
  8450.             state->top_state->out_of_memory = TRUE;
  8451.             return;
  8452.             }
  8453.         }
  8454.     
  8455.     PA_LOCK(text_buf, char *, state->line_buf);
  8456.     
  8457.     XP_BCOPY ( (char *) src, (char *) ( text_buf + state->line_buf_len ), ( length + 1 ) );
  8458.     state->line_buf_len += length;
  8459.     
  8460.     text_buf[ state->line_buf_len ] = 0;
  8461.     
  8462.     PA_UNLOCK(state->line_buf);
  8463. }
  8464.  
  8465. #ifdef TEST_16BIT
  8466. #undef XP_WIN16
  8467. #endif /* TEST_16BIT */
  8468.  
  8469. #ifdef PROFILE
  8470. #pragma profile off
  8471. #endif
  8472.