1/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ 2 3#line 1 "term-styled-ostream.oo.c" 4/* Output stream for CSS styled text, producing ANSI escape sequences. 5 Copyright (C) 2006-2007 Free Software Foundation, Inc. 6 Written by Bruno Haible <bruno@clisp.org>, 2006. 7 8 This program is free software: you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 3 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 20 21#include <config.h> 22 23/* Specification. */ 24#include "term-styled-ostream.h" 25 26#include <stdlib.h> 27 28#include <cr-om-parser.h> 29#include <cr-sel-eng.h> 30#include <cr-style.h> 31#include <cr-rgb.h> 32/* <cr-fonts.h> has a broken double-inclusion guard in libcroco-0.6.1. */ 33#ifndef __CR_FONTS_H__ 34# include <cr-fonts.h> 35#endif 36#include <cr-string.h> 37 38#include "term-ostream.h" 39#include "hash.h" 40#include "xalloc.h" 41 42 43/* CSS matching works as follows: 44 Suppose we have an element inside class "header" inside class "table". 45 We pretend to have an XML tree that looks like this: 46 47 (root) 48 +----table 49 +----header 50 51 For each of these XML nodes, the CSS matching engine can report the 52 matching CSS declarations. We extract the CSS property values that 53 matter for terminal styling and cache them. */ 54 55/* Attributes that can be set on a character. */ 56typedef struct 57{ 58 term_color_t color; 59 term_color_t bgcolor; 60 term_weight_t weight; 61 term_posture_t posture; 62 term_underline_t underline; 63} attributes_t; 64 65#line 66 "term-styled-ostream.c" 66#if !IS_CPLUSPLUS 67#define term_styled_ostream_representation any_ostream_representation 68#endif 69#include "term_styled_ostream.priv.h" 70 71const typeinfo_t term_styled_ostream_typeinfo = { "term_styled_ostream" }; 72 73static const typeinfo_t * const term_styled_ostream_superclasses[] = 74 { term_styled_ostream_SUPERCLASSES }; 75 76#define super styled_ostream_vtable 77 78#line 82 "term-styled-ostream.oo.c" 79 80/* Implementation of ostream_t methods. */ 81 82static void 83term_styled_ostream__write_mem (term_styled_ostream_t stream, 84 const void *data, size_t len) 85{ 86 term_ostream_set_color (stream->destination, stream->curr_attr->color); 87 term_ostream_set_bgcolor (stream->destination, stream->curr_attr->bgcolor); 88 term_ostream_set_weight (stream->destination, stream->curr_attr->weight); 89 term_ostream_set_posture (stream->destination, stream->curr_attr->posture); 90 term_ostream_set_underline (stream->destination, stream->curr_attr->underline); 91 92 term_ostream_write_mem (stream->destination, data, len); 93} 94 95static void 96term_styled_ostream__flush (term_styled_ostream_t stream) 97{ 98 term_ostream_flush (stream->destination); 99} 100 101static void 102term_styled_ostream__free (term_styled_ostream_t stream) 103{ 104 term_ostream_free (stream->destination); 105 cr_cascade_destroy (stream->css_document); 106 cr_sel_eng_destroy (stream->css_engine); 107 free (stream->curr_classes); 108 { 109 void *ptr = NULL; 110 const void *key; 111 size_t keylen; 112 void *data; 113 114 while (hash_iterate (&stream->cache, &ptr, &key, &keylen, &data) == 0) 115 { 116 free (data); 117 } 118 } 119 hash_destroy (&stream->cache); 120 free (stream); 121} 122 123/* Implementation of styled_ostream_t methods. */ 124 125/* CRStyle doesn't contain a value for the 'text-decoration' property. 126 So we have to extend it. */ 127 128enum CRXTextDecorationType 129{ 130 TEXT_DECORATION_NONE, 131 TEXT_DECORATION_UNDERLINE, 132 TEXT_DECORATION_OVERLINE, 133 TEXT_DECORATION_LINE_THROUGH, 134 TEXT_DECORATION_BLINK, 135 TEXT_DECORATION_INHERIT 136}; 137 138typedef struct _CRXStyle 139{ 140 struct _CRXStyle *parent_style; 141 CRStyle *base; 142 enum CRXTextDecorationType text_decoration; 143} CRXStyle; 144 145/* An extended version of cr_style_new. */ 146static CRXStyle * 147crx_style_new (gboolean a_set_props_to_initial_values) 148{ 149 CRStyle *base; 150 CRXStyle *result; 151 152 base = cr_style_new (a_set_props_to_initial_values); 153 if (base == NULL) 154 return NULL; 155 156 result = XMALLOC (CRXStyle); 157 result->base = base; 158 if (a_set_props_to_initial_values) 159 result->text_decoration = TEXT_DECORATION_NONE; 160 else 161 result->text_decoration = TEXT_DECORATION_INHERIT; 162 163 return result; 164} 165 166/* An extended version of cr_style_destroy. */ 167static void 168crx_style_destroy (CRXStyle *a_style) 169{ 170 cr_style_destroy (a_style->base); 171 free (a_style); 172} 173 174/* An extended version of cr_sel_eng_get_matched_style. */ 175static enum CRStatus 176crx_sel_eng_get_matched_style (CRSelEng * a_this, CRCascade * a_cascade, 177 xmlNode * a_node, 178 CRXStyle * a_parent_style, CRXStyle ** a_style, 179 gboolean a_set_props_to_initial_values) 180{ 181 enum CRStatus status; 182 CRPropList *props = NULL; 183 184 if (!(a_this && a_cascade && a_node && a_style)) 185 return CR_BAD_PARAM_ERROR; 186 187 status = cr_sel_eng_get_matched_properties_from_cascade (a_this, a_cascade, 188 a_node, &props); 189 if (!(status == CR_OK)) 190 return status; 191 192 if (props) 193 { 194 CRXStyle *style; 195 196 if (!*a_style) 197 { 198 *a_style = crx_style_new (a_set_props_to_initial_values); 199 if (!*a_style) 200 return CR_ERROR; 201 } 202 else 203 { 204 if (a_set_props_to_initial_values) 205 { 206 cr_style_set_props_to_initial_values ((*a_style)->base); 207 (*a_style)->text_decoration = TEXT_DECORATION_NONE; 208 } 209 else 210 { 211 cr_style_set_props_to_default_values ((*a_style)->base); 212 (*a_style)->text_decoration = TEXT_DECORATION_INHERIT; 213 } 214 } 215 style = *a_style; 216 style->parent_style = a_parent_style; 217 style->base->parent_style = 218 (a_parent_style != NULL ? a_parent_style->base : NULL); 219 220 { 221 CRPropList *cur; 222 223 for (cur = props; cur != NULL; cur = cr_prop_list_get_next (cur)) 224 { 225 CRDeclaration *decl = NULL; 226 227 cr_prop_list_get_decl (cur, &decl); 228 cr_style_set_style_from_decl (style->base, decl); 229 if (decl != NULL 230 && decl->property != NULL 231 && decl->property->stryng != NULL 232 && decl->property->stryng->str != NULL) 233 { 234 if (strcmp (decl->property->stryng->str, "text-decoration") == 0 235 && decl->value != NULL 236 && decl->value->type == TERM_IDENT 237 && decl->value->content.str != NULL) 238 { 239 const char *value = 240 cr_string_peek_raw_str (decl->value->content.str); 241 242 if (value != NULL) 243 { 244 if (strcmp (value, "none") == 0) 245 style->text_decoration = TEXT_DECORATION_NONE; 246 else if (strcmp (value, "underline") == 0) 247 style->text_decoration = TEXT_DECORATION_UNDERLINE; 248 else if (strcmp (value, "overline") == 0) 249 style->text_decoration = TEXT_DECORATION_OVERLINE; 250 else if (strcmp (value, "line-through") == 0) 251 style->text_decoration = TEXT_DECORATION_LINE_THROUGH; 252 else if (strcmp (value, "blink") == 0) 253 style->text_decoration = TEXT_DECORATION_BLINK; 254 else if (strcmp (value, "inherit") == 0) 255 style->text_decoration = TEXT_DECORATION_INHERIT; 256 } 257 } 258 } 259 } 260 } 261 262 cr_prop_list_destroy (props); 263 } 264 265 return CR_OK; 266} 267 268/* According to the CSS2 spec, sections 6.1 and 6.2, we need to do a 269 propagation: specified values -> computed values -> actual values. 270 The computed values are necessary. libcroco does not compute them for us. 271 The function cr_style_resolve_inherited_properties is also not sufficient: 272 it handles only the case of inheritance, not the case of non-inheritance. 273 So we write style accessors that fetch the computed value, doing the 274 inheritance on the fly. 275 We then compute the actual values from the computed values; for colors, 276 this is done through the rgb_to_color method. */ 277 278static term_color_t 279style_compute_color_value (CRStyle *style, enum CRRgbProp which, 280 term_ostream_t stream) 281{ 282 for (;;) 283 { 284 if (style == NULL) 285 return COLOR_DEFAULT; 286 if (cr_rgb_is_set_to_inherit (&style->rgb_props[which].sv)) 287 style = style->parent_style; 288 else if (cr_rgb_is_set_to_transparent (&style->rgb_props[which].sv)) 289 /* A transparent color occurs as default background color, set by 290 cr_style_set_props_to_default_values. */ 291 return COLOR_DEFAULT; 292 else 293 { 294 CRRgb rgb; 295 int r; 296 int g; 297 int b; 298 299 cr_rgb_copy (&rgb, &style->rgb_props[which].sv); 300 if (cr_rgb_compute_from_percentage (&rgb) != CR_OK) 301 abort (); 302 r = rgb.red & 0xff; 303 g = rgb.green & 0xff; 304 b = rgb.blue & 0xff; 305 return term_ostream_rgb_to_color (stream, r, g, b); 306 } 307 } 308} 309 310static term_weight_t 311style_compute_font_weight_value (const CRStyle *style) 312{ 313 int value = 0; 314 for (;;) 315 { 316 if (style == NULL) 317 value += 4; 318 else 319 switch (style->font_weight) 320 { 321 case FONT_WEIGHT_INHERIT: 322 style = style->parent_style; 323 continue; 324 case FONT_WEIGHT_BOLDER: 325 value += 1; 326 style = style->parent_style; 327 continue; 328 case FONT_WEIGHT_LIGHTER: 329 value -= 1; 330 style = style->parent_style; 331 continue; 332 case FONT_WEIGHT_100: 333 value += 1; 334 break; 335 case FONT_WEIGHT_200: 336 value += 2; 337 break; 338 case FONT_WEIGHT_300: 339 value += 3; 340 break; 341 case FONT_WEIGHT_400: case FONT_WEIGHT_NORMAL: 342 value += 4; 343 break; 344 case FONT_WEIGHT_500: 345 value += 5; 346 break; 347 case FONT_WEIGHT_600: 348 value += 6; 349 break; 350 case FONT_WEIGHT_700: case FONT_WEIGHT_BOLD: 351 value += 7; 352 break; 353 case FONT_WEIGHT_800: 354 value += 8; 355 break; 356 case FONT_WEIGHT_900: 357 value += 9; 358 break; 359 default: 360 abort (); 361 } 362 /* Value >= 600 -> WEIGHT_BOLD. Value <= 500 -> WEIGHT_NORMAL. */ 363 return (value >= 6 ? WEIGHT_BOLD : WEIGHT_NORMAL); 364 } 365} 366 367static term_posture_t 368style_compute_font_posture_value (const CRStyle *style) 369{ 370 for (;;) 371 { 372 if (style == NULL) 373 return POSTURE_DEFAULT; 374 switch (style->font_style) 375 { 376 case FONT_STYLE_INHERIT: 377 style = style->parent_style; 378 break; 379 case FONT_STYLE_NORMAL: 380 return POSTURE_NORMAL; 381 case FONT_STYLE_ITALIC: 382 case FONT_STYLE_OBLIQUE: 383 return POSTURE_ITALIC; 384 default: 385 abort (); 386 } 387 } 388} 389 390static term_underline_t 391style_compute_text_underline_value (const CRXStyle *style) 392{ 393 for (;;) 394 { 395 if (style == NULL) 396 return UNDERLINE_DEFAULT; 397 switch (style->text_decoration) 398 { 399 case TEXT_DECORATION_INHERIT: 400 style = style->parent_style; 401 break; 402 case TEXT_DECORATION_NONE: 403 case TEXT_DECORATION_OVERLINE: 404 case TEXT_DECORATION_LINE_THROUGH: 405 case TEXT_DECORATION_BLINK: 406 return UNDERLINE_OFF; 407 case TEXT_DECORATION_UNDERLINE: 408 return UNDERLINE_ON; 409 default: 410 abort (); 411 } 412 } 413} 414 415/* Match the current list of CSS classes to the CSS and return the result. */ 416static attributes_t * 417match (term_styled_ostream_t stream) 418{ 419 xmlNodePtr root; 420 xmlNodePtr curr; 421 char *p_end; 422 char *p_start; 423 CRXStyle *curr_style; 424 CRStyle *curr_style_base; 425 attributes_t *attr; 426 427 /* Create a hierarchy of XML nodes. */ 428 root = xmlNewNode (NULL, (const xmlChar *) "__root__"); 429 root->type = XML_ELEMENT_NODE; 430 curr = root; 431 p_end = &stream->curr_classes[stream->curr_classes_length]; 432 p_start = stream->curr_classes; 433 while (p_start < p_end) 434 { 435 char *p; 436 xmlNodePtr child; 437 438 if (!(*p_start == ' ')) 439 abort (); 440 p_start++; 441 for (p = p_start; p < p_end && *p != ' '; p++) 442 ; 443 444 /* Temporarily replace the ' ' by '\0'. */ 445 *p = '\0'; 446 child = xmlNewNode (NULL, (const xmlChar *) p_start); 447 child->type = XML_ELEMENT_NODE; 448 xmlSetProp (child, (const xmlChar *) "class", (const xmlChar *) p_start); 449 *p = ' '; 450 451 if (xmlAddChild (curr, child) == NULL) 452 /* Error! Shouldn't happen. */ 453 abort (); 454 455 curr = child; 456 p_start = p; 457 } 458 459 /* Retrieve the matching CSS declarations. */ 460 /* Not curr_style = crx_style_new (TRUE); because that assumes that the 461 default foreground color is black and that the default background color 462 is white, which is not necessarily true in a terminal context. */ 463 curr_style = NULL; 464 for (curr = root; curr != NULL; curr = curr->children) 465 { 466 CRXStyle *parent_style = curr_style; 467 curr_style = NULL; 468 469 if (crx_sel_eng_get_matched_style (stream->css_engine, 470 stream->css_document, 471 curr, 472 parent_style, &curr_style, 473 FALSE) != CR_OK) 474 abort (); 475 if (curr_style == NULL) 476 /* No declarations matched this node. Inherit all values. */ 477 curr_style = parent_style; 478 else 479 /* curr_style is a new style, inheriting from parent_style. */ 480 ; 481 } 482 curr_style_base = (curr_style != NULL ? curr_style->base : NULL); 483 484 /* Extract the CSS declarations that we can use. */ 485 attr = XMALLOC (attributes_t); 486 attr->color = 487 style_compute_color_value (curr_style_base, RGB_PROP_COLOR, 488 stream->destination); 489 attr->bgcolor = 490 style_compute_color_value (curr_style_base, RGB_PROP_BACKGROUND_COLOR, 491 stream->destination); 492 attr->weight = style_compute_font_weight_value (curr_style_base); 493 attr->posture = style_compute_font_posture_value (curr_style_base); 494 attr->underline = style_compute_text_underline_value (curr_style); 495 496 /* Free the style chain. */ 497 while (curr_style != NULL) 498 { 499 CRXStyle *parent_style = curr_style->parent_style; 500 501 crx_style_destroy (curr_style); 502 curr_style = parent_style; 503 } 504 505 /* Free the XML nodes. */ 506 xmlFreeNodeList (root); 507 508 return attr; 509} 510 511/* Match the current list of CSS classes to the CSS and store the result in 512 stream->curr_attr and in the cache. */ 513static void 514match_and_cache (term_styled_ostream_t stream) 515{ 516 attributes_t *attr = match (stream); 517 if (hash_insert_entry (&stream->cache, 518 stream->curr_classes, stream->curr_classes_length, 519 attr) == NULL) 520 abort (); 521 stream->curr_attr = attr; 522} 523 524static void 525term_styled_ostream__begin_use_class (term_styled_ostream_t stream, 526 const char *classname) 527{ 528 size_t classname_len; 529 char *p; 530 void *found; 531 532 if (classname[0] == '\0' || strchr (classname, ' ') != NULL) 533 /* Invalid classname argument. */ 534 abort (); 535 536 /* Push the classname onto the classname list. */ 537 classname_len = strlen (classname); 538 if (stream->curr_classes_length + 1 + classname_len + 1 539 > stream->curr_classes_allocated) 540 { 541 size_t new_allocated = stream->curr_classes_length + 1 + classname_len + 1; 542 if (new_allocated < 2 * stream->curr_classes_allocated) 543 new_allocated = 2 * stream->curr_classes_allocated; 544 545 stream->curr_classes = xrealloc (stream->curr_classes, new_allocated); 546 stream->curr_classes_allocated = new_allocated; 547 } 548 p = &stream->curr_classes[stream->curr_classes_length]; 549 *p++ = ' '; 550 memcpy (p, classname, classname_len); 551 stream->curr_classes_length += 1 + classname_len; 552 553 /* Uodate stream->curr_attr. */ 554 if (hash_find_entry (&stream->cache, 555 stream->curr_classes, stream->curr_classes_length, 556 &found) < 0) 557 match_and_cache (stream); 558 else 559 stream->curr_attr = (attributes_t *) found; 560} 561 562static void 563term_styled_ostream__end_use_class (term_styled_ostream_t stream, 564 const char *classname) 565{ 566 char *p_end; 567 char *p_start; 568 char *p; 569 void *found; 570 571 if (stream->curr_classes_length == 0) 572 /* No matching call to begin_use_class. */ 573 abort (); 574 575 /* Remove the trailing classname. */ 576 p_end = &stream->curr_classes[stream->curr_classes_length]; 577 p = p_end; 578 while (*--p != ' ') 579 ; 580 p_start = p + 1; 581 if (!(p_end - p_start == strlen (classname) 582 && memcmp (p_start, classname, p_end - p_start) == 0)) 583 /* The match ing call to begin_use_class used a different classname. */ 584 abort (); 585 stream->curr_classes_length = p - stream->curr_classes; 586 587 /* Update stream->curr_attr. */ 588 if (hash_find_entry (&stream->cache, 589 stream->curr_classes, stream->curr_classes_length, 590 &found) < 0) 591 abort (); 592 stream->curr_attr = (attributes_t *) found; 593} 594 595/* Constructor. */ 596 597term_styled_ostream_t 598term_styled_ostream_create (int fd, const char *filename, 599 const char *css_filename) 600{ 601 term_styled_ostream_t stream = 602 XMALLOC (struct term_styled_ostream_representation); 603 CRStyleSheet *css_file_contents; 604 605 stream->base.base.vtable = &term_styled_ostream_vtable; 606 stream->destination = term_ostream_create (fd, filename); 607 608 if (cr_om_parser_simply_parse_file ((const guchar *) css_filename, 609 CR_UTF_8, /* CR_AUTO is not supported */ 610 &css_file_contents) != CR_OK) 611 { 612 term_ostream_free (stream->destination); 613 free (stream); 614 return NULL; 615 } 616 stream->css_document = cr_cascade_new (NULL, css_file_contents, NULL); 617 stream->css_engine = cr_sel_eng_new (); 618 619 stream->curr_classes_allocated = 60; 620 stream->curr_classes = XNMALLOC (stream->curr_classes_allocated, char); 621 stream->curr_classes_length = 0; 622 623 hash_init (&stream->cache, 10); 624 625 match_and_cache (stream); 626 627 return stream; 628} 629 630#line 631 "term-styled-ostream.c" 631 632const struct term_styled_ostream_implementation term_styled_ostream_vtable = 633{ 634 term_styled_ostream_superclasses, 635 sizeof (term_styled_ostream_superclasses) / sizeof (term_styled_ostream_superclasses[0]), 636 sizeof (struct term_styled_ostream_representation), 637 term_styled_ostream__write_mem, 638 term_styled_ostream__flush, 639 term_styled_ostream__free, 640 term_styled_ostream__begin_use_class, 641 term_styled_ostream__end_use_class, 642}; 643 644#if !HAVE_INLINE 645 646/* Define the functions that invoke the methods. */ 647 648void 649term_styled_ostream_write_mem (term_styled_ostream_t first_arg, const void *data, size_t len) 650{ 651 const struct term_styled_ostream_implementation *vtable = 652 ((struct term_styled_ostream_representation_header *) (struct term_styled_ostream_representation *) first_arg)->vtable; 653 vtable->write_mem (first_arg,data,len); 654} 655 656void 657term_styled_ostream_flush (term_styled_ostream_t first_arg) 658{ 659 const struct term_styled_ostream_implementation *vtable = 660 ((struct term_styled_ostream_representation_header *) (struct term_styled_ostream_representation *) first_arg)->vtable; 661 vtable->flush (first_arg); 662} 663 664void 665term_styled_ostream_free (term_styled_ostream_t first_arg) 666{ 667 const struct term_styled_ostream_implementation *vtable = 668 ((struct term_styled_ostream_representation_header *) (struct term_styled_ostream_representation *) first_arg)->vtable; 669 vtable->free (first_arg); 670} 671 672void 673term_styled_ostream_begin_use_class (term_styled_ostream_t first_arg, const char *classname) 674{ 675 const struct term_styled_ostream_implementation *vtable = 676 ((struct term_styled_ostream_representation_header *) (struct term_styled_ostream_representation *) first_arg)->vtable; 677 vtable->begin_use_class (first_arg,classname); 678} 679 680void 681term_styled_ostream_end_use_class (term_styled_ostream_t first_arg, const char *classname) 682{ 683 const struct term_styled_ostream_implementation *vtable = 684 ((struct term_styled_ostream_representation_header *) (struct term_styled_ostream_representation *) first_arg)->vtable; 685 vtable->end_use_class (first_arg,classname); 686} 687 688#endif 689