Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals | Related Pages

cr-box.c

Go to the documentation of this file.
00001 /* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
00002 
00003 /*
00004  * This file is part of The Croco Library
00005  *
00006  * Copyright (C) 2002-2003 Dodji Seketeli <dodji@seketeli.org>
00007  *
00008  * This program is free software; you can redistribute it and/or
00009  * modify it under the terms of version 2.1 of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00020  * USA
00021  */
00022 
00023 /*
00024  *$Id: cr-box.c,v 1.7 2003/06/08 11:46:55 dodji Exp $
00025  */
00026 
00027 #include <string.h>
00028 #include "cr-box.h"
00029 
00030 /**
00031  *@file
00032  *The definition file of the #CRBox class.
00033  */
00034 
00035 
00036 static enum CRStatus
00037 cr_box_construct (CRBox *a_this, CRStyle *a_style,
00038                   gboolean a_set_default_style) ;
00039 
00040 static enum CRStatus
00041 cr_box_edge_to_string (CRBoxEdge *a_this,
00042                        gulong a_nb_indent,
00043                        GString **a_string) ;
00044 
00045 static enum CRBoxType
00046 cr_box_guess_type (CRStyle *a_style) ;
00047 
00048 
00049 /******************************
00050  *Private methods
00051  ******************************/
00052 
00053 /**
00054  *Guess the type of a box from the 'position' rule
00055  *contained in its style data structure.
00056  *@param a_style the style data structure associated to the box.
00057  */
00058 static enum CRBoxType
00059 cr_box_guess_type (CRStyle *a_style)
00060 {
00061         enum CRBoxType box_type = BOX_TYPE_INLINE ;
00062 
00063         if (!a_style)
00064                 return box_type ;
00065 
00066         switch (a_style->display)
00067         {
00068         case DISPLAY_NONE:
00069                 break ;
00070 
00071         case DISPLAY_INLINE:
00072         case DISPLAY_MARKER:
00073                 box_type = BOX_TYPE_INLINE ;
00074                 break ;
00075 
00076         case DISPLAY_BLOCK:
00077         case DISPLAY_LIST_ITEM:
00078         case DISPLAY_TABLE:
00079         case DISPLAY_INLINE_TABLE:
00080         case DISPLAY_TABLE_ROW_GROUP:
00081         case DISPLAY_TABLE_HEADER_GROUP:
00082         case DISPLAY_TABLE_FOOTER_GROUP:
00083         case DISPLAY_TABLE_ROW:
00084         case DISPLAY_TABLE_COLUMN_GROUP:
00085         case DISPLAY_TABLE_COLUMN:
00086         case DISPLAY_TABLE_CELL:
00087         case DISPLAY_TABLE_CAPTION:
00088                 box_type = BOX_TYPE_BLOCK ;
00089                 break ;
00090 
00091         case DISPLAY_COMPACT:
00092                 box_type = BOX_TYPE_COMPACT ;
00093                 break ;
00094 
00095         case DISPLAY_RUN_IN:
00096                 box_type = BOX_TYPE_RUN_IN ;
00097                 break ;
00098 
00099         case DISPLAY_INHERIT:
00100                 if (a_style->parent_style)
00101                         box_type =  
00102                                 cr_box_guess_type (a_style->parent_style) ;
00103                 break ;
00104 
00105         default:
00106                 box_type = BOX_TYPE_INLINE ;
00107                 break ;
00108         }
00109 
00110         return box_type ;
00111 }
00112 
00113 
00114 static enum CRStatus
00115 cr_box_construct (CRBox *a_this, CRStyle *a_style,
00116                   gboolean a_set_default_style)
00117 {
00118         CRStyle *style = a_style ;
00119 
00120         if (!style && a_set_default_style == TRUE)
00121         {
00122                 style = cr_style_new () ;
00123                 if (!style)
00124                 {
00125                         cr_utils_trace_info 
00126                                 ("Could not create style object") ;
00127                         cr_utils_trace_info ("System may be out of memory") ;
00128                         return CR_ERROR ;
00129                 }
00130         }
00131 
00132         a_this->style = style ;
00133         if (a_this->style)
00134                 cr_style_ref (a_this->style) ;
00135         a_this->type = cr_box_guess_type (a_this->style) ;
00136 
00137         return CR_OK ;
00138 }
00139 
00140 
00141 
00142 static enum CRStatus
00143 cr_box_edge_to_string (CRBoxEdge *a_this,
00144                        gulong a_nb_indent,
00145                        GString **a_string)
00146 {
00147         GString *result = NULL ;
00148 
00149         if (*a_string)
00150         {
00151                 result = *a_string ;
00152         }
00153         else
00154         {
00155                 result = g_string_new (NULL) ;
00156                 if (!result)
00157                 {
00158                         cr_utils_trace_info ("Out of memory") ;
00159                         return CR_ERROR ;
00160                 }
00161         }
00162 
00163         cr_utils_dump_n_chars2 (' ', result, 
00164                                 a_nb_indent) ;
00165         g_string_append_printf (result, "(%ld, %ld)\n", 
00166                                 (long int)a_this->x,
00167                                 (long int) a_this->y) ;
00168         cr_utils_dump_n_chars2 (' ', result, 
00169                                 a_nb_indent) ;
00170         g_string_append_printf (result, "width: %ld\n", 
00171                                 (long int)a_this->width) ;
00172 
00173         cr_utils_dump_n_chars2 (' ', result, 
00174                                 a_nb_indent) ;
00175         g_string_append_printf (result, "max-width: %ld\n", 
00176                                 (long int)a_this->max_width) ;
00177 
00178         cr_utils_dump_n_chars2 (' ', 
00179                                 result, a_nb_indent) ;
00180         g_string_append_printf (result, "height: %ld\n", 
00181                                 (long int)a_this->height) ;
00182         cr_utils_dump_n_chars2 (' ', 
00183                                 result, a_nb_indent) ;
00184         g_string_append_printf (result, "x_offset: %ld\n", 
00185                                 (long int)a_this->x_offset) ;
00186         cr_utils_dump_n_chars2 (' ', result, 
00187                                 a_nb_indent) ;
00188         g_string_append_printf (result, "y_offset: %ld\n", 
00189                                 (long int)a_this->y_offset) ;
00190 
00191         return CR_OK ;
00192 }
00193 
00194 /*******************************
00195  *Public methods
00196  *******************************/
00197 
00198 /**
00199  *Instanciates a new #CRBoxData.
00200  *@param a_node the xml node to store in the box.
00201  *@return the newly built #CRBoxData, or null if an error arises.
00202  */
00203 CRBoxData *
00204 cr_box_data_new (xmlNode *a_node)
00205 {
00206         CRBoxData *result = NULL ;
00207 
00208         result = g_try_malloc (sizeof (CRBoxData)) ;
00209         if (!result)
00210         {
00211                 cr_utils_trace_info ("Out of memory") ;
00212                 return NULL;
00213         }
00214         memset (result, 0, sizeof (CRBoxData)) ;
00215         result->xml_node = a_node ;
00216         return result ;
00217 }
00218 
00219 
00220 /**
00221  *Destructor of #CRBoxData.
00222  *@param a_this the current instance 
00223  *of #CRBoxData to be destroyed.
00224  */
00225 void
00226 cr_box_data_destroy (CRBoxData *a_this)
00227 {
00228         if (!a_this)
00229                 return ;
00230 
00231         g_free (a_this) ;
00232 }
00233 
00234 /**
00235  *Instanciates a new #CRBoxContent and set the
00236  *content to text content.
00237  *@param a_text the text content.
00238  */
00239 CRBoxContent *
00240 cr_box_content_new_from_text (guchar *a_text)
00241 {
00242         CRBoxContent *result = NULL ;
00243 
00244         g_return_val_if_fail (a_text, NULL) ;
00245 
00246         result = g_try_malloc (sizeof (CRBoxContent)) ;
00247         if (!result)
00248         {
00249                 cr_utils_trace_info ("Out of memory") ;
00250                 return NULL ;
00251         }
00252         memset (result, 0, sizeof (CRBoxContent)) ;
00253         result->u.text = g_strdup (a_text) ;
00254         result->type = TEXT_CONTENT_TYPE ;
00255         return result ;
00256 }
00257 
00258 /**
00259  *Destructor of #CRBoxContent.
00260  *@param a_this the current instance of #CRBoxContent
00261  *to be destroyed.
00262  */
00263 void
00264 cr_box_content_destroy (CRBoxContent *a_this)
00265 {
00266         if (!a_this)
00267                 return ;
00268 
00269         switch (a_this->type)
00270         {
00271         case TEXT_CONTENT_TYPE:
00272                 if (a_this->u.text)
00273                 {
00274                         g_free (a_this->u.text) ;
00275                         a_this->u.text = NULL ;
00276                 }
00277                 break ;
00278 
00279         default:
00280                 cr_utils_trace_info ("Unrecognized box content type") ;
00281                 cr_utils_trace_info ("This seems to be a mem leak") ;
00282                 break ;
00283         }
00284 
00285         g_free (a_this) ;
00286         return ;
00287 }
00288 
00289 
00290 /**
00291  *Creates a new box model.
00292  *This box model contains an empty box tree.
00293  *Box tree may be added by calling cr_box_append_child().
00294  *@return the newly built instance of #CRBoxModel, or NULL if an
00295  *error arises.
00296  */
00297 CRBoxModel *
00298 cr_box_model_new (void)
00299 {
00300         CRBoxModel *result = NULL ;
00301 
00302         result = g_try_malloc (sizeof (CRBoxModel)) ;
00303         if (!result)
00304         {
00305                 cr_utils_trace_info ("Out of memory") ;
00306                 return NULL ;
00307         }
00308 
00309         memset (result, 0, sizeof (CRBoxModel)) ;
00310 
00311         cr_box_construct (&result->box, NULL, FALSE) ;
00312 
00313         ((CRBox*)result)->type = BOX_TYPE_BOX_MODEL ;
00314         ((CRBox*)result)->box_model = result ;
00315 
00316         return result ;
00317 }
00318 
00319 
00320 void
00321 cr_box_model_destroy (CRBoxModel *a_this)
00322 {
00323         g_return_if_fail (a_this) ;
00324 
00325         cr_box_destroy (&a_this->box) ;
00326 
00327         g_free (a_this) ;
00328 }
00329 
00330 
00331 void
00332 cr_box_model_ref (CRBoxModel *a_this)
00333 {
00334         if (a_this && a_this->ref_count)
00335         {
00336                 a_this->ref_count ++ ;
00337         }
00338 }
00339 
00340 gboolean
00341 cr_box_model_unref (CRBoxModel *a_this)
00342 {
00343         if (a_this && a_this->ref_count)
00344         {
00345                 a_this->ref_count -- ;
00346         }
00347 
00348         if (a_this && a_this->ref_count == 0)
00349         {
00350                 cr_box_model_destroy (a_this) ;
00351                 return TRUE ;
00352         }
00353 
00354         return FALSE ;
00355 }
00356 
00357 
00358 /**
00359  *Instanciates a new box.
00360  *Everything is initialized to zero in it.
00361  *@return the newly created box.
00362  */
00363 CRBox *
00364 cr_box_new (CRStyle *a_style, gboolean a_default_style)
00365 {
00366         CRBox *result = NULL ;
00367 
00368         result = g_try_malloc (sizeof (CRBox)) ;
00369         if (!result)
00370         {
00371                 cr_utils_trace_info ("Out of memory") ;
00372                 goto error ;
00373         }
00374         memset (result, 0, sizeof (CRBox)) ;
00375 
00376         if (cr_box_construct (result, a_style, a_default_style) == CR_OK)
00377                 return result ;
00378 
00379  error:
00380         if (result)
00381         {
00382                 cr_box_destroy (result) ;
00383                 result = NULL ;
00384         }
00385 
00386         return NULL ;
00387 } 
00388 
00389 /**
00390  *Appends a child box to at the end of the current box's children.
00391  *@param a_this the current box.
00392  *@param a_to_append, the box to append.
00393  *@retrurn CR_OK upon successfull completion, an error code otherwise.
00394  */
00395 enum CRStatus
00396 cr_box_append_child (CRBox *a_this, CRBox *a_to_append)
00397 {
00398         CRBox * cur = NULL ;
00399 
00400         g_return_val_if_fail (a_this 
00401                               && a_this->box_model
00402                               && a_to_append, CR_BAD_PARAM_ERROR) ;
00403 
00404         if (!a_this->children)
00405         {
00406                 a_this->children = a_to_append ;
00407                 a_to_append->prev = NULL ;
00408                 a_to_append->parent = a_this ;
00409                 a_to_append->box_model = a_this->box_model ;
00410                 return CR_OK ;
00411         }
00412 
00413         for (cur = a_this->children ; cur && cur->next ; cur = cur->next)
00414                 ;
00415 
00416         cur->next = a_to_append ;
00417         a_to_append->prev = cur ;
00418         a_to_append->parent = a_this ;
00419         a_to_append->box_model = a_this->box_model ;
00420 
00421         return CR_OK ;
00422 }
00423 
00424 
00425 /**
00426  *Inserts a sibling box between two adjacent sibling nodes.
00427  *@param a_prev the box after which we have to insert a new one.
00428  *@param a_next the box before which we have to insert a new one.
00429  *@param a_to_insert the node to insert.
00430  */
00431 enum CRStatus
00432 cr_box_insert_sibling (CRBox *a_prev,
00433                        CRBox *a_next,
00434                        CRBox *a_to_insert)
00435 {
00436         g_return_val_if_fail (a_prev && a_prev->parent
00437                               && a_next && a_prev->next == a_next
00438                               && a_next->parent == a_prev->parent
00439                               && a_prev->box_model
00440                               && a_prev->box_model == a_next->box_model
00441                               && a_to_insert
00442                               && a_to_insert->parent != a_prev->parent,
00443                               CR_BAD_PARAM_ERROR) ;
00444 
00445         a_prev->next = a_to_insert ;
00446         a_to_insert->prev = a_prev ;
00447         a_to_insert->next = a_next ;
00448         a_next->prev = a_to_insert ;
00449         a_to_insert->parent = a_prev->parent ;
00450         a_to_insert->box_model = a_prev->box_model ;
00451 
00452         return CR_OK ;
00453 }
00454 
00455 
00456 /**
00457  *This is for debug purposes ...
00458  *Gives a string representation of the box tree.
00459  *@return the build string of NULL in case of an error.
00460  */
00461 enum CRStatus
00462 cr_box_to_string (CRBox *a_this, 
00463                   gulong a_nb_indent,
00464                   GString **a_string)
00465 {
00466         GString *result = NULL ;
00467         CRBox *cur_box = NULL ;
00468 
00469         g_return_val_if_fail (a_this && a_string, 
00470                               CR_BAD_PARAM_ERROR) ;
00471 
00472         if (*a_string)
00473         {
00474                 result = *a_string ;
00475         }
00476         else
00477         {
00478                 result = g_string_new (NULL) ;
00479                 if (!result)
00480                 {
00481                         cr_utils_trace_info ("Out of memory") ;
00482                         return CR_ERROR ;
00483                 }
00484                 *a_string = result ;
00485         }        
00486 
00487         for (cur_box = a_this ; cur_box ; cur_box = cur_box->next)
00488         {
00489                 if (cur_box->prev || cur_box->parent)
00490                         g_string_append_printf (result, "\n\n") ;
00491 
00492                 cr_utils_dump_n_chars2 (' ', result, a_nb_indent) ;
00493 
00494                 switch (cur_box->type)
00495                 {                        
00496                 case BOX_TYPE_BLOCK:
00497                         g_string_append_printf (result, "BLOCK") ;
00498                         break ;
00499 
00500                 case BOX_TYPE_ANONYMOUS_BLOCK:
00501                         g_string_append_printf (result, "ANONYMOUS BLOCK") ;
00502                         break ;
00503 
00504                 case BOX_TYPE_INLINE:
00505                         g_string_append_printf (result, "INLINE") ;
00506                         break ;
00507 
00508                 case BOX_TYPE_ANONYMOUS_INLINE:
00509                         g_string_append_printf (result, "ANONYMOUS INLINE") ;
00510                         break ;
00511 
00512                 case BOX_TYPE_COMPACT:
00513                         g_string_append_printf (result, "COMPACT") ;
00514                         break ;
00515 
00516                 case BOX_TYPE_RUN_IN:
00517                         g_string_append_printf (result, "RUN IN") ;
00518                         break ;
00519 
00520                 case BOX_TYPE_BOX_MODEL:
00521                         g_string_append_printf (result, "Root") ;
00522                         break ;
00523 
00524                 default:
00525                         g_string_append_printf (result, "UNKNOWN") ;
00526                         break ;
00527                 }
00528                 
00529                 g_string_append_printf (result, " box\n") ;
00530                 cr_utils_dump_n_chars2 (' ', result, a_nb_indent) ;
00531                 g_string_append_printf (result, "{") ;
00532         
00533                 if (cur_box->box_data && cur_box->box_data->xml_node)
00534                 {
00535                         switch (cur_box->box_data->xml_node->type)
00536                         {
00537                         case XML_ELEMENT_NODE:
00538                                 cr_utils_dump_n_chars2 
00539                                         (' ', result, a_nb_indent) ;
00540                                 g_string_append_printf 
00541                                         (result, "xml-node-name: %s\n", 
00542                                          cur_box->box_data->xml_node->name) ;
00543                                 break ;
00544 
00545                         case XML_TEXT_NODE:
00546                                 cr_utils_dump_n_chars2 
00547                                         (' ', result, a_nb_indent) ;
00548                                 g_string_append_printf 
00549                                         (result, "xml-text-node\n") ;
00550                                 break ;
00551 
00552                         default:                                
00553                                 break ;
00554                         }
00555                 }
00556 
00557                 cr_utils_dump_n_chars2 (' ', result, a_nb_indent + 2) ;
00558                 g_string_append_printf (result, "/*****%s begin*****/\n", 
00559                                         "outer_edge") ;
00560                 cr_box_edge_to_string (&cur_box->outer_edge,
00561                                        a_nb_indent + 2, &result) ;
00562                 cr_utils_dump_n_chars2 (' ', result, a_nb_indent + 2) ;
00563                 g_string_append_printf (result, "/*****%s end*****/\n", 
00564                                         "outer_edge") ;
00565 
00566                 cr_utils_dump_n_chars2 (' ', result, a_nb_indent + 2) ;
00567                 g_string_append_printf (result, "/*****%s begin*****/\n", 
00568                                         "border_edge") ;
00569                 cr_box_edge_to_string (&cur_box->border_edge, 
00570                                        a_nb_indent + 2, &result) ;
00571                 cr_utils_dump_n_chars2 (' ', result, a_nb_indent + 2) ;
00572                 g_string_append_printf (result, "/*****%s end*****/\n", 
00573                                         "border_edge") ;
00574 
00575                 cr_utils_dump_n_chars2 (' ', result, a_nb_indent + 2) ;
00576                 g_string_append_printf (result, "/*****%s begin*****/\n", 
00577                                         "padding_edge") ;
00578                 cr_box_edge_to_string (&cur_box->padding_edge,
00579                                        a_nb_indent + 2, &result) ;
00580                 cr_utils_dump_n_chars2 (' ', result, a_nb_indent + 2) ;
00581                 g_string_append_printf (result, "/*****%s end*****/\n", 
00582                                         "padding_edge") ;
00583 
00584                 cr_utils_dump_n_chars2 (' ', result, a_nb_indent + 2) ;
00585                 g_string_append_printf (result, "/*****%s begin*****/\n", 
00586                                         "inner_edge") ;
00587                 cr_box_edge_to_string (&cur_box->inner_edge,
00588                                        a_nb_indent + 2, &result) ;
00589                 cr_utils_dump_n_chars2 (' ', result, a_nb_indent + 2) ;
00590                 g_string_append_printf (result, "/*****%s end*****/\n", 
00591                                         "inner_edge") ;
00592 
00593                 if (cur_box->children)
00594                 {
00595                         g_string_append_printf (result, "\n") ;
00596                         cr_box_to_string (cur_box->children,
00597                                           a_nb_indent + 2, &result) ;
00598                 }
00599 
00600                 g_string_append_printf (result, "\n") ;
00601                 cr_utils_dump_n_chars2 (' ', result, a_nb_indent) ;
00602                 g_string_append_printf (result, "}\n") ;
00603         }
00604 
00605         return CR_OK ;
00606 }
00607 
00608 enum CRStatus
00609 cr_box_dump_to_file (CRBox *a_this, 
00610                      gulong a_nb_indent,
00611                      FILE *a_filep)
00612 {
00613         GString *str = NULL ;
00614         enum CRStatus status = CR_OK ;
00615 
00616         g_return_val_if_fail (a_this && a_filep,
00617                               CR_BAD_PARAM_ERROR) ;
00618 
00619         status = cr_box_to_string (a_this, a_nb_indent, &str) ;
00620 
00621         if (status != CR_OK)
00622         {
00623                 cr_utils_trace_info ("An error occured "
00624                                      "during in memory serialisation") ;
00625                 goto cleanup ;
00626         }
00627 
00628         if (!str || !str->str)
00629         {
00630                 cr_utils_trace_info ("Error: Box could not be serialised") ;
00631                 goto cleanup ;
00632         }
00633 
00634         if (!fwrite (str->str, 1, str->len, a_filep))
00635         {
00636                 cr_utils_trace_info ("An error occured during"
00637                                      "serialisation into file") ;
00638                 status = CR_ERROR ;
00639                 goto cleanup ;
00640         }
00641 
00642         status = CR_OK ;
00643 
00644  cleanup:
00645 
00646         if (str)
00647         {
00648                 g_string_free (str, TRUE) ;
00649                 str = NULL ;
00650         }
00651         return status ;
00652 }
00653 
00654 /**
00655  *Increments the reference count of
00656  *the current instance of #CRBox.
00657  */
00658 enum CRStatus
00659 cr_box_ref (CRBox *a_this)
00660 {
00661         g_return_val_if_fail (a_this, CR_BAD_PARAM_ERROR) ;
00662         
00663         a_this->ref_count ++ ;
00664 
00665         return TRUE ;
00666 }
00667 
00668 
00669 /**
00670  *Decrements the current instance's ref count.
00671  *If the ref count reaches zero, the instance is destroyed.
00672  *@param a_this the current instance.
00673  *@return TRUE if the ref count reached zero and the instance has been
00674  *destroyed, FALSE otherwise.
00675  */
00676 gboolean
00677 cr_box_unref (CRBox *a_this)
00678 {
00679         g_return_val_if_fail (a_this, FALSE) ;
00680 
00681         if (a_this->ref_count)
00682                 a_this->ref_count -- ;
00683 
00684         if (a_this->ref_count == 0)
00685         {
00686                 cr_box_destroy (a_this) ;
00687                 return TRUE ;
00688         }
00689 
00690         return FALSE ;
00691 }
00692 
00693 
00694 /**
00695  *Destructor of #CRBox.
00696  *recursively destroys all
00697  *the children nodes of the current node.
00698  *@param a_this the current box to destroy.
00699  */
00700 void
00701 cr_box_destroy (CRBox *a_this)
00702 
00703 {
00704         CRBox *cur_box = NULL ;
00705 
00706         g_return_if_fail (a_this) ;
00707         
00708         for (cur_box = a_this; cur_box && cur_box->next ; 
00709              cur_box = cur_box->next) ;
00710 
00711         for (; cur_box ; cur_box = cur_box->prev)
00712         {
00713                 if (cur_box->content)
00714                 {
00715                         cr_box_content_destroy (cur_box->content) ;
00716                         cur_box->content = NULL ;
00717                 }
00718 
00719                 if (cur_box->style)
00720                 {
00721                         cr_style_unref (cur_box->style) ;
00722                         cur_box->style = NULL ;
00723                 }
00724 
00725                 if (cur_box->next)
00726                 {
00727                         g_free (cur_box->next) ;
00728                         cur_box->next = NULL ;
00729                 }
00730 
00731                 if (cur_box->children)
00732                 {
00733                         cr_box_destroy (cur_box->children) ;
00734                         g_free (cur_box->children) ;
00735                         cur_box->children = NULL ;
00736                 }
00737         }
00738 }
00739 

Generated on Wed Oct 1 01:36:44 2003 for Libcroco by doxygen 1.3.3