mirror of
https://libwebsockets.org/repo/libwebsockets
synced 2024-11-24 01:39:33 +00:00
1e0953ffb8
iwashiira on github https://github.com/warmcat/libwebsockets/issues/3140 found the html / css calculation could end up with NULL margin sizes.
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;
|
|
}
|