int css_define_color(const char *name, const char *value) { ColorDef *def; QEColor color; /* Check color validity */ if (css_get_color(&color, value)) return -1; /* Make room: reallocate table in chunks of 8 entries */ if ((nb_custom_colors & 7) == 0) { def = (ColorDef*)malloc((nb_css_colors + nb_custom_colors + 8) * sizeof(ColorDef)); if (!def) return -1; memcpy(def, custom_colors, (nb_css_colors + nb_custom_colors) * sizeof(ColorDef)); if (custom_colors != css_colors) free(custom_colors); custom_colors = def; } /* Check for redefinition */ def = css_lookup_color(custom_colors, nb_css_colors + nb_custom_colors, name); if (def) { def->color = color; return 0; } def = &custom_colors[nb_css_colors + nb_custom_colors]; def->name = strdup(name); def->color = color; nb_custom_colors++; return 0; }
static void html_eval_tag(XMLState *s, CSSBox *box) { const char *value; CSSProperty *first_prop, **last_prop; QEColor color; int width, height, val, type; int border, padding; CSSPropertyValue arg; CSSPropertyValue args[2]; first_prop = NULL; last_prop = &first_prop; switch (box->tag) { case CSS_ID_img: parse_img: box->content_type = CSS_CONTENT_TYPE_IMAGE; box->u.image.content_alt = NULL; /* set alt content */ value = css_attr_str(box, CSS_ID_alt); if (!value) { /* if no alt, display the name of the image */ value = css_attr_str(box, CSS_ID_src); if (value) value = basename(value); } if (value && value[0] != '\0') { arg.type = CSS_VALUE_STRING; arg.u.str = strdup(value); css_add_prop(&last_prop, CSS_content_alt, &arg); } width = css_attr_int(box, CSS_ID_width, 0); if (width <= 0) width = DEFAULT_IMG_WIDTH; height = css_attr_int(box, CSS_ID_height, 0); if (height <= 0) height = DEFAULT_IMG_HEIGHT; css_add_prop_unit(&last_prop, CSS_width, CSS_UNIT_PIXEL, width); css_add_prop_unit(&last_prop, CSS_height, CSS_UNIT_PIXEL, height); /* border */ val = css_attr_int(box, CSS_ID_border, -1); if (val >= 0) { css_add_prop_unit(&last_prop, CSS_border_left_width, CSS_UNIT_PIXEL, val); css_add_prop_unit(&last_prop, CSS_border_right_width, CSS_UNIT_PIXEL, val); css_add_prop_unit(&last_prop, CSS_border_top_width, CSS_UNIT_PIXEL, val); css_add_prop_unit(&last_prop, CSS_border_bottom_width, CSS_UNIT_PIXEL, val); css_add_prop_int(&last_prop, CSS_border_left_style, CSS_BORDER_STYLE_SOLID); css_add_prop_int(&last_prop, CSS_border_right_style, CSS_BORDER_STYLE_SOLID); css_add_prop_int(&last_prop, CSS_border_top_style, CSS_BORDER_STYLE_SOLID); css_add_prop_int(&last_prop, CSS_border_bottom_style, CSS_BORDER_STYLE_SOLID); } /* margins */ val = css_attr_int(box, CSS_ID_hspace, -1); if (val >= 0) { css_add_prop_unit(&last_prop, CSS_margin_left, CSS_UNIT_PIXEL, val); css_add_prop_unit(&last_prop, CSS_margin_right, CSS_UNIT_PIXEL, val); } val = css_attr_int(box, CSS_ID_vspace, -1); if (val >= 0) { css_add_prop_unit(&last_prop, CSS_margin_top, CSS_UNIT_PIXEL, val); css_add_prop_unit(&last_prop, CSS_margin_bottom, CSS_UNIT_PIXEL, val); } break; case CSS_ID_body: value = css_attr_str(box, CSS_ID_text); if (value && !css_get_color(&color, value)) { css_add_prop_int(&last_prop, CSS_color, color); } /* we handle link by adding a new stylesheet entry */ value = css_attr_str(box, CSS_ID_link); if (value && !css_get_color(&color, value)) { CSSStyleSheetEntry *e; CSSSimpleSelector ss1, *ss = &ss1; CSSProperty **last_prop; CSSStyleSheetAttributeEntry **plast_attr; /* specific to <a href="xxx"> tag */ memset(ss, 0, sizeof(CSSSimpleSelector)); ss->tag = CSS_ID_a; plast_attr = &ss->attrs; add_attribute(&plast_attr, CSS_ID_href, CSS_ATTR_OP_SET, ""); e = add_style_entry(s->style_sheet, ss, CSS_MEDIA_ALL); /* add color property */ last_prop = &e->props; css_add_prop_int(&last_prop, CSS_color, color); } break; case CSS_ID_font: case CSS_ID_basefont: /* size */ value = css_attr_str(box, CSS_ID_size); if (value) { val = strtol(value, NULL, 10); if (value[0] == '+' || value[0] == '-') { /* relative size */ val += s->base_font; } if (val < 1) val = 1; else if (val > 7) val = 7; if (box->tag == CSS_ID_basefont) s->base_font = val; /* XXX: incorrect for basefont */ css_add_prop_unit(&last_prop, CSS_font_size, CSS_UNIT_IN, get_font_size(val - 1)); } /* color */ value = css_attr_str(box, CSS_ID_color); if (value && !css_get_color(&color, value)) { css_add_prop_int(&last_prop, CSS_color, color); } break; case CSS_ID_br: value = css_attr_strlower(box, CSS_ID_clear); if (value) { val = css_get_enum(value, "none,left,right,all"); if (val >= 0) { css_add_prop_int(&last_prop, CSS_clear, val + CSS_CLEAR_NONE); } } break; case CSS_ID_table: val = css_attr_int(box, CSS_ID_width, -1); if (val >= 0) { css_add_prop_unit(&last_prop, CSS_width, CSS_UNIT_PIXEL, val); } border = css_attr_int(box, CSS_ID_border, -1); if (border >= 0) { css_add_prop_unit(&last_prop, CSS_border_left_width, CSS_UNIT_PIXEL, border); css_add_prop_unit(&last_prop, CSS_border_right_width, CSS_UNIT_PIXEL, border); css_add_prop_unit(&last_prop, CSS_border_top_width, CSS_UNIT_PIXEL, border); css_add_prop_unit(&last_prop, CSS_border_bottom_width, CSS_UNIT_PIXEL, border); css_add_prop_int(&last_prop, CSS_border_left_style, CSS_BORDER_STYLE_GROOVE); css_add_prop_int(&last_prop, CSS_border_right_style, CSS_BORDER_STYLE_GROOVE); css_add_prop_int(&last_prop, CSS_border_top_style, CSS_BORDER_STYLE_GROOVE); css_add_prop_int(&last_prop, CSS_border_bottom_style, CSS_BORDER_STYLE_GROOVE); /* cell have a border of 1 pixel width */ if (border > 1) border = 1; } val = css_attr_int(box, CSS_ID_cellspacing, -1); if (val >= 0) { css_add_prop_unit(&last_prop, CSS_border_spacing_horizontal, CSS_UNIT_PIXEL, val); css_add_prop_unit(&last_prop, CSS_border_spacing_vertical, CSS_UNIT_PIXEL, val); } padding = css_attr_int(box, CSS_ID_cellpadding, -1); /* apply border styles to each cell (cannot be done exactly by CSS) */ if (border >= 1 || padding >= 1) html_table_borders(box, border, padding); break; case CSS_ID_col: case CSS_ID_colgroup: val = css_attr_int(box, CSS_ID_width, -1); if (val >= 0) { css_add_prop_unit(&last_prop, CSS_width, CSS_UNIT_PIXEL, val); } break; case CSS_ID_td: val = css_attr_int(box, CSS_ID_width, -1); if (val >= 0) { css_add_prop_unit(&last_prop, CSS_width, CSS_UNIT_PIXEL, val); } val = css_attr_int(box, CSS_ID_height, -1); if (val >= 0) { css_add_prop_unit(&last_prop, CSS_height, CSS_UNIT_PIXEL, val); } break; case CSS_ID_ol: case CSS_ID_li: /* NOTE: case is important */ /* XXX: currently cannot propagate for LI tag */ value = css_attr_str(box, CSS_ID_type); if (value) { val = css_get_enum(value, "1,a,A,i,I"); if (val >= 0) { val += CSS_LIST_STYLE_TYPE_DECIMAL; css_add_prop_int(&last_prop, CSS_list_style_type, val); } } /* XXX: add value, but needs a css extension */ if (box->tag == CSS_ID_ol) val = CSS_ID_start; else val = CSS_ID_value; /* NOTE: only works with digits */ val = css_attr_int(box, val, 0); if (val > 0) { args[0].type = CSS_VALUE_IDENT; args[0].u.val = CSS_ID_list_item; args[1].type = CSS_VALUE_INTEGER; args[1].u.val = val - 1; /* currently needs minus 1 */ css_add_prop_values(&last_prop, CSS_counter_reset, 2, args); } break; /* controls */ case CSS_ID_button: type = CSS_ID_submit; value = css_attr_strlower(box, CSS_ID_type); if (value) type = css_new_ident(value); if (type != CSS_ID_button && type != CSS_ID_reset) type = CSS_ID_submit; goto parse_input; case CSS_ID_input: type = CSS_ID_text; value = css_attr_strlower(box, CSS_ID_type); if (value) { type = css_new_ident(value); } else { CSSAttribute *attr; /* NOTE: we add an attribute for css rules */ attr = box_new_attr(CSS_ID_type, "text"); if (attr) { attr->next = box->attrs; box->attrs = attr; } } parse_input: if (type == CSS_ID_image) goto parse_img; if (type == CSS_ID_button || type == CSS_ID_reset || type == CSS_ID_submit || type == CSS_ID_text || type == CSS_ID_password || type == CSS_ID_file) { /* put text inside the box (XXX: use attr() in content attribute ? */ value = css_attr_str(box, CSS_ID_value); if (value) { css_set_text_string(box, value); } } /* size */ if (type == CSS_ID_text || type == CSS_ID_password) { val = css_attr_int(box, CSS_ID_size, 10); css_add_prop_unit(&last_prop, CSS_width, CSS_UNIT_EM, val << CSS_LENGTH_FRAC_BITS); } break; case CSS_ID_textarea: val = css_attr_int(box, CSS_ID_cols, 10); if (val < 1) val = 1; css_add_prop_unit(&last_prop, CSS_width, CSS_UNIT_EM, val << CSS_LENGTH_FRAC_BITS); val = css_attr_int(box, CSS_ID_rows, 1); if (val < 1) val = 1; css_add_prop_unit(&last_prop, CSS_height, CSS_UNIT_EM, val << CSS_LENGTH_FRAC_BITS); break; case CSS_ID_select: val = css_attr_int(box, CSS_ID_size, 1); if (val < 1) val = 1; css_add_prop_unit(&last_prop, CSS_height, CSS_UNIT_EM, val << CSS_LENGTH_FRAC_BITS); break; default: break; } /* generic attributes */ value = css_attr_str(box, CSS_ID_bgcolor); if (value && !css_get_color(&color, value)) { css_add_prop_int(&last_prop, CSS_background_color, color); } value = css_attr_strlower(box, CSS_ID_align); if (value) { switch (box->tag) { case CSS_ID_caption: /* use caption-side property for captions */ val = css_get_enum(value, "top,bottom,left,right"); if (val >= 0) { css_add_prop_int(&last_prop, CSS_caption_side, val); } break; case CSS_ID_img: /* floating images */ val = css_get_enum(value, "left,right"); if (val >= 0) { css_add_prop_int(&last_prop, CSS_float, val + CSS_FLOAT_LEFT); } break; case CSS_ID_table: val = css_get_enum(value, "left,right,center"); if (val == CSS_TEXT_ALIGN_LEFT || val == CSS_TEXT_ALIGN_RIGHT) { css_add_prop_int(&last_prop, CSS_float, val + CSS_FLOAT_LEFT); } else if (val == CSS_TEXT_ALIGN_CENTER) { css_add_prop_int(&last_prop, CSS_margin_left, CSS_AUTO); css_add_prop_int(&last_prop, CSS_margin_right, CSS_AUTO); } break; default: val = css_get_enum(value, "left,right,center"); if (val >= 0) { css_add_prop_int(&last_prop, CSS_text_align, val); } break; } } value = css_attr_strlower(box, CSS_ID_valign); if (value) { val = css_get_enum(value, "baseline,,,top,,middle,bottom"); if (val >= 0) { css_add_prop_int(&last_prop, CSS_vertical_align, val); } } val = css_attr_int(box, CSS_ID_colspan, 1); if (val > 1) { css_add_prop_unit(&last_prop, CSS_column_span, CSS_VALUE_INTEGER, val); } val = css_attr_int(box, CSS_ID_rowspan, 1); if (val > 1) { css_add_prop_unit(&last_prop, CSS_row_span, CSS_VALUE_INTEGER, val); } value = css_attr_str(box, CSS_ID_style); if (value) { CSSParseState b1, *b = &b1; b->ptr = NULL; b->line_num = s->line_num; /* XXX: slightly incorrect */ b->filename = s->filename; b->ignore_case = s->ignore_case; *last_prop = css_parse_properties(b, value); } box->properties = first_prop; }
/* parse the properties and return a list of properties. NOTE: 'b' is only used for error reporting */ CSSProperty *css_parse_properties(CSSParseState *b, const char *props_str) { const char *p; char property[64]; char buf[1024], buf2[64]; int property_index, type, val, nb_args, i, unit; int property_index1; CSSPropertyValue args[MAX_ARGS]; CSSProperty **last_prop, *first_prop; const CSSPropertyDef *def; val = 0; first_prop = NULL; last_prop = &first_prop; p = props_str; for (;;) { get_str(&p, property, sizeof(property), ":"); if (*p == '\0') break; if (*p == ':') p++; skip_spaces(&p); /* find the property */ def = css_properties; for (;;) { if (def >= css_properties + NB_PROPERTIES) { css_error1(b, "unsupported property '%s'", property); /* property not found skip it: find next ';' */ while (*p && *p != ';') p++; goto next; } if (!strcmp(def->name, property)) break; def++; } property_index = def - css_properties; type = def->type; nb_args = 0; for (;;) { /* get argument */ skip_spaces(&p); if (*p == ';' || *p == '\0') break; /* more than 1 argument only if wanted */ if (nb_args >= 1 && !(type & (CSS_TYPE_FOUR|CSS_TYPE_TWO|CSS_TYPE_ARGS))) break; if (nb_args >= 2 && !(type & (CSS_TYPE_FOUR|CSS_TYPE_ARGS))) break; if (nb_args >= 4 && (!type & CSS_TYPE_ARGS)) break; if (nb_args >= MAX_ARGS) break; if (*p == '\"' || *p == '\'') { /* string parsing */ /* if no string expected, continue parsing */ if (!(type & CSS_TYPE_STRING)) goto next; args[nb_args].u.str = css_parse_string(&p); unit = CSS_VALUE_STRING; goto got_val; } if (type & CSS_TYPE_ATTR) { /* attr(x) support */ if (strstart(p, "attr(", &p)) { get_str(&p, buf, sizeof(buf), ");"); if (buf[0] != '\0') { if (*p != ')') goto next; p++; if (b->ignore_case) css_strtolower(buf, sizeof(buf)); args[nb_args].u.attr_id = css_new_ident(buf); unit = CSS_VALUE_ATTR; goto got_val; } } } if (type & CSS_TYPE_COUNTER) { /* counter(x[,type]) support */ if (strstart(p, "counter(", &p)) { get_str(&p, buf, sizeof(buf), ",);"); args[nb_args].u.counter.type = CSS_LIST_STYLE_TYPE_DECIMAL; if (*p == ',') { p++; get_str(&p, buf2, sizeof(buf2), ");"); val = css_get_enum(buf2, list_style_enum); if (val >= 0) args[nb_args].u.counter.type = val; } if (*p != ')') goto next; p++; args[nb_args].u.counter.counter_id = css_new_ident(buf); unit = CSS_VALUE_COUNTER; goto got_val; } } get_str(&p, buf, sizeof(buf), ";"); unit = CSS_UNIT_NONE; if (type & CSS_TYPE_AUTO) { if (!strcmp(buf, "auto")) { val = CSS_AUTO; goto got_val; } } if (!(type & CSS_TYPE_NOINHERIT)) { if (!strcmp(buf, "inherit")) { val = CSS_INHERIT; goto got_val; } } if (type & CSS_TYPE_INTEGER) { const char *p1; val = strtol(buf, (char **)&p1, 0); if (*p1 == '\0') { unit = CSS_VALUE_INTEGER; goto got_val; } } if (type & CSS_TYPE_LENGTH) { if (!css_get_length(&val, &unit, buf)) goto got_val; } if (type & CSS_TYPE_BORDER_STYLE) { val = css_get_enum(buf, border_style_enum); if (val >= 0) goto got_val; } if (type & CSS_TYPE_LIST_STYLE) { val = css_get_enum(buf, list_style_enum); if (val >= 0) goto got_val; } if (type & CSS_TYPE_ENUM) { val = css_get_enum(buf, def->name + strlen(def->name) + 1); if (val >= 0) goto got_val; } if (type & CSS_TYPE_IDENT) { val = css_new_ident(buf); unit = CSS_VALUE_IDENT; goto got_val; } if (type & CSS_TYPE_FONT_FAMILY) { val = css_get_font_family(buf); if (val == 0) val = CSS_INHERIT; goto got_val; } if (type & CSS_TYPE_COLOR) { QEColor color; /* XXX: color parsing is not always discriminant */ if (!css_get_color(&color, buf)) { val = color; unit = CSS_VALUE_COLOR; goto got_val; } } css_error1(b, "unrecognized value '%s' for property '%s'", buf, def->name); goto next; got_val: /* specific handling may be necessary. We do them here */ switch (property_index) { case CSS_font_size: if (unit == CSS_UNIT_NONE) { if (val == 7) { /* smaller */ unit = CSS_UNIT_PERCENT; val = (CSS_LENGTH_FRAC_BASE * 10) / 12; } else if (val == 8) { /* larger */ unit = CSS_UNIT_PERCENT; val = (CSS_LENGTH_FRAC_BASE * 12) / 10; } else if (val >= 0) { unit = CSS_UNIT_IN; val = get_font_size(val); } else { goto next; } } break; case CSS_border: case CSS_border_left: case CSS_border_top: case CSS_border_right: case CSS_border_bottom: if (unit == CSS_VALUE_COLOR) { property_index1 = property_index + CSS_border_color - CSS_border; } else if (unit == CSS_UNIT_NONE) { property_index1 = property_index + CSS_border_style - CSS_border; } else { property_index1 = property_index + CSS_border_width - CSS_border; } args[0].type = unit; args[0].u.val = val; if (property_index == CSS_border) { for (i = 0; i < 4; i++) css_add_prop(&last_prop, property_index1 + 1 + i, &args[0]); } else { css_add_prop(&last_prop, property_index1, &args[0]); } /* parse next args without storing them */ continue; } args[nb_args].type = unit; if (unit != CSS_VALUE_STRING && unit != CSS_VALUE_ATTR && unit != CSS_VALUE_COUNTER) { args[nb_args].u.val = val; } nb_args++; } if (type & CSS_TYPE_SPECIAL) goto next; if (type & CSS_TYPE_FOUR) { CSSPropertyValue v1, v2, v3, v4; /* handle specifically the four args case */ v1 = args[0]; switch (nb_args) { case 1: args[1] = args[2] = args[3] = v1; break; case 2: v2 = args[1]; args[1] = args[3] = v1; args[0] = args[2] = v2; break; case 3: v2 = args[1]; v3 = args[2]; args[1] = v1; args[0] = args[2] = v2; args[3] = v3; break; case 4: default: v2 = args[1]; v3 = args[2]; v4 = args[3]; args[1] = v1; args[2] = v2; args[3] = v3; args[0] = v4; break; } for (i = 0; i < 4; i++) css_add_prop(&last_prop, property_index + 1 + i, &args[i]); } else if (type & CSS_TYPE_TWO) { if (nb_args == 1) args[1] = args[0]; for (i = 0; i < 2; i++) css_add_prop(&last_prop, property_index + 1 + i, &args[i]); } else if (type & CSS_TYPE_ARGS) { /* unbounded number of args */ css_add_prop_values(&last_prop, property_index, nb_args, args); } else { css_add_prop(&last_prop, property_index, &args[0]); } next: skip_spaces(&p); if (*p != ';') break; p++; } return first_prop; }