/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/

#include	"memory_debug.h"
#include	"text_render.h"
#include	"window.h"
#include	"utils.h"

typedef struct sp_work {
	TR_CHAR_INFO	ci;
	TR_ELEMENT *	prev_element_ptr;
	TR_ELEMENT *	element_ptr;
	char		el_type;
#define SPWT_STANDARD_ELEMENT	1
#define SPWT_ILLEAGAL_ELEMENT	2
	char		rotate;
	char		el_dir;

	int		real_line_pixels;
} SP_WORK;

typedef struct sp_element_work {
	LC_STRING_PIC		pic;
	I_POINT			offset;
	LC_FONT *		font;
	int			size;
	int			len;
	L_CHAR *		str;
	TR_PTR *		ptr;
	TR_ATTR *		attr;
	char			glue_start;
	char			glue_end;
	short			ll_dir;
	short			ln_dir;

	void *			obj;
} SP_ELEMENT_WORK;

typedef struct sp_box_line_work {
	I_POINT			offset;
	short			return_dir;
} SP_BOX_LINE_WORK;

typedef struct sp_box_work {
	I_POINT			offset;
} SP_BOX_WORK;

TR_ERROR sp_last_ll_destroy(TR_SEQUENCE * sq,SP_WORK * spw);
TR_ERROR sp_last_el_destroy(TR_SEQUENCE * sq,SP_WORK * spw);
TR_ERROR sp_line_check(TR_SEQUENCE * sq,SP_WORK * spw);
TR_ERROR sp_new_element(TR_SEQUENCE * sq,SP_WORK * spw);
void sp_get_pic(SP_WORK * spw,SP_ELEMENT_WORK * ew,TR_CHAR_INFO * ci);
TR_ERROR sp_flush_element(TR_SEQUENCE * sq,TR_CHAR_INFO * ci,SP_WORK * spw);


int
sp_new_sequence(TR_SEQUENCE *,void *);
void
sp_start_lang_line_op(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci);
TR_ERROR
sp_put_box_line(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci,
	TR_PTR p,
	TR_CHAR_ELEMENT * el);
void
sp_new_box_line(TR_SEQUENCE * sq,TR_BOX_LINE * bl);
TR_ERROR
sp_lang_line_ctl(TR_SEQUENCE *,TR_CHAR_INFO * ci,TR_LL_CTL *);
TR_ERROR
sp_stop_lang_line_op(TR_SEQUENCE *,TR_CHAR_INFO *);
TR_ERROR sp_pop_box_line(
		struct tr_sequence * sq,
		struct tr_char_info *,
		TR_PTR);
void
sp_unmapping_element(TR_ELEMENT * el);
void
sp_free_box_line(TR_SEQUENCE *,TR_BOX_LINE *);
void
sp_new_box(TR_SEQUENCE *,TR_BOX *);
void
sp_free_box(TR_SEQUENCE *,TR_BOX *);

TR_BOX_OP string_pic_op = {
	sp_new_sequence,	/* new_sequence */
	0,	/** init_thread */
	0,	/** exit_thread */

	sp_unmapping_element,	/* unmapping_element */
	sp_new_box_line,	/* new_box_line */
	sp_free_box_line,	/* free_box_line */
	sp_new_box,		/** new_box */
	sp_free_box,		/* free_box */
	sp_start_lang_line_op,	/* start_lang_line_op */
	sp_stop_lang_line_op,	/* _stop_lang_line_op */
	sp_put_box_line,	/* put_box_line */
	sp_pop_box_line,	/* pop_box_line */
	sp_lang_line_ctl,	/* lang_line_ctl */
	0,
	0,

	{0,0},
	0
};


void
sp_unmapping_element(TR_ELEMENT * el)
{
SP_ELEMENT_WORK * ew;
	ew = el->work;
	if ( ew == 0 )
		return;
	if ( ew->pic.pic )
		d_f_ree(ew->pic.pic);
	if ( ew->str )
		d_f_ree(ew->str);
	if ( ew->ptr )
		d_f_ree(ew->ptr);
	_tr_free_attr(ew->attr);
}

int
sp_new_sequence(TR_SEQUENCE * sq,void * work)
{
SP_WORK * w;
	w = d_alloc(sizeof(*w));
	memset(w,0,sizeof(*w));
	sq->work = w;
	return 0;
}

void
sp_new_box_line(TR_SEQUENCE * sq,TR_BOX_LINE * bl)
{
SP_BOX_LINE_WORK * bw;
	bw = d_alloc(sizeof(*bw));
	memset(bw,0,sizeof(*bw));
	bl->work = bw;
}

void
sp_free_box_line(TR_SEQUENCE * sq,TR_BOX_LINE * bl)
{
	d_f_ree(bl->work);
}

void
sp_new_box(TR_SEQUENCE *sq,TR_BOX * b)
{
SP_BOX_WORK * spbw;
	spbw = d_alloc(sizeof(*spbw));
	memset(spbw,0,sizeof(*spbw));
	b->work = spbw;
}

void
sp_free_box(TR_SEQUENCE * sq,TR_BOX * b)
{
	d_f_ree(b->work);
}

void
sp_start_lang_line_op(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci)
{
SP_WORK * spw;
	spw = sq->work;
	spw->ci = *ci;
	spw->element_ptr = ci->lang_line->element_list;
	if ( spw->ci.box_line->attr.default_dir & VSD_V )
		spw->el_dir = VSD_V;
	else	spw->el_dir = VSD_H;
	if ( spw->ci.lang_line->dir & spw->el_dir )
		spw->rotate = 0;
	else	spw->rotate = 1;
	if ( spw->el_dir == VSD_V ) {
		if ( spw->rotate == 0 )
			spw->el_type = SPWT_ILLEAGAL_ELEMENT;
		else 	spw->el_type = SPWT_STANDARD_ELEMENT;
	}
	else {
		if ( spw->ci.lang_line->dir &
				spw->ci.lang->dir_flags )
			spw->el_type = SPWT_STANDARD_ELEMENT;
		else	spw->el_type = SPWT_ILLEAGAL_ELEMENT;
	}
}



int
sp_min_line_pixels(TR_BOX_LINE * bl)
{
int ret;
TR_LANG_LINE * ll;
TR_ELEMENT * el;
SP_ELEMENT_WORK * ew;
	ret = 0;
	for ( ll = bl->lang_list ; ll ; ll = ll->box_next ) {
		for ( el = ll->element_list ; el ; el = el->ll_next ) {
			if ( el->cr )
				continue;
			ew = el->work;
			if ( ew == 0 )
				continue;
			ret += ew->pic.width;
		}
	}
	return ret;
}

void
sp_line_dir_metric(TR_SEQUENCE * sq,TR_CHAR_INFO * ci)
{
SP_WORK * spw;
int px;
int target_line_pix;
int start_offset;
REAL1	pitch;
int max_pitch;
int el_nos;
TR_ELEMENT * el;
SP_BOX_LINE_WORK * blw;
int ofs;
SP_ELEMENT_WORK * ew;
int sum_pitch,_pitch;
REAL1 sum_pitch_f;
	spw = sq->work;
	px = sp_min_line_pixels(ci->box_line);
	if ( ci->box->attr.max_line_pixels < 0 ) {
		if ( spw->real_line_pixels < px ) {
			spw->real_line_pixels = px;
			target_line_pix = px;
		}
		else 	target_line_pix
				= spw->real_line_pixels;
	}
	else {
		target_line_pix = ci->box->attr.max_line_pixels;
	}
	blw = ci->box_line->work;
	for ( el_nos = 0 , el = ci->box_line->el_head->next ;
		el != ci->box_line->el_head ;
		el = el->next )
		if ( el->cr == 0 )
			el_nos ++;
	max_pitch = target_line_pix - px;
	switch ( ci->box_line->attr.align ) {
	case TRT_CENTER:
		start_offset = (target_line_pix - px)/2;
		pitch = 0;
		break;
	case TRT_CAPITA_BASIS_ALL:
		if ( el_nos == 1 )
			goto line_start;
		start_offset = 0;
		pitch = ((REAL1)(target_line_pix - px))/(el_nos - 1);
		break;
	case TRT_CAPITA_BASIS:
		if ( blw->return_dir & VSD_FORWARD ) {
			start_offset = 0;
			pitch = 0;
		}
		else if ( blw->return_dir & VSD_REVERSE ) {
			start_offset = max_pitch;
			pitch = 0;
		}
		else {
			start_offset = 0;
			pitch = ((REAL1)max_pitch)/(el_nos - 1);
		}
		break;
	case TRT_LINE_START:
	line_start:
		if ( ci->box_line->attr.default_dir & VSD_FORWARD )
			start_offset = 0;
		else if ( ci->box_line->attr.default_dir & VSD_REVERSE )
			start_offset = max_pitch;
		else	er_panic("sp");
		break;
	case TRT_LINE_END:
		if ( ci->box_line->attr.default_dir & VSD_FORWARD )
			start_offset = max_pitch;
		else if ( ci->box_line->attr.default_dir & VSD_REVERSE )
			start_offset = 0;
		else	er_panic("sp");
		break;
	default:
		er_panic("sp_lr_line_metric");
	}
	ofs = start_offset;
	sum_pitch = 0;
	sum_pitch_f = 0;
	for ( el = ci->box_line->el_head->next ;
			el != ci->box_line->el_head;
			el = el->next ) {

		if ( el->cr )
			continue;
		ew = el->work;
		if ( ew == 0 )
			continue;
		if ( ci->box->attr.default_lattr.default_dir & VSD_H )
			ew->offset.x = ofs;
		else	ew->offset.y = ofs;

		if ( el->cr )
			continue;

		ofs = ofs + ew->pic.width;
		
		sum_pitch_f += pitch;
		_pitch = sum_pitch_f - sum_pitch;
		if ( sum_pitch + _pitch > max_pitch )
			_pitch = max_pitch - sum_pitch;
		sum_pitch += _pitch;

		ofs += _pitch;
	}
}

void
sp_line_height_metric_horizontal(TR_SEQUENCE * sq,TR_CHAR_INFO * ci)
{
int assent,dessent,_a;
int a,d;
TR_ELEMENT * el;
SP_ELEMENT_WORK * ew;
	assent = dessent = 0;
	for ( el = ci->box_line->el_head->next ;
			el != ci->box_line->el_head;
			el = el->next ) {

		if ( el->cr )
			continue;
		ew = el->work;
		if ( ew == 0 )
			continue;
		a = - ew->pic.r.tl.y;
		d = ew->pic.r.br.y;
		if ( assent < a )
			assent = a;
		if ( dessent < d )
			dessent = d;
	}
	ci->box_line->desent = d;
	ci->box_line->assent = a;
	ci->box_line->base_offset = _a = assent * ci->box_line->attr.pitch/10;
	ci->box_line->height = (assent + dessent)*
				ci->box_line->attr.pitch/10;
	for ( el = ci->box_line->el_head->next;
			el != ci->box_line->el_head;
			el = el->next ) {
		if ( el->cr )
			continue;
		ew = el->work;
		if ( ew == 0 )
			continue;
		ew->offset.y = _a;
	}
}

typedef struct lhm_t {
	struct lhm_t *		next;
	LC_FONT *		font;
	int			size;
	short			desent;
	short			assent;
} LHM_T;

LHM_T * sp_set_lhm(LHM_T * t,SP_ELEMENT_WORK * ew);
LHM_T * sp_get_lhm(LHM_T * t,SP_ELEMENT_WORK * ew);
void sp_free_lhm(LHM_T * t);


LHM_T *
sp_set_lhm(LHM_T * t,SP_ELEMENT_WORK * ew)
{
LHM_T * ret;
	for ( ret = t ; ret ; ret = ret->next ) {
		if ( ew->font == ret->font &&
				ew->size == ret->size  ) {
			if ( -ew->pic.r.tl.x > ret->assent )
				ret->desent = -ew->pic.r.tl.x;
			if ( ew->pic.r.br.x > ret->desent )
				ret->assent = ew->pic.r.br.x;
			return t;
		}
	}
	ret = d_alloc(sizeof(*ret));
	ret->font = ew->font;
	ret->size = ew->size;
	ret->assent = ew->pic.r.br.x;
	ret->desent = -ew->pic.r.tl.x;
	ret->next = t;
	return ret;
}

LHM_T *
sp_get_lhm(LHM_T * t,SP_ELEMENT_WORK * ew)
{
LHM_T * ret;
	for ( ret = t ; ret ; ret = ret->next ) {
		if ( ew->font == ret->font &&
				ew->size == ret->size ) {
			return ret;
		}
	}
	return 0;
}

void
sp_free_lhm(LHM_T * t)
{
LHM_T * u;
	for ( ; t ; ) {
		u = t->next;
		d_f_ree(t);
		t = u;
	}
}

void
sp_line_height_metric_vertical(TR_SEQUENCE * sq,TR_CHAR_INFO * ci)
{
LHM_T * t,* u;
TR_LANG_LINE * ll;
TR_ELEMENT * el;
SP_ELEMENT_WORK * ew;
int width,w;
int bo;
	t = 0;
	for ( ll = ci->box_line->lang_list ; ll ; ll = ll->box_next ) {
		for ( el = ll->element_list ; el ; el = el->ll_next ) {
			if ( el->cr )
				continue;
			ew = el->work;
			t = sp_set_lhm(t,ew);
		}
	}
	width = 0;
	for ( u = t ; u ; u = u->next ) {
		w = u->assent + u->desent;
		if ( w > width )
			width = w;
	}
	ci->box_line->desent = width/2;
	ci->box_line->assent = width - ci->box_line->desent;
	bo = ci->box_line->base_offset = ci->box_line->assent * ci->box_line->attr.pitch / 10;
	ci->box_line->height = width * ci->box_line->attr.pitch / 10;
	for ( ll = ci->box_line->lang_list ; ll ; ll = ll->box_next ) {
		for ( el = ll->element_list ; el ; el = el->ll_next ) {
			ew = el->work;
			u = sp_get_lhm(t,ew);
			ew->offset.x =
				- (bo - (u->desent + u->assent)/2 + u->assent);
		}
	}
	sp_free_lhm(t);
}

void
sp_line_height_metric(TR_SEQUENCE * sq,TR_CHAR_INFO * ci)
{
int offset;
TR_BOX_LINE * bl;
SP_BOX_LINE_WORK * bw;
TR_BOX * b;
int flags;
	if ( (flags = (ci->box_line->attr.default_dir & VSD_H)) != 0 )
		sp_line_height_metric_horizontal(sq,ci);
	else	sp_line_height_metric_vertical(sq,ci);
	b = ci->box;
	for ( offset = 0, bl = ci->box->line_list ;
			bl ; bl = bl->next ) {
		bl->doc_start_offset = offset;
		bw = bl->work;
		if ( flags ) {
			bw->offset.y = b->start_px_offset + bl->doc_start_offset;
			bw->offset.x = 0;
		}
		else {
			bw->offset.x = b->start_px_offset + bl->doc_start_offset;
			bw->offset.y = 0;
		}
		offset += bl->height;
	}
}


TR_ERROR
sp_last_ll_destroy(TR_SEQUENCE * sq,SP_WORK * spw)
{
TR_LANG_LINE * ll,* ll_prev;
TR_ERROR err;
	ll_prev = 0;
	ll = _tr_next_lang_line(spw->ci.lang_line);
	for ( ; ll ; ll = _tr_next_lang_line(ll) ) {
		if ( ll->box_line != spw->ci.box_line )
			break;
		ll_prev = ll;
	}
	if ( ll_prev == 0 ) {
		err.code = TRE_INSERT_CHAR;
		return err;
	}
	_tr_destroy_lang_line(sq,ll_prev);
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	return err;
}


TR_ERROR
sp_last_el_destroy(TR_SEQUENCE * sq,SP_WORK * spw)
{
TR_ELEMENT * el;
TR_ERROR err;
	if ( spw->element_ptr == 0 ||
		spw->element_ptr->ll_next == 0 ) {
		err.code = TRE_INSERT_CHAR;
		return err;
	}
	for ( el = spw->element_ptr ; el->ll_next ; el = el->ll_next );
	_tr_destroy_element(sq,spw->ci.lang_line,el);
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	return err;
}

TR_ERROR
sp_line_check(TR_SEQUENCE * sq,SP_WORK * spw)
{
int min;
TR_ERROR err;
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	if ( spw->ci.box->attr.max_line_pixels < 0 ) {
		return err;
	}
	else {
		for ( ; ; ) {
			min = sp_min_line_pixels(spw->ci.box_line);
			if ( min <= spw->ci.box->attr.max_line_pixels )
				return err;
			err = sp_last_ll_destroy(sq,spw);
			if ( err.code != TRE_OK )
				break;
		}
		for ( ; ; ) {
			min = sp_min_line_pixels(spw->ci.box_line);
			if ( min <= spw->ci.box->attr.max_line_pixels )
				return err;
			err = sp_last_el_destroy(sq,spw);
			if ( err.code != TRE_OK )
				break;
		}
		return err;
	}
}

TR_ERROR
sp_line_height_check(TR_SEQUENCE * sq,TR_CHAR_INFO * ci)
{
int ci_flag;
TR_BOX_LINE * bl;
TR_ERROR err;
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	if ( ci->box->attr.max_doc_dir_pixels < 0 )
		return err;
	ci_flag = 0;
	for ( bl = ci->box->line_list ; bl ; bl = bl->next ) {
		if ( bl->doc_start_offset + ci->box->start_px_offset
				> ci->box->attr.max_doc_dir_pixels )
			goto over;
		if ( bl == ci->box_line ) {
			ci_flag = 1;
		}
	}
	return err;
over:
	if ( ci_flag == 0 )
		err.code = TRE_INSERT_LINE;
	for ( ; bl ; bl = bl->next ) {
		if ( bl == ci->box_line )
			break;
	}
	if ( bl == 0 )
		er_panic("sp_line_height_check");
	_tr_destroy_box_line_list(sq,bl->next);
	bl->next = 0;
	return err;
}

TR_ERROR
sp_new_element(TR_SEQUENCE * sq,SP_WORK * spw)
{
TR_ELEMENT * elp;
SP_ELEMENT_WORK * w;
TR_ERROR err;
	err = _tr_new_element(0,sq,spw->ci.lang_line);
	if ( err.code != TRE_OK )
		return err;
	for ( elp = spw->ci.lang_line->element_list ; elp->ll_next ;
			elp = elp->ll_next );
	spw->element_ptr = elp;
	w = d_alloc(sizeof(*w));
	memset(w,0,sizeof(*w));
	elp->work = w;
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	return err;
}

void
sp_get_pic(SP_WORK * spw,SP_ELEMENT_WORK * ew,TR_CHAR_INFO * ci)
{
LC_STRING_PIC pic;
int w;
	if ( spw->rotate ) {
		get_string_pic_font(&pic,ew->font,ew->size,ew->str,ew->len,ci->lang_line->dir);
		pic_rotate_90(&ew->pic,&pic);
		d_f_ree(pic.pic);
	}
	else {
		get_string_pic_font(&ew->pic,ew->font,ew->size,ew->str,ew->len,ci->lang_line->dir);
		if ( ew->len == 1 && spw->el_dir == VSD_V ) {
			w = ew->pic.r.br.x - ew->pic.r.tl.x;
			ew->pic.r.br.x = w/2;
			ew->pic.r.tl.x = w - w/2;
			ew->pic.r.br.y -= ew->pic.r.tl.y;
			ew->pic.r.tl.y = 0;
			ew->pic.width = ew->pic.r.br.y;
		}
	}
}

TR_ERROR
sp_flush_element(TR_SEQUENCE * sq,TR_CHAR_INFO * ci,SP_WORK * spw)
{
SP_ELEMENT_WORK * ew;
TR_ERROR err;
TR_ERROR ret;
	if ( spw->element_ptr == 0 )
		er_panic("sp_flush_element");
	ret.code = TRE_OK;
	ret.subcode = TRE_OK_NONE;
	if ( spw->element_ptr->cr )
		return ret;
	ew = spw->element_ptr->work;
	if ( ew == 0 )
		return ret;
	if ( ew->pic.pic )
		d_f_ree(ew->pic.pic);
	memset(&ew->pic,0,sizeof(ew->pic));
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	if ( ew->len == 0 )
		return err;
	sp_get_pic(spw,ew,ci);
	ew->ll_dir = ci->lang_line->dir;
	ew->ln_dir = ci->box_line->attr.default_dir;
	return sp_line_check(sq,spw);
}

TR_ERROR
sp_put_box_line(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci,
	TR_PTR p,
	TR_CHAR_ELEMENT * el)
{
SP_WORK * spw;
SP_ELEMENT_WORK * ew;
TR_ERROR err;
LCF_SET *set;
LC_WS_COND wsc;
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	spw = sq->work;
	if ( spw->element_ptr == 0 ) {
		err = sp_new_element(sq,spw);
		if ( err.code != TRE_OK )
			return err;
	}
	set = 0;
	ew = spw->element_ptr->work;
	if ( ew->str ) {
		if ( el->d.type < 0 )
			goto new_el;
		wsc.cond = WSC_PIC_REQUIRED;
		set = ws_convert(el->d.ws.ws,&wsc,el->d.ws.size,&el->ch,1,
				CBF_SRC_PLANE|CBF_DST_PLANE);
		if ( set == 0 || set->font == 0 )
			er_panic("put_char");
		if ( set->font != ew->font )
			goto new_el;
		if ( set->size != ew->size )
			goto new_el;
		if ( _tr_cmp_attr(el->attr,ew->attr) )
			goto new_el;
		if ( spw->el_type == SPWT_ILLEAGAL_ELEMENT )
			goto new_el;
		goto next;
	new_el:
		err = sp_flush_element(sq,ci,spw);
		if (err.code != TRE_OK )
			goto end;
		spw->element_ptr = spw->element_ptr->ll_next;
		if ( spw->element_ptr == 0 ) {
			err = sp_new_element(sq,spw);
			if ( err.code != TRE_OK )
				goto end;
			ew = spw->element_ptr->work;
		}
		else {
			ew = spw->element_ptr->work;
			if ( ew->str )
				d_f_ree(ew->str);
			if ( ew->ptr )
				d_f_ree(ew->ptr);
			ew->str = 0;
			ew->len = 0;
			ew->glue_start = 0;
			ew->glue_end = 0;
		}
	next:
		ew->font = set->font;
		ew->size = set->size;
		ew->len ++;
		ew->str = d_re_alloc(ew->str,
				sizeof(L_CHAR)*ew->len);
		ew->ptr = d_re_alloc(ew->ptr,
				sizeof(TR_PTR)*ew->len);
		ew->str[ew->len-1] = set->ch;
		ew->ptr[ew->len-1] = p;
	}
	else {
		if ( el->d.type < 0 )
			goto new_el;
		wsc.cond = WSC_PIC_REQUIRED;
		set = ws_convert(el->d.ws.ws,&wsc,el->d.ws.size,&el->ch,1,
				CBF_SRC_PLANE|CBF_DST_PLANE);

		ew->len = 1;
		ew->str = d_alloc(sizeof(L_CHAR)*ew->len);
		ew->ptr = d_alloc(sizeof(TR_PTR)*ew->len);
		if ( el->d.type >= 0 ) {
			ew->font = set->font;
			ew->size = set->size;
			ew->str[ew->len-1] = set->ch;
			ew->ptr[ew->len-1] = p;
		}
		else {
		TR_METRIC m;
			m = (*sq->box_op->obj_op->get_obj_metric)
				(el->d.obj.obj,ci->lang_line->dir);
			ew->pic.r = m.size;
			if ( spw->el_dir  == VSD_H )
				ew->pic.width = m.end.x;
			else	ew->pic.width = m.end.y;
			ew->obj = (*sq->box_op->obj_op->copy_obj)(el->d.obj.obj);
			ew->str[ew->len-1] = set->ch;
			ew->ptr[ew->len-1] = p;
		}
		ew->attr = _tr_copy_attr(el->attr);
	}
	ew->glue_end = TRW_ZERO;
	if ( set )
		d_f_ree(set);
end:
	return err;
}

TR_ERROR
sp_lang_line_ctl_weight(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci,
	TR_LL_CTL * ctl)
{
SP_WORK * spw;
SP_ELEMENT_WORK * ew;
TR_ERROR err;
	spw = sq->work;
	if ( spw->element_ptr == 0 ) {
		err = sp_new_element(sq,spw);
		if ( err.code != TRE_OK )
			return err;
	}
	ew = spw->element_ptr->work;
	if ( ew->len == 0 ) {
		if ( ew->glue_start < ctl->w.weight )
			ew->glue_start = ctl->w.weight;
	}
	else {
		if ( ew->glue_end < ctl->w.weight )
			ew->glue_end = ctl->w.weight;
	}
	if ( ew->glue_end == TRW_RABER ) {
		err = sp_flush_element(sq,ci,spw);
		if (err.code != TRE_OK )
			return err;
		spw->element_ptr = spw->element_ptr->ll_next;
		if ( spw->element_ptr == 0 ) {
			err = sp_new_element(sq,spw);
			if ( err.code != TRE_OK )
				return err;
			ew = spw->element_ptr->work;
		}
		else {
			ew = spw->element_ptr->work;
			if ( ew->str )
				d_f_ree(ew->str);
			ew->str = 0;
			ew->len = 0;
			ew->glue_start = 0;
			ew->glue_end = 0;
		}
	}
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	return err;
}

TR_ERROR
sp_lang_line_ctl_return_code(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci,
	TR_LL_CTL * ctl)
{
TR_ERROR err;
SP_BOX_LINE_WORK * blw;
	blw = ci->box_line->work;
	blw->return_dir = ctl->rc.dir;
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	return err;
}


TR_ERROR
sp_lang_line_ctl(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci,
	TR_LL_CTL * ctl)
{
TR_ERROR err;
	switch ( ctl->h.cmd ) {
	case LLC_WEIGHT:
		return sp_lang_line_ctl_weight(sq,ci,ctl);
	case LLC_RETURN_CODE:
		return sp_lang_line_ctl_return_code(sq,ci,ctl);
	default:
		er_panic("sp_lang_line_ctl");
	}
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	return err;
}


TR_ERROR
sp_stop_lang_line_op(
	TR_SEQUENCE * sq,
	TR_CHAR_INFO * ci)
{
TR_ERROR err;
SP_WORK * spw;
	spw = sq->work;
	if ( spw->element_ptr ) {
		err = sp_flush_element(sq,ci,spw);
		if ( err.code != TRE_OK )
			return err;
	}
	sp_line_height_metric(sq,ci);
	err = sp_line_height_check(sq,ci);
	if ( err.code != TRE_OK )
		return err;
	sp_line_dir_metric(sq,ci);
	err.code = TRE_OK;
	err.subcode = TRE_OK_NONE;
	return err;
}

TR_ERROR sp_pop_box_line(
		struct tr_sequence * sq,
		struct tr_char_info * ci,
		TR_PTR p)
{
TR_ELEMENT * el, * el2;
SP_ELEMENT_WORK * ew;
int i;
SP_WORK * spw;
	spw = sq->work;
	for ( el = ci->lang_line->element_list ; el ; el = el->ll_next ) {
		ew = el->work;
		for ( i = 0 ; i < ew->len ; i ++ ) {
			if ( tr_ptr_cmp(ew->ptr[i],p) == 0 ) {
				if ( i == 0 )
					goto el_destroy;
				ew->len = i;
				sp_get_pic(spw,ew,ci);
				el = el->ll_next;
				goto el_destroy;
			}
		}
	}
	goto check;
el_destroy:
	for ( ; el ; ) {
		el2 = el->ll_next;
		_tr_destroy_element(sq,ci->lang_line,el);
		el = el2;
	}
check:
	spw = sq->work;
	return sp_line_check(sq,spw);
}

void
sp_get_pic_element(PIC * pi,TR_SEQUENCE * sq,TR_ELEMENT * el,I_POINT p)
{
SP_ELEMENT_WORK * ew;
PIC_ELEMENT * pe;
int len;
TR_ATTR * a;
TR_REFERENCE ref,* _ref;
AVT_NODE * n;
int tr_ref_cmp();
int cc;
int w,h,i;
int flag;
	ew = el->work;
	if ( ew == 0 )
		return;
	if ( el->cr )
		return;
	pe = d_alloc(sizeof(*pe));
	memset(pe,0,sizeof(*pe));
	pe->p = ew->pic;
	pe->p.pic = d_alloc(
			len = (w = pe->p.r.br.x - pe->p.r.tl.x) *
			(h = pe->p.r.br.y - pe->p.r.tl.y)
			);
	memcpy(pe->p.pic,ew->pic.pic,len);
	p = p_addi(p,ew->offset);
	pe->p.r.tl = p_addi(p,pe->p.r.tl);
	pe->p.r.br = p_addi(p,pe->p.r.br);
	a = _tr_get_attr(ew->attr,TRAT_REFERENCE);
	if ( a ) {
		ref.id = a->r.id;
		n = avt_search(sq->ref,&ref,tr_ref_cmp);
		if ( n ) {
			_ref = n->data;
			if ( _ref )
				pe->ref = ll_copy_str(_ref->data);
			else pe->ref = 0;
		}
		else 	pe->ref = 0;
		pe->flags |= PLF_ANCOR;
	}
	a = _tr_get_attr(ew->attr,TRAT_COLOR);
	if ( a )
		cc = a->c.color;
	else	cc = 0;

	flag = 0;
	pe->data = d_alloc(sizeof(*pe->data)*w*h);
	pe->alpha = d_alloc(w*h);
	for ( i = 0 ; i < w*h ; i ++ ) {
		if ( pe->p.pic[i] == 255 )
			pe->data[i] = C_TRANSPARENT;
		else {
			pe->data[i] = cc;
			if ( pe->p.pic[i] != 0 )
				flag = 1;
		}
		pe->alpha[i] = pe->p.pic[i];
	}
	if ( flag == 0 ) {
		if ( pe->alpha )
			d_f_ree(pe->alpha);
		pe->alpha = 0;
	}
	if ( pe->flags & PLF_ANCOR ) {
		if ( (ew->ln_dir & VSD_H) ) {
			for ( i = 0 ; i < w ; i ++ )
				pe->data[i + w*(h-1)] = cc;
			if ( pe->alpha )
				for ( i = 0 ; i < w ; i ++ )
					pe->alpha[i + w*(h-1)] = 0;
		}
		else if ( (ew->ll_dir & VSD_H) ) {
			for ( i = 0 ; i < h ; i ++ )
				pe->data[i*w] = cc;
			if ( pe->alpha )
				for ( i = 0 ; i < h ; i ++ )
					pe->alpha[i*w] = 0;
		}
			for ( i = 0 ; i < h ; i ++ )
				pe->data[i*w + w-1] = cc;
			if ( pe->alpha )
				for ( i = 0 ; i < h ; i ++ )
					pe->alpha[i*w + w-1] = 0;
		else {
		}
	}
	d_f_ree(pe->p.pic);
	pe->p.pic = 0;

	pe->next = pi->list;
	pi->list = pe;

	insert_rect_i(&pi->rect,pe->p.r.tl);
	insert_rect_i(&pi->rect,pe->p.r.br);
}

void
sp_get_pic_box_line(PIC * pi,TR_SEQUENCE * sq,TR_BOX_LINE * bl,I_POINT p)
{
SP_BOX_LINE_WORK * blw;
TR_ELEMENT * el;
	blw = bl->work;
	p = p_addi(p,blw->offset);
	for ( el = bl->el_head->next ; el != bl->el_head ; el = el->next )
		sp_get_pic_element(pi,sq,el,p);
}

void
sp_get_pic_box(PIC * pi,TR_SEQUENCE * sq,TR_BOX * b,I_POINT p)
{
SP_BOX_WORK * bw;
TR_BOX_LINE * bl;
	bw = b->work;
	for ( bl = b->line_list ; bl ; bl = bl->next )
		sp_get_pic_box_line(pi,sq,bl,bw->offset);
}

void
sp_get_sequence_pic(PIC * pi,TR_SEQUENCE * sq)
{
TR_BOX * b;
I_POINT p;
	p.x = p.y = 0;
	pi->rect.tl.x = pi->rect.tl.y = 0;
	pi->rect.br.x = pi->rect.br.y = -1;
	pi->list = 0;
	for ( b = sq->box_ring->next ; b != sq->box_ring ; b = b->next )
		sp_get_pic_box(pi,sq,b,p);
}




