pjs/lib/layout/layspace.c

800 строки
16 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "xp.h"
#include "pa_parse.h"
#include "layout.h"
#define SPACE_WORD 1
#define SPACE_LINE 2
#define SPACE_BLOCK 3
static void lo_AppendSpacerElementToLineList( MWContext *context, lo_DocState *state,
LO_SpacerStruct *spacer );
static void lo_ParseAttributesForBlockSpacer( MWContext *context, lo_DocState *state,
LO_SpacerStruct *spacer );
static void lo_LayoutBlockSpacer(MWContext *context, lo_DocState *state,
LO_SpacerStruct *spacer, Bool relayout);
void
lo_ProcessSpacerTag(MWContext *context, lo_DocState *state, PA_Tag *tag)
{
int32 type;
int32 size;
PA_Block buff;
LO_SpacerStruct *spacer;
spacer = (LO_SpacerStruct*)lo_NewElement(context, state, LO_SPACER, NULL, 0);
XP_ASSERT(spacer);
if (!spacer)
{
state->top_state->out_of_memory = TRUE;
return;
}
type = SPACE_WORD;
buff = lo_FetchParamValue(context, tag, PARAM_TYPE);
if (buff != NULL)
{
char *type_str;
PA_LOCK(type_str, char *, buff);
if (pa_TagEqual("line", type_str))
{
type = SPACE_LINE;
}
else if (pa_TagEqual("vert", type_str))
{
type = SPACE_LINE;
}
else if (pa_TagEqual("vertical", type_str))
{
type = SPACE_LINE;
}
else if (pa_TagEqual("block", type_str))
{
type = SPACE_BLOCK;
}
PA_UNLOCK(buff);
PA_FREE(buff);
}
size = 0;
buff = lo_FetchParamValue(context, tag, PARAM_SIZE);
if (buff != NULL)
{
char *str;
PA_LOCK(str, char *, buff);
size = XP_ATOI(str);
PA_UNLOCK(buff);
PA_FREE(buff);
if (size < 1)
{
size = 0;
}
}
spacer->size = size;
spacer->type = (int8) type;
spacer->tag = tag;
lo_AppendSpacerElementToLineList( context, state, spacer );
lo_LayoutSpacerElement(context, state, spacer, FALSE);
/*
* Spacers of size 0 do nothing.
* Unless they are block spacers that use WIDTH and HEIGHT.
*/
/*
if ((size == 0)&&(type != SPACE_BLOCK))
{
return;
}
if (type == SPACE_WORD)
{
lo_InsertWordBreak(context, state);
size = FEUNITS_X(size, context);
state->x += size;
}
else if (type == SPACE_LINE)
{
lo_SetSoftLineBreakState(context, state, FALSE, 1);
size = FEUNITS_Y(size, context);
state->y += size;
}
else if (type == SPACE_BLOCK)
{
lo_format_block_spacer(context, state, tag);
}
*/
}
static void lo_AppendSpacerElementToLineList( MWContext *context, lo_DocState *state, LO_SpacerStruct *spacer )
{
spacer->lo_any.type = LO_SPACER;
spacer->lo_any.x = state->x;
spacer->lo_any.y = state->y;
spacer->lo_any.x_offset = 0;
spacer->lo_any.y_offset = 0;
spacer->lo_any.width = 0;
spacer->lo_any.height = 0;
spacer->lo_any.line_height = 0;
spacer->lo_any.ele_id = NEXT_ELEMENT;
lo_AppendToLineList(context, state, (LO_Element*)spacer, 0);
}
void lo_LayoutSpacerElement(MWContext *context, lo_DocState *state, LO_SpacerStruct *spacer, Bool relayout)
{
int32 size = spacer->size;
int8 type = spacer->type;
/*
* Spacers of size 0 do nothing.
* Unless they are block spacers that use WIDTH and HEIGHT.
*/
if ((size == 0)&&(type != SPACE_BLOCK))
{
return;
}
if (type == SPACE_WORD)
{
if (!relayout)
lo_InsertWordBreak(context, state);
size = FEUNITS_X(size, context);
state->x += size;
}
else if (type == SPACE_LINE)
{
/* lo_SetSoftLineBreakState(context, state, FALSE, 1); */
lo_SetLineBreakState(context, state, FALSE, LO_LINEFEED_BREAK_SOFT, 1, relayout);
size = FEUNITS_Y(size, context);
state->y += size;
}
else if (type == SPACE_BLOCK)
{
if (!relayout)
{
lo_ParseAttributesForBlockSpacer(context, state, spacer);
}
lo_LayoutBlockSpacer(context, state, spacer, relayout);
}
if (!relayout)
{
/* We do not want to keep a pointer to the tag around for relayout
because we have all the information we need in the LO_SPACER
layout element */
spacer->tag = NULL;
}
}
static void lo_ParseAttributesForBlockSpacer( MWContext *context, lo_DocState *state, LO_SpacerStruct *spacer )
{
Bool floating;
int32 alignment;
int32 val;
PA_Block buff;
PA_Tag *tag = spacer->tag;
char *str;
alignment = LO_ALIGN_BASELINE;
floating = FALSE;
/*
* Check for an align parameter
*/
buff = lo_FetchParamValue(context, tag, PARAM_ALIGN);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
alignment = lo_EvalAlignParam(str, &floating);
PA_UNLOCK(buff);
PA_FREE(buff);
}
spacer->alignment = alignment;
spacer->floating = floating;
/*
* Get the width parameter, in absolute or percentage.
* If percentage, make it absolute.
*/
buff = lo_FetchParamValue(context, tag, PARAM_WIDTH);
if (buff != NULL)
{
Bool is_percent;
PA_LOCK(str, char *, buff);
val = lo_ValueOrPercent(str, &is_percent);
if (is_percent != FALSE)
{
spacer->is_percent_width = TRUE;
spacer->width = val;
}
else
{
spacer->is_percent_width = FALSE;
val = FEUNITS_X(val, context);
if (val < 1)
{
val = 1;
}
spacer->width = val;
}
PA_UNLOCK(buff);
PA_FREE(buff);
}
/*
* Get the height parameter, in absolute or percentage.
* If percentage, make it absolute.
*/
buff = lo_FetchParamValue(context, tag, PARAM_HEIGHT);
if (buff != NULL)
{
Bool is_percent;
PA_LOCK(str, char *, buff);
val = lo_ValueOrPercent(str, &is_percent);
if (is_percent != FALSE)
{
spacer->is_percent_height = TRUE;
spacer->height = val;
}
else
{
spacer->is_percent_height = FALSE;
val = FEUNITS_Y(val, context);
if (val < 1)
{
val = 1;
}
spacer->height = val;
}
PA_UNLOCK(buff);
PA_FREE(buff);
}
}
static void lo_LayoutBlockSpacer(MWContext *context, lo_DocState *state, LO_SpacerStruct *spacer, Bool relayout)
{
int32 width = 0;
int32 height = 0;
int32 doc_width;
int32 alignment = spacer->alignment;
Bool floating = spacer->floating;
Bool line_break;
char *str;
int32 x, y;
int16 x_offset;
int32 y_offset;
int32 baseline_inc, line_inc;
LO_TextStruct tmp_text;
LO_TextInfo text_info;
LO_Element *eptr;
PA_Block buff;
x = state->x;
y = state->y;
doc_width = state->right_margin - state->left_margin;
if (spacer->is_percent_width)
{
int32 val;
if (state->allow_percent_width == FALSE)
{
val = 0;
}
else
{
val = spacer->width;
val = doc_width * val / 100;
if (val < 1)
{
val = 1;
}
}
width = val;
}
else
{
width = spacer->width;
}
if (spacer->is_percent_height)
{
int32 val;
if (state->allow_percent_height == FALSE)
{
val = 0;
}
else
{
val = state->win_height * val / 100;
if (val < 1)
{
val = 1;
}
}
height = val;
}
else
{
height = spacer->height;
}
/*
* All this work is to get the text_info filled in for the current
* font in the font stack. Yuck, there must be a better way.
*/
memset (&tmp_text, 0, sizeof (tmp_text));
buff = PA_ALLOC(1);
if (buff == NULL)
{
state->top_state->out_of_memory = TRUE;
return;
}
PA_LOCK(str, char *, buff);
str[0] = ' ';
PA_UNLOCK(buff);
tmp_text.text = buff;
tmp_text.text_len = 1;
tmp_text.text_attr = state->font_stack->text_attr;
FE_GetTextInfo(context, &tmp_text, &text_info);
PA_FREE(buff);
/*
* If both dimentions are bogus, no spacer.
* If only one dimension bogus, then make it 1.
*/
if ((width < 1)&&(height < 1))
{
return;
}
else if (width < 1)
{
width = 1;
}
else if (height < 1)
{
height = 1;
}
/*
* SEVERE FLOW BREAK! This may be a floating image,
* which means at this point we go do something completely
* different.
*/
if (floating != FALSE)
{
if (alignment == LO_ALIGN_RIGHT)
{
if (state->right_margin_stack == NULL)
{
x = state->right_margin - width;
}
else
{
x = state->right_margin_stack->margin - width;
}
if (x < 0)
{
x = 0;
}
}
else
{
x = state->left_margin;
}
y = -1;
lo_AddMarginStack(state, x, y,
width, height, 0, 0, 0, alignment);
if (state->at_begin_line != FALSE)
{
lo_FindLineMargins(context, state, !relayout);
state->x = state->left_margin;
}
return;
}
/*
* Will this spacer make the line too wide.
*/
if ((state->x + width) > state->right_margin)
{
line_break = TRUE;
}
else
{
line_break = FALSE;
}
/*
* if we are at the beginning of the line. There is
* no point in breaking, we are just too wide.
* Also don't break in unmapped preformatted text.
* Also can't break inside a NOBR section.
*/
if ((state->at_begin_line != FALSE)||
(state->preformatted == PRE_TEXT_YES)||
(state->breakable == FALSE))
{
line_break = FALSE;
}
/*
* break on the spacer if we have
* a break.
*/
if (line_break != FALSE)
{
if (!relayout)
lo_SoftLineBreak(context, state, TRUE);
else
lo_rl_AddSoftBreakAndFlushLine(context, state);
x = state->x;
y = state->y;
}
/*
* Figure out how to align this spacer.
* baseline_inc is how much to increase the baseline
* of previous element of this line. line_inc is how
* much to increase the line height below the baseline.
*/
baseline_inc = 0;
line_inc = 0;
x_offset = 0;
y_offset = 0;
lo_CalcAlignOffsets(state, &text_info, alignment,
width, height,
&x_offset, &y_offset, &line_inc, &baseline_inc);
width += x_offset;
height += y_offset;
/*
* Change the rest of the line the way lo_AppendToLineList()
* would if we were adding a real element.
*/
eptr = state->line_list;
if (eptr != NULL)
{
while (eptr->lo_any.next != NULL)
{
eptr->lo_any.y_offset += baseline_inc;
eptr = eptr->lo_any.next;
}
eptr->lo_any.y_offset += baseline_inc;
}
state->baseline += (intn) baseline_inc;
state->line_height += (intn) (baseline_inc + line_inc);
/*
* Clean up state
*/
state->x = state->x + width;
state->linefeed_state = 0;
state->at_begin_line = FALSE;
state->trailing_space = FALSE;
state->cur_ele_type = LO_NONE;
}
#if 0
void
lo_format_block_spacer(MWContext *context, lo_DocState *state, PA_Tag *tag, Bool relayout)
{
Bool line_break;
Bool floating;
int32 baseline_inc, line_inc;
int32 alignment;
int16 x_offset;
int32 y_offset;
int32 x, y;
int32 width, height;
int32 doc_width;
int32 val;
PA_Block buff;
char *str;
LO_TextStruct tmp_text;
LO_TextInfo text_info;
LO_Element *eptr;
x = state->x;
y = state->y;
width = 0;
height = 0;
alignment = LO_ALIGN_BASELINE;
floating = FALSE;
/*
* Check for an align parameter
*/
buff = lo_FetchParamValue(context, tag, PARAM_ALIGN);
if (buff != NULL)
{
PA_LOCK(str, char *, buff);
alignment = lo_EvalAlignParam(str, &floating);
PA_UNLOCK(buff);
PA_FREE(buff);
}
doc_width = state->right_margin - state->left_margin;
/*
* Get the width parameter, in absolute or percentage.
* If percentage, make it absolute.
*/
buff = lo_FetchParamValue(context, tag, PARAM_WIDTH);
if (buff != NULL)
{
Bool is_percent;
PA_LOCK(str, char *, buff);
val = lo_ValueOrPercent(str, &is_percent);
if (is_percent != FALSE)
{
if (state->allow_percent_width == FALSE)
{
val = 0;
}
else
{
val = doc_width * val / 100;
if (val < 1)
{
val = 1;
}
}
}
else
{
val = FEUNITS_X(val, context);
if (val < 1)
{
val = 1;
}
}
width = val;
PA_UNLOCK(buff);
PA_FREE(buff);
}
/*
* Get the height parameter, in absolute or percentage.
* If percentage, make it absolute.
*/
buff = lo_FetchParamValue(context, tag, PARAM_HEIGHT);
if (buff != NULL)
{
Bool is_percent;
PA_LOCK(str, char *, buff);
val = lo_ValueOrPercent(str, &is_percent);
if (is_percent != FALSE)
{
if (state->allow_percent_height == FALSE)
{
val = 0;
}
else
{
val = state->win_height * val / 100;
if (val < 1)
{
val = 1;
}
}
}
else
{
val = FEUNITS_Y(val, context);
if (val < 1)
{
val = 1;
}
}
height = val;
PA_UNLOCK(buff);
PA_FREE(buff);
}
/*
* All this work is to get the text_info filled in for the current
* font in the font stack. Yuck, there must be a better way.
*/
memset (&tmp_text, 0, sizeof (tmp_text));
buff = PA_ALLOC(1);
if (buff == NULL)
{
state->top_state->out_of_memory = TRUE;
return;
}
PA_LOCK(str, char *, buff);
str[0] = ' ';
PA_UNLOCK(buff);
tmp_text.text = buff;
tmp_text.text_len = 1;
tmp_text.text_attr = state->font_stack->text_attr;
FE_GetTextInfo(context, &tmp_text, &text_info);
PA_FREE(buff);
/*
* If both dimentions are bogus, no spacer.
* If only one dimension bogus, then make it 1.
*/
if ((width < 1)&&(height < 1))
{
return;
}
else if (width < 1)
{
width = 1;
}
else if (height < 1)
{
height = 1;
}
/*
* SEVERE FLOW BREAK! This may be a floating image,
* which means at this point we go do something completely
* different.
*/
if (floating != FALSE)
{
if (alignment == LO_ALIGN_RIGHT)
{
if (state->right_margin_stack == NULL)
{
x = state->right_margin - width;
}
else
{
x = state->right_margin_stack->margin - width;
}
if (x < 0)
{
x = 0;
}
}
else
{
x = state->left_margin;
}
y = -1;
lo_AddMarginStack(state, x, y,
width, height, 0, 0, 0, alignment);
if (state->at_begin_line != FALSE)
{
lo_FindLineMargins(context, state, TRUE);
state->x = state->left_margin;
}
return;
}
/*
* Will this spacer make the line too wide.
*/
if ((state->x + width) > state->right_margin)
{
line_break = TRUE;
}
else
{
line_break = FALSE;
}
/*
* if we are at the beginning of the line. There is
* no point in breaking, we are just too wide.
* Also don't break in unmapped preformatted text.
* Also can't break inside a NOBR section.
*/
if ((state->at_begin_line != FALSE)||
(state->preformatted == PRE_TEXT_YES)||
(state->breakable == FALSE))
{
line_break = FALSE;
}
/*
* break on the spacer if we have
* a break.
*/
if (line_break != FALSE)
{
lo_SoftLineBreak(context, state, TRUE);
x = state->x;
y = state->y;
}
/*
* Figure out how to align this spacer.
* baseline_inc is how much to increase the baseline
* of previous element of this line. line_inc is how
* much to increase the line height below the baseline.
*/
baseline_inc = 0;
line_inc = 0;
x_offset = 0;
y_offset = 0;
lo_CalcAlignOffsets(state, &text_info, alignment,
width, height,
&x_offset, &y_offset, &line_inc, &baseline_inc);
width += x_offset;
height += y_offset;
/*
* Change the rest of the line the way lo_AppendToLineList()
* would if we were adding a real element.
*/
eptr = state->line_list;
if (eptr != NULL)
{
while (eptr->lo_any.next != NULL)
{
eptr->lo_any.y_offset += baseline_inc;
eptr = eptr->lo_any.next;
}
eptr->lo_any.y_offset += baseline_inc;
}
state->baseline += (intn) baseline_inc;
state->line_height += (intn) (baseline_inc + line_inc);
/*
* Clean up state
*/
state->x = state->x + width;
state->linefeed_state = 0;
state->at_begin_line = FALSE;
state->trailing_space = FALSE;
state->cur_ele_type = LO_NONE;
}
#endif