983 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			983 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * lws abstract display
 | |
|  *
 | |
|  * Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
|  * of this software and associated documentation files (the "Software"), to
 | |
|  * deal in the Software without restriction, including without limitation the
 | |
|  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 | |
|  * sell copies of the Software, and to permit persons to whom the Software is
 | |
|  * furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in
 | |
|  * all copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | |
|  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 | |
|  * IN THE SOFTWARE.
 | |
|  *
 | |
|  * Display List LHP layout
 | |
|  *
 | |
|  * The basic flow is logical elements exist in a stack as they are parsed, the
 | |
|  * job of lhp_displaylist_layout() is to translate these into a tree of DLOs,
 | |
|  * having parent-child relationships with (x,y) of the DLO box being an offset
 | |
|  * into a local origin formed from the DLO parent box (which in turn may be
 | |
|  * a child with its origin defined by its parent, etc).
 | |
|  *
 | |
|  * The element stack only exists while it and its parent elements are being
 | |
|  * parsed, it goes out of scope as the element ends.  So we must create related
 | |
|  * DLOs by stream-parsing, while we still have everything relevant to hand.
 | |
|  *
 | |
|  * This gets us out of having to run around fixing up DLO (x,y) as we do the
 | |
|  * layout, since the DLO parent-child relationships are static even if their
 | |
|  * content size isn't.
 | |
|  *
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <private-lib-core.h>
 | |
| #include "private-lib-drivers-display-dlo.h"
 | |
| 
 | |
| /*
 | |
|  * HTML Elements we can deal with for layout
 | |
|  */
 | |
| 
 | |
| enum {
 | |
| 	/* 0 is no match */
 | |
| 	LHP_ELEM_BR = 1,
 | |
| 	LHP_ELEM_DIV,
 | |
| 	LHP_ELEM_TABLE,
 | |
| 	LHP_ELEM_TR,
 | |
| 	LHP_ELEM_TD,
 | |
| 	LHP_ELEM_IMG,
 | |
| };
 | |
| 
 | |
| static const struct {
 | |
| 	const char	*elem;
 | |
| 	uint8_t		elem_len;
 | |
| } elems[] = {
 | |
| 	{ "br",		2 },
 | |
| 	{ "div",	3 },
 | |
| 	{ "table",	5 },
 | |
| 	{ "tr",		2 },
 | |
| 	{ "td",		2 },
 | |
| 	{ "img",	3 },
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * Newline moves the psb->cury to cover text that was already placed using the
 | |
|  * old psb->cury as to top of it.  So a final newline on the last line of text
 | |
|  * does not create an extra blank line.
 | |
|  */
 | |
| 
 | |
| static const lws_fx_t two = { 2,0 };
 | |
| 
 | |
| static void
 | |
| newline(lhp_ctx_t *ctx, lhp_pstack_t *psb, lhp_pstack_t *ps,
 | |
| 	lws_displaylist_t *dl)
 | |
| {
 | |
| 	int16_t group_baseline = 9999, group_height = 0;
 | |
| 	lws_fx_t line_height = { 0, 0 }, w, add, ew, t1;
 | |
| 	const struct lcsp_atr *a;
 | |
| 	lws_dlo_t *dlo, *d, *d1;
 | |
| 	int t = 0;
 | |
| 
 | |
| 	if (!psb || !ps) {
 | |
| 		lwsl_err("%s: psb/ps NULL!\n", __func__);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	dlo = (lws_dlo_t *)psb->dlo;
 | |
| 
 | |
| 	lws_fx_add(&w, lws_csp_px(ps->css_padding[CCPAS_LEFT], ps),
 | |
| 		       lws_csp_px(ps->css_padding[CCPAS_RIGHT], ps));
 | |
| 
 | |
| 	if (lws_fx_comp(&w, &psb->widest) > 0)
 | |
| 		psb->widest = w;
 | |
| 
 | |
| 	if (!dlo || !dlo->children.tail)
 | |
| 		return;
 | |
| 
 | |
| 	d = lws_container_of(dlo->children.tail, lws_dlo_t, list);
 | |
| 
 | |
| 	/*
 | |
| 	 * We may be at the end of a line of text
 | |
| 	 *
 | |
| 	 * Figure out the biggest height on the line, and the total width
 | |
| 	 */
 | |
| 
 | |
| 	while (d) {
 | |
| 		t |= d->_destroy == lws_display_dlo_text_destroy;
 | |
| 		/* find the "worst" height on the line */
 | |
| 		if (lws_fx_comp(&d->box.h, &line_height) > 0)
 | |
| 			line_height = d->box.h;
 | |
| 
 | |
| 		if (d->_destroy == lws_display_dlo_text_destroy) {
 | |
| 			lws_dlo_text_t *text = lws_container_of(d,
 | |
| 						lws_dlo_text_t, dlo.list);
 | |
| 
 | |
| 			if (text->font_y_baseline < group_baseline)
 | |
| 				group_baseline = text->font_y_baseline;
 | |
| 			if (text->font_height > group_height)
 | |
| 				group_height = text->font_height;
 | |
| 		}
 | |
| 
 | |
| 		if (!d->flag_runon)
 | |
| 			break;
 | |
| 		d = lws_container_of(d->list.prev, lws_dlo_t, list);
 | |
| 	};
 | |
| 
 | |
| 	/* mark the related text dlos with information about group bl and h,
 | |
| 	 * offset box y to align to group baseline if necessary */
 | |
| 
 | |
| 	d1 = d;
 | |
| 	while (d) {
 | |
| 		if (d->_destroy == lws_display_dlo_text_destroy) {
 | |
| 			lws_dlo_text_t *t1 = lws_container_of(d1,
 | |
| 						lws_dlo_text_t, dlo.list);
 | |
| 			lws_fx_t ft;
 | |
| 
 | |
| 			t1->group_height = group_height;
 | |
| 			t1->group_y_baseline = group_baseline;
 | |
| 
 | |
| 			ft.whole = (t1->font_height - t1->font_y_baseline) -
 | |
| 					(group_height - group_baseline);
 | |
| 			ft.frac = 0;
 | |
| 
 | |
| 			lws_fx_sub(&t1->dlo.box.y,  &t1->dlo.box.y, &ft);
 | |
| 		}
 | |
| 		if (!d1->list.next)
 | |
| 			break;
 | |
| 		d1 = lws_container_of(d1->list.next, lws_dlo_t, list);
 | |
| 	};
 | |
| 
 | |
| 	w = psb->curx;
 | |
| 	ew = ctx->ic.wh_px[0];
 | |
| 	if (psb->css_width && psb->css_width->unit != LCSP_UNIT_NONE)
 | |
| 		ew = *lws_csp_px(psb->css_width, psb);
 | |
| 	lws_fx_sub(&ew, &ew, lws_csp_px(ps->css_margin[CCPAS_RIGHT], ps));
 | |
| 	lws_fx_sub(&ew, &ew, lws_csp_px(ps->css_padding[CCPAS_RIGHT], ps));
 | |
| 
 | |
| 	if (lws_fx_comp(&w, &psb->widest) > 0)
 | |
| 		psb->widest = w;
 | |
| 
 | |
| 	if (!t) /* no textual children to newline (eg, <div></div>) */
 | |
| 		return;
 | |
| 
 | |
| 	 /*
 | |
| 	  * now is our chance to fix up dlos that are part of the line for
 | |
| 	  * text-align rule of the container.
 | |
| 	  */
 | |
| 
 | |
| 	a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_TEXT_ALIGN);
 | |
| 	if (a) {
 | |
| 
 | |
| 		switch (a->propval) {
 | |
| 		case LCSP_PROPVAL_CENTER:
 | |
| 			add = *lws_csp_px(ps->css_padding[CCPAS_LEFT], ps);
 | |
| 			lws_fx_sub(&t1, &ew, &w);
 | |
| 			lws_fx_div(&t1, &t1, &two);
 | |
| 			lws_fx_add(&add, &add, &t1);
 | |
| 			goto fixup;
 | |
| 		case LCSP_PROPVAL_RIGHT:
 | |
| 			lws_fx_sub(&add, &ew, &w);
 | |
| 			lws_fx_sub(&add, &add, &d->box.x);
 | |
| 
 | |
| fixup:
 | |
| 			lws_fx_add(&t1, &add, &w);
 | |
| 			if (lws_fx_comp(&t1, &psb->widest) > 0)
 | |
| 				psb->widest = t1;
 | |
| 
 | |
| 			do {
 | |
| 				lws_fx_add(&d->box.x, &d->box.x, &add);
 | |
| 				if (!d->list.next)
 | |
| 					break;
 | |
| 				d = lws_container_of(d->list.next, lws_dlo_t,
 | |
| 							list);
 | |
| 			} while (1);
 | |
| 			break;
 | |
| 		default:
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	lws_fx_add(&psb->cury, &psb->cury, &line_height);
 | |
| 	lws_fx_set(psb->curx, 0, 0);
 | |
| 	psb->dlo_set_curx = NULL;
 | |
| 	psb->dlo_set_cury = NULL;
 | |
| 	psb->runon = 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| lhp_set_dlo_padding_margin(lhp_pstack_t *ps, lws_dlo_t *dlo)
 | |
| {
 | |
| 	int n;
 | |
| 
 | |
| 	for (n = 0; n < 4; n ++) {
 | |
| 		if (ps->css_margin[n])
 | |
| 			dlo->margin[n] = *lws_csp_px(ps->css_margin[n], ps);
 | |
| 		else
 | |
| 			lws_fx_set(dlo->margin[n], 0, 0);
 | |
| 		if (ps->css_padding[n])
 | |
| 			dlo->padding[n] = *lws_csp_px(ps->css_padding[n], ps);
 | |
| 		else
 | |
| 			lws_fx_set(dlo->padding[n], 0, 0);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void
 | |
| lhp_set_dlo_adjust_to_contents(lhp_pstack_t *ps)
 | |
| {
 | |
| 	lhp_pstack_t *psb = lws_container_of(ps->list.prev, lhp_pstack_t, list);
 | |
| 	lws_dlo_dim_t dim;
 | |
| 
 | |
| 	lws_dlo_contents(ps->dlo, &dim);
 | |
| 	lws_display_dlo_adjust_dims(ps->dlo, &dim);
 | |
| 
 | |
| 	if (lws_fx_comp(&dim.w, &psb->widest) > 0)
 | |
| 		psb->widest = dim.w;
 | |
| 
 | |
| 	if (lws_fx_comp(&dim.h, &psb->deepest) > 0)
 | |
| 		psb->deepest = dim.h;
 | |
| }
 | |
| 
 | |
| static void
 | |
| runon(lhp_pstack_t *ps, lws_dlo_t *dlo)
 | |
| {
 | |
| 	dlo->flag_runon = (uint8_t)(ps->runon & 1);
 | |
| 	ps->runon = 1;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Handle end-of-div, table, tr, td retrospective dlo dimension adjustment
 | |
|  */
 | |
| 
 | |
| int
 | |
| lws_lhp_dlo_adjust_div_type_element(lhp_ctx_t *ctx, lhp_pstack_t *psb,
 | |
| 				    lhp_pstack_t *pst, lhp_pstack_t *ps,
 | |
| 				    int elem_match)
 | |
| {
 | |
| 	lws_dlo_rect_t *rect = (lws_dlo_rect_t *)ps->dlo;
 | |
| 	lws_fx_t t1, w, wd;
 | |
| 	char rd = 0;
 | |
| 
 | |
| 	/* need this to get bottom clearance for next block */
 | |
| 
 | |
| 	lws_fx_add(&ps->cury, &ps->cury,
 | |
| 		lws_csp_px(ps->css_padding[CCPAS_BOTTOM], ps));
 | |
| 
 | |
| 	if (psb && ps->dlo &&
 | |
| 	    ps->css_margin[CCPAS_LEFT]->propval == LCSP_PROPVAL_AUTO &&
 | |
| 	    ps->css_margin[CCPAS_RIGHT]->propval == LCSP_PROPVAL_AUTO) {
 | |
| 		lws_dlo_rect_t *re = (lws_dlo_rect_t *)ps->dlo;
 | |
| 
 | |
| 		/* h-center a div... find the available h space first */
 | |
| 		w = ctx->ic.wh_px[LWS_LHPREF_WIDTH];
 | |
| 		if (psb->css_width &&
 | |
| 		    psb->css_width->propval != LCSP_PROPVAL_AUTO)
 | |
| 			w = *lws_csp_px(psb->css_width, psb);
 | |
| 
 | |
| 		lws_fx_sub(&t1, &w, &re->dlo.box.w);
 | |
| 		lws_fx_div(&t1, &t1, &two);
 | |
| 		lws_fx_sub(&wd, &t1, &re->dlo.box.x);
 | |
| 
 | |
| 		lws_fx_add(&re->dlo.box.x, &re->dlo.box.x, &wd);
 | |
| 	}
 | |
| 
 | |
| 	/* fix up the dimensions of div rectangle */
 | |
| 	if (!rect) {
 | |
| 		lwsl_notice("%s: elem %d: NO RECT\n", __func__, elem_match);
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	lhp_set_dlo_adjust_to_contents(ps);
 | |
| 
 | |
| 	/* if a td, deal with columnar changes in width */
 | |
| 
 | |
| 	if (ps->dlo->col_list.owner) {
 | |
| 		lhp_table_col_t *tc = lws_container_of(
 | |
| 				ps->dlo->col_list.owner,
 | |
| 				lhp_table_col_t, col_dlos);
 | |
| 		lws_fx_t wdelta, ow;
 | |
| 
 | |
| 		ow = tc->width;
 | |
| 		lws_fx_set(tc->width, 0, 0);
 | |
| 
 | |
| 		/* discover the new width of column */
 | |
| 
 | |
| 		lws_start_foreach_dll(struct lws_dll2 *, c1,
 | |
| 				      lws_dll2_get_head(&tc->col_dlos)) {
 | |
| 			lws_dlo_t *dloc = lws_container_of(c1,
 | |
| 					lws_dlo_t, col_list);
 | |
| 
 | |
| 			if (lws_fx_comp(&dloc->box.w, &tc->width) > 0)
 | |
| 				tc->width = dloc->box.w;
 | |
| 		} lws_end_foreach_dll(c1);
 | |
| 
 | |
| 		/* new width - old column width */
 | |
| 		lws_fx_sub(&wdelta, &tc->width, &ow);
 | |
| 
 | |
| 		/*
 | |
| 		 * Update all dlos in our column (except
 | |
| 		 * ourselves) with the increased column width
 | |
| 		 */
 | |
| 
 | |
| 		lws_start_foreach_dll(struct lws_dll2 *, cold,
 | |
| 				      lws_dll2_get_head(&tc->col_dlos)) {
 | |
| 			lws_dlo_t *dloc = lws_container_of(cold,
 | |
| 					lws_dlo_t, col_list);
 | |
| 
 | |
| 			if (dloc != &rect->dlo)
 | |
| 				/* we already did this for the
 | |
| 				 * affected dlo */
 | |
| 				lws_fx_add(&dloc->box.w,
 | |
| 					   &dloc->box.w, &wdelta);
 | |
| 
 | |
| 			rd = 1;
 | |
| 
 | |
| 			/* ... and then all of their row-mates
 | |
| 			 * to the right also need their
 | |
| 			 * x adjusting then */
 | |
| 
 | |
| 			while (dloc->row_list.next) {
 | |
| 				dloc = lws_container_of(
 | |
| 					dloc->row_list.next,
 | |
| 					lws_dlo_t, row_list);
 | |
| 
 | |
| 				lws_fx_add(&dloc->box.x,
 | |
| 					   &dloc->box.x, &wdelta);
 | |
| 			}
 | |
| 		} lws_end_foreach_dll(cold);
 | |
| 	}
 | |
| 
 | |
| 	/* if a td, deal with row changes in height */
 | |
| 
 | |
| 	if (ps->dlo->row_list.owner) {
 | |
| 		lhp_table_row_t *tr = lws_container_of(
 | |
| 				ps->dlo->row_list.owner,
 | |
| 				lhp_table_row_t, row_dlos);
 | |
| 		lws_fx_t hdelta, oh;
 | |
| 
 | |
| 		oh = tr->height;
 | |
| 		lws_fx_set(tr->height, 0, 0);
 | |
| 
 | |
| 		/* discover the new width of column */
 | |
| 
 | |
| 		lws_start_foreach_dll(struct lws_dll2 *, r1,
 | |
| 				      lws_dll2_get_head(&tr->row_dlos)) {
 | |
| 			lws_dlo_t *dlor = lws_container_of(r1,
 | |
| 					lws_dlo_t, row_list);
 | |
| 
 | |
| 			if (lws_fx_comp(&dlor->box.h, &tr->height) > 0)
 | |
| 				tr->height = dlor->box.h;
 | |
| 		} lws_end_foreach_dll(r1);
 | |
| 
 | |
| 		/* new height - old row height */
 | |
| 		lws_fx_sub(&hdelta, &tr->height, &oh);
 | |
| 
 | |
| 		/*
 | |
| 		 * Update all dlos in our row (except
 | |
| 		 * ourselves) with the increased row height
 | |
| 		 */
 | |
| 
 | |
| 		lws_start_foreach_dll(struct lws_dll2 *, rold,
 | |
| 				      lws_dll2_get_head(&tr->row_dlos)) {
 | |
| 			lws_dlo_t *dlor = lws_container_of(rold,
 | |
| 					lws_dlo_t, row_list);
 | |
| 
 | |
| 			if (dlor != &rect->dlo)
 | |
| 				/* we already did this for the
 | |
| 				 * affected dlo */
 | |
| 				lws_fx_add(&dlor->box.h,
 | |
| 					   &dlor->box.h, &hdelta);
 | |
| 
 | |
| 			/* ... so all of their col-mates below
 | |
| 			 * also need their y adjusting then */
 | |
| 
 | |
| 			while (dlor->col_list.next) {
 | |
| 				dlor = lws_container_of(
 | |
| 					dlor->col_list.next,
 | |
| 					lws_dlo_t, col_list);
 | |
| 
 | |
| 				lws_fx_add(&dlor->box.y,
 | |
| 					   &dlor->box.y, &hdelta);
 | |
| 			}
 | |
| 
 | |
| 			rd = 1;
 | |
| 
 | |
| 		} lws_end_foreach_dll(rold);
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Row dimensions have to be reassessed?
 | |
| 	 */
 | |
| 
 | |
| 	if (rd) {
 | |
| 		lws_start_foreach_dll(struct lws_dll2 *, ro,
 | |
| 		       lws_dll2_get_head(&pst->dlo->children)) {
 | |
| 			lws_dlo_t *dlo = lws_container_of(ro, lws_dlo_t, list);
 | |
| 			lws_dlo_dim_t dim;
 | |
| 
 | |
| 			lws_dlo_contents(dlo, &dim);
 | |
| 			lws_display_dlo_adjust_dims(dlo, &dim);
 | |
| 		} lws_end_foreach_dll(ro);
 | |
| 	}
 | |
| 
 | |
| 	if (psb && ps->css_position->propval != LCSP_PROPVAL_ABSOLUTE) {
 | |
| 		/* parent should account for our margin */
 | |
| 		if (elem_match == LHP_ELEM_DIV) {
 | |
| 			lws_fx_add(&psb->curx, &psb->curx, &ps->widest);
 | |
| 			/* now we applied ps->widest, reset it */
 | |
| 			lws_fx_set(ps->widest, 0, 0);
 | |
| 			psb->dlo_set_curx = ps->dlo;
 | |
| 		} else {
 | |
| 			/* needed for margin between table cells */
 | |
| 			lws_fx_add(&psb->curx, &psb->curx, lws_csp_px(ps->css_margin[CCPAS_LEFT], ps));
 | |
| 			lws_fx_add(&psb->curx, &psb->curx, lws_csp_px(ps->css_margin[CCPAS_RIGHT], ps));
 | |
| 		}
 | |
| 
 | |
| 		if (elem_match != LHP_ELEM_TD) {
 | |
| 			if (ps->css_display->propval != LCSP_PROPVAL_INLINE_BLOCK) {
 | |
| 				lws_fx_add(&psb->cury, &psb->cury, &ps->dlo->box.h);
 | |
| 				psb->dlo_set_cury = ps->dlo;
 | |
| 			}
 | |
| 		//	lws_fx_add(&psb->cury, &psb->cury, &ps->dlo->margin[CCPAS_BOTTOM]);
 | |
| 		} else
 | |
| 			ps->widest = ps->dlo->box.w;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Generic LHP displaylist object layout callback... converts html elements
 | |
|  * into DLOs on the display list
 | |
|  */
 | |
| 
 | |
| lws_stateful_ret_t
 | |
| lhp_displaylist_layout(lhp_ctx_t *ctx, char reason)
 | |
| {
 | |
| 	lhp_pstack_t *psb = NULL, *pst = NULL, *psp = NULL,
 | |
| 		     *ps = lws_container_of(ctx->stack.tail, lhp_pstack_t, list);
 | |
| 	struct lws_context *cx = (struct lws_context *)ctx->user1;
 | |
| 	lws_dl_rend_t *drt = (lws_dl_rend_t *)ctx->user;
 | |
| 	lws_fx_t br[4], t1, indent, ox, w, h;
 | |
| 	const lws_display_font_t *f = NULL;
 | |
| 	lhp_table_col_t *tcol = NULL;
 | |
| 	lhp_table_row_t *trow = NULL;
 | |
| 	lws_dlo_t *abut_x, *abut_y;
 | |
| 	uint32_t col = 0xff000000;
 | |
| 	lws_dlo_text_t *txt;
 | |
| 	const lcsp_atr_t *a;
 | |
| 	lws_dlo_image_t u;
 | |
| 	const char *pname;
 | |
| 	char lastm = 0;
 | |
| 	int elem_match;
 | |
| 	lws_box_t box;
 | |
| 	char url[128];
 | |
| 	int n, s = 0;
 | |
| 
 | |
| 	/* default font choice */
 | |
| 	lws_font_choice_t fc = {
 | |
| 		.family_name		= "term, serif",
 | |
| 		.fixed_height		= 16,
 | |
| 		.weight			= 400,
 | |
| 	};
 | |
| 
 | |
| 	if (!ps->font) {
 | |
| 		a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_SIZE);
 | |
| 		if (a)
 | |
| 			fc.fixed_height = (uint16_t)a->u.i.whole;
 | |
| 
 | |
| 		a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_FAMILY);
 | |
| 		if (a)
 | |
| 			fc.family_name = (const char *)&a[1];
 | |
| 
 | |
| 		a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_WEIGHT);
 | |
| 		if (a) {
 | |
| 			switch (a->propval) {
 | |
| 			case LCSP_PROPVAL_BOLD:
 | |
| 				fc.weight = 700;
 | |
| 				break;
 | |
| 			case LCSP_PROPVAL_BOLDER:
 | |
| 				fc.weight = 800;
 | |
| 				break;
 | |
| 			default:
 | |
| 				if (a->u.i.whole)
 | |
| 					fc.weight = (uint16_t)a->u.i.whole;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		ps->font = lws_font_choose(cx, &fc);
 | |
| 	}
 | |
| 	f = ps->font;
 | |
| 
 | |
| 	psb = lws_css_get_parent_block(ctx, ps);
 | |
| 
 | |
| 	elem_match = 0;
 | |
| 	for (n = 0; n < (int)LWS_ARRAY_SIZE(elems); n++)
 | |
| 		if (ctx->npos == elems[n].elem_len &&
 | |
| 		    !memcmp(ctx->buf, elems[n].elem, elems[n].elem_len))
 | |
| 			elem_match = n + 1;
 | |
| 
 | |
| 	switch (reason) {
 | |
| 	case LHPCB_CONSTRUCTED:
 | |
| 	case LHPCB_DESTRUCTED:
 | |
| 	case LHPCB_COMPLETE:
 | |
| 	case LHPCB_FAILED:
 | |
| 		break;
 | |
| 
 | |
| 	case LHPCB_ELEMENT_START:
 | |
| 
 | |
| 		switch (elem_match) {
 | |
| 		case LHP_ELEM_BR:
 | |
| 			newline(ctx, psb, ps, drt->dl);
 | |
| 			break;
 | |
| 
 | |
| 		case LHP_ELEM_TR:
 | |
| 			if (!psb)
 | |
| 				break;
 | |
| 
 | |
| 			pst = ps;
 | |
| 			while (pst && !pst->is_table)
 | |
| 				pst = lws_css_get_parent_block(ctx, pst);
 | |
| 			if (!pst) {
 | |
| 				lwsl_err("%s: td: no table found\n", __func__);
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			pst->curx.whole = 0;
 | |
| 			pst->curx.frac = 0;
 | |
| 			psb->dlo_set_curx = NULL;
 | |
| 
 | |
| 			trow = lws_zalloc(sizeof(*trow), __func__);
 | |
| 			if (!trow) {
 | |
| 				lwsl_err("%s: OOM\n", __func__);
 | |
| 				return LWS_SRET_FATAL;
 | |
| 			}
 | |
| 			lws_dll2_add_tail(&trow->list, &pst->dlo->table_rows);
 | |
| 			trow = NULL;
 | |
| 			pst->td_idx = 0;
 | |
| 
 | |
| 			goto do_rect;
 | |
| 
 | |
| 		case LHP_ELEM_TD:
 | |
| 			if (!psb) {
 | |
| 				lwsl_err("%s: td: no psb found\n", __func__);
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			pst = ps;
 | |
| 			while (pst && !pst->is_table)
 | |
| 				pst = lws_css_get_parent_block(ctx, pst);
 | |
| 			if (!pst) {
 | |
| 				lwsl_err("%s: td: no table found\n", __func__);
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			if (pst->td_idx >= (int)pst->dlo->table_cols.count) {
 | |
| 				tcol = lws_zalloc(sizeof(*tcol), __func__);
 | |
| 				if (!tcol) {
 | |
| 					lwsl_err("%s: OOM\n", __func__);
 | |
| 					return LWS_SRET_FATAL;
 | |
| 				}
 | |
| 				lws_dll2_add_tail(&tcol->list, &pst->dlo->table_cols);
 | |
| 			} else {
 | |
| 				tcol = lws_container_of(pst->dlo->table_cols.head, lhp_table_col_t, list);
 | |
| 				n = pst->td_idx;
 | |
| 				while (n--)
 | |
| 					tcol = lws_container_of(tcol->list.next, lhp_table_col_t, list);
 | |
| 			}
 | |
| 
 | |
| 			if (pst->dlo->table_rows.tail)
 | |
| 				trow = lws_container_of(pst->dlo->table_rows.tail, lhp_table_row_t, list);
 | |
| 
 | |
| 			goto do_rect;
 | |
| 
 | |
| 		case LHP_ELEM_TABLE:
 | |
| 			ps->is_table = 1;
 | |
| 			/* fallthru */
 | |
| 		case LHP_ELEM_DIV:
 | |
| 
 | |
| do_rect:
 | |
| 			lws_fx_set(box.x, 0, 0);
 | |
| 			lws_fx_set(box.y, 0, 0);
 | |
| 			lws_fx_set(box.h, 0, 0);
 | |
| 			lws_fx_set(box.w, 0, 0);
 | |
| 			abut_x = NULL;
 | |
| 			abut_y = NULL;
 | |
| 
 | |
| 			if (ps->css_position->propval == LCSP_PROPVAL_ABSOLUTE) {
 | |
| 				box.x = *lws_csp_px(ps->css_pos[CCPAS_LEFT], ps);
 | |
| 				box.y = *lws_csp_px(ps->css_pos[CCPAS_TOP], ps);
 | |
| 			} else {
 | |
| 				if (psb) {
 | |
| 
 | |
| 						/* margin adjusts our child box origin */
 | |
| 					lws_fx_add(&box.x, &psb->curx,
 | |
| 							lws_csp_px(ps->css_margin[CCPAS_LEFT], ps));
 | |
| 					box.y = psb->cury;
 | |
| 					abut_x = psb->dlo_set_curx;
 | |
| 					abut_y = psb->dlo_set_cury;
 | |
| 					//lws_fx_add(&box.y, &psb->cury,
 | |
| 					//	   lws_csp_px(ps->css_margin[CCPAS_TOP], ps));
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			/* If there's an explicit width, try to go with that */
 | |
| 
 | |
| 			if (ps->css_width &&
 | |
| 			    ps->css_width->unit != LCSP_UNIT_NONE &&
 | |
| 			    lws_fx_comp(lws_csp_px(ps->css_width, ps), &box.w) < 0)
 | |
| 				box.w = *lws_csp_px(ps->css_width, ps);
 | |
| 
 | |
| 			/* !!! we rely on this being nonzero to not infinite loop at text layout */
 | |
| 
 | |
| 			lws_fx_add(&box.w, &box.w,
 | |
| 			   lws_csp_px(ps->css_padding[CCPAS_LEFT], ps));
 | |
| 			lws_fx_add(&box.w, &box.w,
 | |
| 			   lws_csp_px(ps->css_padding[CCPAS_RIGHT], ps));
 | |
| 
 | |
| 			ps->drt.w = box.w;
 | |
| 			ps->curx = *lws_csp_px(ps->css_padding[CCPAS_LEFT], ps);
 | |
| 			ps->cury = *lws_csp_px(ps->css_padding[CCPAS_TOP], ps);
 | |
| 
 | |
| 			memset(br, 0, sizeof(br));
 | |
| 
 | |
| 			if (ps->css_border_radius[0])
 | |
| 				br[0] = *lws_csp_px(ps->css_border_radius[0], ps);
 | |
| 			if (ps->css_border_radius[1])
 | |
| 				br[1] = *lws_csp_px(ps->css_border_radius[1], ps);
 | |
| 			if (ps->css_border_radius[2])
 | |
| 				br[2] = *lws_csp_px(ps->css_border_radius[2], ps);
 | |
| 			if (ps->css_border_radius[3])
 | |
| 				br[3] = *lws_csp_px(ps->css_border_radius[3], ps);
 | |
| 
 | |
| 			psp = lws_container_of(ps->list.prev, lhp_pstack_t, list);
 | |
| 
 | |
| 			ps->dlo = (lws_dlo_t *)lws_display_dlo_rect_new(drt->dl,
 | |
| 					ps->css_position->propval == LCSP_PROPVAL_ABSOLUTE ? NULL : psp->dlo,
 | |
| 					&box, br, ps->css_background_color ?
 | |
| 					  ps->css_background_color->u.rgba : 0);
 | |
| 			if (!ps->dlo) {
 | |
| 				lwsl_err("%s: FAILED to create rect\n", __func__);
 | |
| 				return LWS_SRET_FATAL;
 | |
| 			}
 | |
| 
 | |
| 			ps->dlo->abut_x = abut_x;
 | |
| 			ps->dlo->abut_y = abut_y;
 | |
| 
 | |
| 			if (psb)
 | |
| 				lws_fx_add(&psb->curx, &psb->curx,
 | |
| 					   lws_csp_px(ps->css_margin[CCPAS_RIGHT], ps));
 | |
| 
 | |
| 			if (tcol)
 | |
| 				lws_dll2_add_tail(&ps->dlo->col_list, &tcol->col_dlos);
 | |
| 			if (trow)
 | |
| 				lws_dll2_add_tail(&ps->dlo->row_list, &trow->row_dlos);
 | |
| 
 | |
| 			lws_lhp_tag_dlo_id(ctx, ps, ps->dlo);
 | |
| 			lhp_set_dlo_padding_margin(ps, ps->dlo);
 | |
| 			break;
 | |
| 
 | |
| 		case LHP_ELEM_IMG:
 | |
| 			pname = lws_html_get_atr(ps, "src", 3);
 | |
| 
 | |
| 			if (!psb)
 | |
| 				break;
 | |
| 
 | |
| 			if (ps->css_position->propval == LCSP_PROPVAL_ABSOLUTE) {
 | |
| 				box.x = *lws_csp_px(ps->css_pos[CCPAS_LEFT], ps);
 | |
| 				box.y = *lws_csp_px(ps->css_pos[CCPAS_TOP], ps);
 | |
| 			} else {
 | |
| 				box.x = psb->curx;
 | |
| 				box.y = psb->cury;
 | |
| 			}
 | |
| 
 | |
| 			lws_fx_set(box.x, 0, 0);
 | |
| 			lws_fx_set(box.y, 0, 0);
 | |
| 
 | |
| 			if (psb) {
 | |
| 				lws_fx_add(&box.x, &box.x,
 | |
| 					lws_csp_px(psb->css_margin[CCPAS_LEFT], psb));
 | |
| 				lws_fx_add(&box.y, &box.y,
 | |
| 					lws_csp_px(psb->css_margin[CCPAS_TOP], psb));
 | |
| 			}
 | |
| 
 | |
| 			box.h = ctx->ic.wh_px[1]; /* placeholder */
 | |
| 			lws_fx_sub(&box.w, &ctx->ic.wh_px[0], &box.x);
 | |
| 
 | |
| 			if (ps->css_width &&
 | |
| 			    lws_fx_comp(lws_csp_px(ps->css_width, ps), &box.w) > 0)
 | |
| 				box.w = *lws_csp_px(ps->css_width, ps);
 | |
| 
 | |
| 			if (lws_http_rel_to_url(url, sizeof(url),
 | |
| 						ctx->base_url, pname))
 | |
| 				break;
 | |
| 
 | |
| 			if (lws_dlo_ss_find(cx, url, &u))
 | |
| 				break;
 | |
| 
 | |
| 			lws_lhp_tag_dlo_id(ctx, ps, (lws_dlo_t *)(u.u.dlo_jpeg));
 | |
| 
 | |
| 			w = *lws_csp_px(lws_css_cascade_get_prop_atr(ctx,
 | |
| 							LCSP_PROP_WIDTH), ps);
 | |
| 			h = *lws_csp_px(lws_css_cascade_get_prop_atr(ctx,
 | |
| 							LCSP_PROP_HEIGHT), ps);
 | |
| 
 | |
| 			if (!w.whole || !h.whole) {
 | |
| 				w = ((lws_dlo_t *)(u.u.dlo_jpeg))->box.w;
 | |
| 				h = ((lws_dlo_t *)(u.u.dlo_jpeg))->box.w;
 | |
| 			}
 | |
| 
 | |
| 			if (psb) {
 | |
| 				lws_fx_add(&psb->curx, &psb->curx, &w);
 | |
| 				lws_fx_add(&psb->cury, &psb->cury, &h);
 | |
| 				psb->dlo_set_curx = ps->dlo;
 | |
| 				psb->dlo_set_cury = ps->dlo;
 | |
| 				if (lws_fx_comp(&psb->curx, &psb->widest) > 0)
 | |
| 					psb->widest = psb->curx;
 | |
| 			}
 | |
| 
 | |
| 			break;
 | |
| 		default:
 | |
| 			break;
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	case LHPCB_ELEMENT_END:
 | |
| 
 | |
| 		if (ctx->npos == 2 && ctx->buf[0] == 'h' &&
 | |
| 		    ctx->buf[1] > '0' && ctx->buf[1] <= '6') {
 | |
| 
 | |
| 			if (!psb)
 | |
| 				break;
 | |
| 
 | |
| 			newline(ctx, psb, ps, drt->dl);
 | |
| 			lws_fx_add(&psb->cury, &psb->cury,
 | |
| 				lws_csp_px(ps->css_padding[CCPAS_BOTTOM], ps));
 | |
| 			lws_fx_add(&psb->cury, &psb->cury,
 | |
| 				lws_csp_px(ps->css_margin[CCPAS_BOTTOM], ps));
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		switch (elem_match) {
 | |
| 
 | |
| 		case LHP_ELEM_TR:
 | |
| 			pst = ps;
 | |
| 			while (pst && !pst->is_table)
 | |
| 				pst = lws_css_get_parent_block(ctx, pst);
 | |
| 			if (!pst) {
 | |
| 				lwsl_err("%s:  /td: no table\n", __func__);
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			pst->tr_idx++;
 | |
| 			pst->td_idx = 0;
 | |
| 			goto do_end_rect;
 | |
| 
 | |
| 		case LHP_ELEM_TD:
 | |
| 			pst = ps;
 | |
| 			while (pst && !pst->is_table)
 | |
| 				pst = lws_css_get_parent_block(ctx, pst);
 | |
| 			if (!pst) {
 | |
| 				lwsl_err("%s:  /td: no table\n", __func__);
 | |
| 				break;
 | |
| 			}
 | |
| 			pst->td_idx++;
 | |
| 			goto do_end_rect;
 | |
| 
 | |
| 
 | |
| 			/* fallthru */
 | |
| 
 | |
| 		case LHP_ELEM_TABLE:
 | |
| 		case LHP_ELEM_DIV:
 | |
| do_end_rect:
 | |
| 			ox = ps->curx;
 | |
| 
 | |
| 			if (lws_fx_comp(&ox, &ps->widest) > 0)
 | |
| 				ps->widest = ox;
 | |
| 
 | |
| 			newline(ctx, ps, ps, drt->dl);
 | |
| 
 | |
| 			if (lws_lhp_dlo_adjust_div_type_element(ctx, psb, pst, ps, elem_match))
 | |
| 				break;
 | |
| 
 | |
| 			if (lws_fx_comp(&ps->curx, &ps->widest) > 0)
 | |
| 				ps->widest = ps->curx;
 | |
| 
 | |
| 			/* move parent on according to used area plus bottom margin */
 | |
| 
 | |
| 			if (psb && ps->css_position->propval != LCSP_PROPVAL_ABSOLUTE) {
 | |
| 
 | |
| 				switch (ps->css_display->propval) {
 | |
| 				case LCSP_PROPVAL_BLOCK:
 | |
| 				case LCSP_PROPVAL_TABLE:
 | |
| 				case LCSP_PROPVAL_TABLE_ROW:
 | |
| 					lws_fx_set(psb->curx, 0, 0);
 | |
| 					psb->dlo_set_curx = NULL;
 | |
| 
 | |
| 					if (ps->css_display->propval == LCSP_PROPVAL_TABLE_ROW)
 | |
| 						break;
 | |
| 					lws_fx_add(&psb->cury, &psb->cury, lws_csp_px(ps->css_margin[CCPAS_BOTTOM], ps));
 | |
| 					break;
 | |
| 
 | |
| 				case LCSP_PROPVAL_INLINE_BLOCK:
 | |
| 					//lws_fx_add(&psb->cury, &psb->cury, lws_csp_px(ps->css_margin[CCPAS_BOTTOM], ps));
 | |
| 					lws_fx_add(&psb->curx, &psb->curx, &ps->widest);
 | |
| 					lws_fx_add(&psb->curx, &psb->curx, lws_csp_px(ps->css_margin[CCPAS_RIGHT], ps));
 | |
| 					lws_fx_set(ps->widest, 0, 0);
 | |
| 					psb->dlo_set_curx = ps->dlo;
 | |
| 					psb->dlo_set_cury = ps->dlo;
 | |
| 					break;
 | |
| 
 | |
| 				default:
 | |
| 					lws_fx_add(&psb->curx, &psb->curx, &ps->widest);
 | |
| 					psb->dlo_set_curx = ps->dlo;
 | |
| 					break;
 | |
| 				}
 | |
| 
 | |
| 				if (lws_fx_comp(&psb->curx, &psb->widest) > 0)
 | |
| 					psb->widest = psb->curx;
 | |
| 			}
 | |
| 
 | |
| 			ps->dlo = NULL;
 | |
| 			break;
 | |
| 		default:
 | |
| 			break;
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	case LHPCB_CONTENT:
 | |
| 
 | |
| 		if (!ps->css_display ||
 | |
| 		    ps->css_display->propval == LCSP_PROPVAL_NONE)
 | |
| 			break;
 | |
| 
 | |
| 		if (ps->css_color)
 | |
| 			col = ps->css_color->u.rgba;
 | |
| 
 | |
| 		a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_SIZE);
 | |
| 		if (a)
 | |
| 			fc.fixed_height = (uint16_t)a->u.i.whole;
 | |
| 
 | |
| 		a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_FAMILY);
 | |
| 		if (a)
 | |
| 			fc.family_name = (const char *)&a[1];
 | |
| 
 | |
| 		for (n = 0; n < ctx->npos; n++)
 | |
| 			if (ctx->buf[n] == '\n')
 | |
| 				s++;
 | |
| 
 | |
| 		if (s == ctx->npos)
 | |
| 			return 0;
 | |
| 
 | |
| 		/*
 | |
| 		 * Let's not deal with things off the bottom of the display
 | |
| 		 * surface.
 | |
| 		 */
 | |
| 
 | |
| 		if (psb && psb->cury.whole > ctx->ic.wh_px[LWS_LHPREF_HEIGHT].whole)
 | |
| 			return 0;
 | |
| 
 | |
| 		if (!psb)
 | |
| 			return 0;
 | |
| 
 | |
| 		f = lws_font_choose(cx, &fc);
 | |
| 
 | |
| 		n = s;
 | |
| 		while (n < ctx->npos) {
 | |
| 			int m;
 | |
| 
 | |
| 			lws_fx_set(box.x, 0, 0);
 | |
| 			lws_fx_set(box.y, 0, 0);
 | |
| 			lws_fx_set(box.w, 0, 0);
 | |
| 
 | |
| 			if (n == s && !(psb->runon & 1)) {
 | |
| 				lws_fx_set(indent, 0, 0);
 | |
| 			} else
 | |
| 				indent = psb->curx;
 | |
| 			lws_fx_add(&box.x, &indent,
 | |
| 					  lws_csp_px(ps->css_padding[CCPAS_LEFT], ps));
 | |
| 			lws_fx_add(&box.y, &box.y, &psb->cury);
 | |
| 
 | |
| 			box.h.whole = (int32_t)f->choice.fixed_height;
 | |
| 			box.h.frac = 0;
 | |
| 
 | |
| 			if (psb->css_width &&
 | |
| 				(psb->css_width->propval == LCSP_PROPVAL_AUTO ||
 | |
| 				 ps->css_width->propval == LCSP_PROPVAL_AUTO)) {
 | |
| 				//lws_fx_sub(&box.w, &ctx->ic.wh_px[0], &box.x);
 | |
| 				box.w = ctx->ic.wh_px[0];
 | |
| 			} else {
 | |
| 				lws_fx_sub(&t1, &psb->drt.w,
 | |
| 					   lws_csp_px(psb->css_padding[CCPAS_LEFT], psb));
 | |
| 				lws_fx_sub(&box.w, &t1,
 | |
| 					   lws_csp_px(psb->css_padding[CCPAS_RIGHT], psb));
 | |
| 			}
 | |
| 
 | |
| 			if (!box.w.whole)
 | |
| 				lws_fx_sub(&box.w, &ctx->ic.wh_px[0], &box.x);
 | |
| 			assert(psb);
 | |
| 
 | |
| 			txt = lws_display_dlo_text_new(drt->dl,
 | |
| 					(lws_dlo_t *)psb->dlo, &box, f);
 | |
| 			if (!txt) {
 | |
| 				lwsl_err("%s: failed to alloc text\n", __func__);
 | |
| 				return 1;
 | |
| 			}
 | |
| 			runon(psb, &txt->dlo);
 | |
| 			txt->flags |= LWSDLO_TEXT_FLAG_WRAP;
 | |
| 
 | |
| 			lhp_set_dlo_padding_margin(ps, &txt->dlo);
 | |
| 
 | |
| //			a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_TEXT_ALIGN);
 | |
| 
 | |
| 			//lwsl_hexdump_notice(ctx->buf + n, (size_t)(ctx->npos - n));
 | |
| 			m = lws_display_dlo_text_update(txt, col, indent,
 | |
| 							ctx->buf + n,
 | |
| 							(size_t)(ctx->npos - n));
 | |
| 			if (m < 0) {
 | |
| 				lwsl_err("text_update ret %d\n", m);
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			if (m == 2 && lastm)
 | |
| 				return 0;
 | |
| 
 | |
| 			lastm = m == 2;
 | |
| 
 | |
| 			n = (int)((size_t)n + txt->text_len);
 | |
| 			txt->dlo.box.w = txt->bounding_box.w;
 | |
| 			txt->dlo.box.h = txt->bounding_box.h;
 | |
| 
 | |
| 			lws_fx_add(&psb->curx, &psb->curx, &txt->bounding_box.w);
 | |
| 			psb->dlo_set_curx = &txt->dlo;
 | |
| 
 | |
| 			//lwsl_user("%s: bounding width %d, m: %d, text %.*s\n",
 | |
| 			//	  __func__, txt->bounding_box.w.whole, m,
 | |
| 			//	  ctx->npos, ctx->buf);
 | |
| 
 | |
| 			if (m > 0) { /* wrapping */
 | |
| 				newline(ctx, psb, ps, drt->dl);
 | |
| 				lws_fx_set(ps->curx, 0, 0);
 | |
| 				lws_fx_set(psb->curx, 0, 0);
 | |
| 				psb->dlo_set_curx = NULL;
 | |
| 				lws_fx_add(&ps->cury, &ps->cury, &txt->bounding_box.h);
 | |
| 				psb->dlo_set_cury = &txt->dlo;
 | |
| 			}
 | |
| 		}
 | |
| 		break;
 | |
| 	case LHPCB_COMMENT:
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 |