From: Bruno Haible Date: Tue, 12 Dec 2006 14:22:13 +0000 (+0000) Subject: New module 'term-styled-ostream'. X-Git-Tag: v0.17~598 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4c7332056c124ffff140c2dbffa977e344f37edd;p=thirdparty%2Fgettext.git New module 'term-styled-ostream'. --- diff --git a/gnulib-local/ChangeLog b/gnulib-local/ChangeLog index cc9ad2e4a..66d9f0339 100644 --- a/gnulib-local/ChangeLog +++ b/gnulib-local/ChangeLog @@ -1,3 +1,10 @@ +2006-12-01 Bruno Haible + + * modules/term-styled-ostream: New file. + * lib/term-styled-ostream.oo.c: New file. + * lib/term-styled-ostream.oo.h: New file. + * Makefile.am (EXTRA_DIST): Add the new files. + 2006-12-01 Bruno Haible * modules/html-styled-ostream: New file. diff --git a/gnulib-local/Makefile.am b/gnulib-local/Makefile.am index 2395a1e60..e33d6a386 100644 --- a/gnulib-local/Makefile.am +++ b/gnulib-local/Makefile.am @@ -238,6 +238,8 @@ lib/styled-ostream.oo.c \ lib/styled-ostream.oo.h \ lib/term-ostream.oo.c \ lib/term-ostream.oo.h \ +lib/term-styled-ostream.oo.c \ +lib/term-styled-ostream.oo.h \ lib/termcap.h \ lib/vasprintf.c \ lib/xalloc.h \ @@ -308,6 +310,7 @@ modules/termcap \ modules/termcap-h \ modules/term-ostream \ modules/term-ostream-tests \ +modules/term-styled-ostream \ modules/vasprintf.diff \ modules/wait-process.diff \ modules/xalloc \ diff --git a/gnulib-local/lib/term-styled-ostream.oo.c b/gnulib-local/lib/term-styled-ostream.oo.c new file mode 100644 index 000000000..6de9662c1 --- /dev/null +++ b/gnulib-local/lib/term-styled-ostream.oo.c @@ -0,0 +1,465 @@ +/* Output stream for CSS styled text, producing ANSI escape sequences. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include "term-styled-ostream.h" + +#include + +#include +#include +#include +#include +#include + +#include "term-ostream.h" +#include "hash.h" +#include "xalloc.h" + + +/* CSS matching works as follows: + Suppose we have an element inside class "header" inside class "table". + We pretend to have an XML tree that looks like this: + + (root) + +----table + +----header + + For each of these XML nodes, the CSS matching engine can report the + matching CSS declarations. We extract the CSS property values that + matter for terminal styling and cache them. */ + +/* Attributes that can be set on a character. */ +typedef struct +{ + term_color_t color; + term_color_t bgcolor; + term_weight_t weight; + term_posture_t posture; + term_underline_t underline; +} attributes_t; + +struct term_styled_ostream : struct styled_ostream +{ +fields: + /* The destination stream. */ + term_ostream_t destination; + /* The CSS document. */ + CRCascade *css_document; + /* The CSS matching engine. */ + CRSelEng *css_engine; + /* The list of active XML elements, with a space before each. + For example, in above example, it is " table header". */ + char *curr_classes; + size_t curr_classes_length; + size_t curr_classes_allocated; + /* A hash table mapping a list of classes (as a string) to an + 'attributes_t *'. */ + hash_table cache; + /* The current attributes. */ + attributes_t *curr_attr; +}; + +/* Implementation of ostream_t methods. */ + +static void +term_styled_ostream::write_mem (term_styled_ostream_t stream, + const void *data, size_t len) +{ + term_ostream_set_color (stream->destination, stream->curr_attr->color); + term_ostream_set_bgcolor (stream->destination, stream->curr_attr->bgcolor); + term_ostream_set_weight (stream->destination, stream->curr_attr->weight); + term_ostream_set_posture (stream->destination, stream->curr_attr->posture); + term_ostream_set_underline (stream->destination, stream->curr_attr->underline); + + term_ostream_write_mem (stream->destination, data, len); +} + +static void +term_styled_ostream::flush (term_styled_ostream_t stream) +{ + term_ostream_flush (stream->destination); +} + +static void +term_styled_ostream::free (term_styled_ostream_t stream) +{ + term_ostream_free (stream->destination); + cr_cascade_destroy (stream->css_document); + cr_sel_eng_destroy (stream->css_engine); + free (stream->curr_classes); + { + void *ptr = NULL; + const void *key; + size_t keylen; + void *data; + + while (hash_iterate (&stream->cache, &ptr, &key, &keylen, &data) == 0) + { + free (data); + } + } + hash_destroy (&stream->cache); + free (stream); +} + +/* Implementation of styled_ostream_t methods. */ + +/* According to the CSS2 spec, sections 6.1 and 6.2, we need to do a + propagation: specified values -> computed values -> actual values. + The computed values are necessary. libcroco does not compute them for us. + The function cr_style_resolve_inherited_properties is also not sufficient: + it handles only the case of inheritance, not the case of non-inheritance. + So we write style accessors that fetch the computed value, doing the + inheritance on the fly. + We then compute the actual values from the computed values; for colors, + this is done through the rgb_to_color method. */ + +static term_color_t +style_compute_color_value (CRStyle *style, enum CRRgbProp which, + term_ostream_t stream) +{ + for (;;) + { + if (style == NULL) + return COLOR_DEFAULT; + if (cr_rgb_is_set_to_inherit (&style->rgb_props[which].sv)) + style = style->parent_style; + else if (cr_rgb_is_set_to_transparent (&style->rgb_props[which].sv)) + /* A transparent color occurs as default background color, set by + cr_style_set_props_to_default_values. */ + return COLOR_DEFAULT; + else + { + CRRgb rgb; + int r; + int g; + int b; + + cr_rgb_copy (&rgb, &style->rgb_props[which].sv); + if (cr_rgb_compute_from_percentage (&rgb) != CR_OK) + abort (); + r = rgb.red & 0xff; + g = rgb.green & 0xff; + b = rgb.blue & 0xff; + return term_ostream_rgb_to_color (stream, r, g, b); + } + } +} + +static term_weight_t +style_compute_font_weight_value (const CRStyle *style) +{ + int value = 0; + for (;;) + { + if (style == NULL) + value += 4; + else + switch (style->font_weight) + { + case FONT_WEIGHT_INHERIT: + style = style->parent_style; + continue; + case FONT_WEIGHT_BOLDER: + value += 1; + style = style->parent_style; + continue; + case FONT_WEIGHT_LIGHTER: + value -= 1; + style = style->parent_style; + continue; + case FONT_WEIGHT_100: + value += 1; + break; + case FONT_WEIGHT_200: + value += 2; + break; + case FONT_WEIGHT_300: + value += 3; + break; + case FONT_WEIGHT_400: case FONT_WEIGHT_NORMAL: + value += 4; + break; + case FONT_WEIGHT_500: + value += 5; + break; + case FONT_WEIGHT_600: + value += 6; + break; + case FONT_WEIGHT_700: case FONT_WEIGHT_BOLD: + value += 7; + break; + case FONT_WEIGHT_800: + value += 8; + break; + case FONT_WEIGHT_900: + value += 9; + break; + default: + abort (); + } + /* Value >= 600 -> WEIGHT_BOLD. Value <= 500 -> WEIGHT_NORMAL. */ + return (value >= 6 ? WEIGHT_BOLD : WEIGHT_NORMAL); + } +} + +static term_posture_t +style_compute_font_posture_value (const CRStyle *style) +{ + for (;;) + { + if (style == NULL) + return POSTURE_DEFAULT; + switch (style->font_style) + { + case FONT_STYLE_INHERIT: + style = style->parent_style; + break; + case FONT_STYLE_NORMAL: + return POSTURE_NORMAL; + case FONT_STYLE_ITALIC: + case FONT_STYLE_OBLIQUE: + return POSTURE_ITALIC; + default: + abort (); + } + } +} + +static term_underline_t +style_compute_text_underline_value (const CRStyle *style) +{ + /* Not supported by libcroco's CRStyle type! */ + return UNDERLINE_DEFAULT; +} + +/* Match the current list of CSS classes to the CSS and return the result. */ +static attributes_t * +match (term_styled_ostream_t stream) +{ + xmlNodePtr root; + xmlNodePtr curr; + char *p_end; + char *p_start; + CRStyle *curr_style; + attributes_t *attr; + + /* Create a hierarchy of XML nodes. */ + root = xmlNewNode (NULL, (const xmlChar *) "__root__"); + root->type = XML_ELEMENT_NODE; + curr = root; + p_end = &stream->curr_classes[stream->curr_classes_length]; + p_start = stream->curr_classes; + while (p_start < p_end) + { + char *p; + xmlNodePtr child; + + if (!(*p_start == ' ')) + abort (); + p_start++; + for (p = p_start; p < p_end && *p != ' '; p++) + ; + + /* Temporarily replace the ' ' by '\0'. */ + *p = '\0'; + child = xmlNewNode (NULL, (const xmlChar *) p_start); + child->type = XML_ELEMENT_NODE; + xmlSetProp (child, (const xmlChar *) "class", (const xmlChar *) p_start); + *p = ' '; + + if (xmlAddChild (curr, child) == NULL) + /* Error! Shouldn't happen. */ + abort (); + + curr = child; + p_start = p; + } + + /* Retrieve the matching CSS declarations. */ + /* Not curr_style = cr_style_new (TRUE); because that assumes that the + default foreground color is black and that the default background color + is white, which is not necessarily true in a terminal context. */ + curr_style = NULL; + for (curr = root; curr != NULL; curr = curr->children) + { + CRStyle *parent_style = curr_style; + curr_style = NULL; + + if (cr_sel_eng_get_matched_style (stream->css_engine, + stream->css_document, + curr, + parent_style, &curr_style, + FALSE) != CR_OK) + abort (); + if (curr_style == NULL) + /* No declarations matched this node. Inherit all values. */ + curr_style = parent_style; + else + /* curr_style is a new style, inheriting from parent_style. */ + ; + } + + /* Extract the CSS declarations that we can use. */ + attr = XMALLOC (attributes_t); + attr->color = + style_compute_color_value (curr_style, RGB_PROP_COLOR, + stream->destination); + attr->bgcolor = + style_compute_color_value (curr_style, RGB_PROP_BACKGROUND_COLOR, + stream->destination); + attr->weight = style_compute_font_weight_value (curr_style); + attr->posture = style_compute_font_posture_value (curr_style); + attr->underline = style_compute_text_underline_value (curr_style); + + /* Free the style chain. */ + while (curr_style != NULL) + { + CRStyle *parent_style = curr_style->parent_style; + + cr_style_destroy (curr_style); + curr_style = parent_style; + } + + /* Free the XML nodes. */ + xmlFreeNodeList (root); + + return attr; +} + +/* Match the current list of CSS classes to the CSS and store the result in + stream->curr_attr and in the cache. */ +static void +match_and_cache (term_styled_ostream_t stream) +{ + attributes_t *attr = match (stream); + if (hash_insert_entry (&stream->cache, + stream->curr_classes, stream->curr_classes_length, + attr) == NULL) + abort (); + stream->curr_attr = attr; +} + +static void +term_styled_ostream::begin_use_class (term_styled_ostream_t stream, + const char *classname) +{ + size_t classname_len; + char *p; + void *found; + + if (classname[0] == '\0' || strchr (classname, ' ') != NULL) + /* Invalid classname argument. */ + abort (); + + /* Push the classname onto the classname list. */ + classname_len = strlen (classname); + if (stream->curr_classes_length + 1 + classname_len + 1 + > stream->curr_classes_allocated) + { + size_t new_allocated = stream->curr_classes_length + 1 + classname_len + 1; + if (new_allocated < 2 * stream->curr_classes_allocated) + new_allocated = 2 * stream->curr_classes_allocated; + + stream->curr_classes = xrealloc (stream->curr_classes, new_allocated); + stream->curr_classes_allocated = new_allocated; + } + p = &stream->curr_classes[stream->curr_classes_length]; + *p++ = ' '; + memcpy (p, classname, classname_len); + stream->curr_classes_length += 1 + classname_len; + + /* Uodate stream->curr_attr. */ + if (hash_find_entry (&stream->cache, + stream->curr_classes, stream->curr_classes_length, + &found) < 0) + match_and_cache (stream); + else + stream->curr_attr = (attributes_t *) found; +} + +static void +term_styled_ostream::end_use_class (term_styled_ostream_t stream, + const char *classname) +{ + char *p_end; + char *p_start; + char *p; + void *found; + + if (stream->curr_classes_length == 0) + /* No matching call to begin_use_class. */ + abort (); + + /* Remove the trailing classname. */ + p_end = &stream->curr_classes[stream->curr_classes_length]; + p = p_end; + while (*--p != ' ') + ; + p_start = p + 1; + if (!(p_end - p_start == strlen (classname) + && memcmp (p_start, classname, p_end - p_start) == 0)) + /* The match ing call to begin_use_class used a different classname. */ + abort (); + stream->curr_classes_length = p - stream->curr_classes; + + /* Update stream->curr_attr. */ + if (hash_find_entry (&stream->cache, + stream->curr_classes, stream->curr_classes_length, + &found) < 0) + abort (); + stream->curr_attr = (attributes_t *) found; +} + +/* Constructor. */ + +term_styled_ostream_t +term_styled_ostream_create (int fd, const char *filename, + const char *css_filename) +{ + term_styled_ostream_t stream = + XMALLOC (struct term_styled_ostream_representation); + CRStyleSheet *css_file_contents; + + stream->base.base.vtable = &term_styled_ostream_vtable; + stream->destination = term_ostream_create (fd, filename); + + if (cr_om_parser_simply_parse_file ((const guchar *) css_filename, + CR_UTF_8, /* CR_AUTO is not supported */ + &css_file_contents) != CR_OK) + { + term_ostream_free (stream->destination); + free (stream); + return NULL; + } + stream->css_document = cr_cascade_new (NULL, css_file_contents, NULL); + stream->css_engine = cr_sel_eng_new (); + + stream->curr_classes_allocated = 60; + stream->curr_classes = XNMALLOC (stream->curr_classes_allocated, char); + stream->curr_classes_length = 0; + + hash_init (&stream->cache, 10); + + match_and_cache (stream); + + return stream; +} diff --git a/gnulib-local/lib/term-styled-ostream.oo.h b/gnulib-local/lib/term-styled-ostream.oo.h new file mode 100644 index 000000000..82b35b2b0 --- /dev/null +++ b/gnulib-local/lib/term-styled-ostream.oo.h @@ -0,0 +1,50 @@ +/* Output stream for CSS styled text, producing ANSI escape sequences. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Bruno Haible , 2006. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _TERM_STYLED_OSTREAM_H +#define _TERM_STYLED_OSTREAM_H + +#include "styled-ostream.h" + + +struct term_styled_ostream : struct styled_ostream +{ +methods: +}; + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Create an output stream referring to the file descriptor FD, styled with + the file CSS_FILENAME. + FILENAME is used only for error messages. + Note that the resulting stream must be closed before FD can be closed. + Return NULL upon failure. */ +extern term_styled_ostream_t + term_styled_ostream_create (int fd, const char *filename, + const char *css_filename); + + +#ifdef __cplusplus +} +#endif + +#endif /* _TERM_STYLED_OSTREAM_H */ diff --git a/gnulib-local/modules/term-styled-ostream b/gnulib-local/modules/term-styled-ostream new file mode 100644 index 000000000..230a88b70 --- /dev/null +++ b/gnulib-local/modules/term-styled-ostream @@ -0,0 +1,33 @@ +Description: +Output stream for CSS styled text, producing ANSI escape sequences. + +Files: +lib/term-styled-ostream.oo.h +lib/term-styled-ostream.oo.c + +Depends-on: +styled-ostream +term-ostream +libcroco +libxml +hash +xalloc + +configure.ac: + +Makefile.am: +lib_SOURCES += term-styled-ostream.c +term-styled-ostream.h term-styled-ostream.c : $(top_srcdir)/build-aux/moopp term-styled-ostream.oo.h term-styled-ostream.oo.c styled-ostream.oo.h ostream.oo.h + $(top_srcdir)/build-aux/moopp $(srcdir)/term-styled-ostream.oo.c $(srcdir)/term-styled-ostream.oo.h $(srcdir)/styled-ostream.oo.h $(srcdir)/ostream.oo.h +BUILT_SOURCES += term-styled-ostream.h term-styled-ostream.c term_styled_ostream.priv.h term_styled_ostream.vt.h +CLEANFILES += term-styled-ostream.h term-styled-ostream.c term_styled_ostream.priv.h term_styled_ostream.vt.h + +Include: +"term-styled-ostream.h" + +License: +GPL + +Maintainer: +Bruno Haible +