]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
New module 'term-styled-ostream'.
authorBruno Haible <bruno@clisp.org>
Tue, 12 Dec 2006 14:22:13 +0000 (14:22 +0000)
committerBruno Haible <bruno@clisp.org>
Tue, 23 Jun 2009 10:14:27 +0000 (12:14 +0200)
gnulib-local/ChangeLog
gnulib-local/Makefile.am
gnulib-local/lib/term-styled-ostream.oo.c [new file with mode: 0644]
gnulib-local/lib/term-styled-ostream.oo.h [new file with mode: 0644]
gnulib-local/modules/term-styled-ostream [new file with mode: 0644]

index cc9ad2e4a84cb70f25b0ee212e198fb4d6443815..66d9f0339bd7c417a82c53f3d067e8365336a17e 100644 (file)
@@ -1,3 +1,10 @@
+2006-12-01  Bruno Haible  <bruno@clisp.org>
+
+       * 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  <bruno@clisp.org>
 
        * modules/html-styled-ostream: New file.
index 2395a1e60036a5cb15d5d23ce1c283e279bdb5e6..e33d6a386c1e9d66ec16c1a3502598e5013345ad 100644 (file)
@@ -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 (file)
index 0000000..6de9662
--- /dev/null
@@ -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 <bruno@clisp.org>, 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 <config.h>
+
+/* Specification.  */
+#include "term-styled-ostream.h"
+
+#include <stdlib.h>
+
+#include <cr-om-parser.h>
+#include <cr-sel-eng.h>
+#include <cr-style.h>
+#include <cr-rgb.h>
+#include <cr-fonts.h>
+
+#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 (file)
index 0000000..82b35b2
--- /dev/null
@@ -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 <bruno@clisp.org>, 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 (file)
index 0000000..230a88b
--- /dev/null
@@ -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
+