Code

warn if unable to fill an unbounded area
[inkscape.git] / src / libcroco / cr-declaration.c
1 /* -*- Mode: C; indent-tabs-mode: ni; c-basic-offset: 8 -*- */
3 /*
4  * This file is part of The Croco Library
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2.1 of the GNU Lesser General Public
8  * License as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA
19  *
20  * Author: Dodji Seketeli.
21  * See COPYRIGHTS file for copyright information.
22  */
25 #include <string.h>
26 #include "cr-declaration.h"
27 #include "cr-statement.h"
28 #include "cr-parser.h"
30 /**
31  *@file
32  *The definition of the #CRDeclaration class.
33  */
35 /**
36  *Dumps (serializes) one css declaration to a file.
37  *@param a_this the current instance of #CRDeclaration.
38  *@param a_fp the destination file pointer.
39  *@param a_indent the number of indentation white char. 
40  */
41 static void
42 dump (CRDeclaration * a_this, FILE * a_fp, glong a_indent)
43 {
44         g_return_if_fail (a_this);
46         gchar *str = cr_declaration_to_string (a_this, a_indent);
47         if (str) {
48                 fprintf (a_fp, "%s", str);
49                 g_free (str);
50                 str = NULL;
51         }
52 }
54 /**
55  *Constructor of #CRDeclaration.
56  *@param a_property the property string of the declaration
57  *@param a_value the value expression of the declaration.
58  *@return the newly built instance of #CRDeclaration, or NULL in
59  *case of error.
60  */
61 CRDeclaration *
62 cr_declaration_new (CRStatement * a_statement,
63                     CRString * a_property, CRTerm * a_value)
64 {
65         g_return_val_if_fail (a_property, NULL);
67         if (a_statement)
68                 g_return_val_if_fail (a_statement
69                                       && ((a_statement->type == RULESET_STMT)
70                                           || (a_statement->type
71                                               == AT_FONT_FACE_RULE_STMT)
72                                           || (a_statement->type
73                                               == AT_PAGE_RULE_STMT)), NULL);
75         CRDeclaration * result = (CRDeclaration *)g_try_malloc (sizeof (CRDeclaration));
76         if (!result) {
77                 cr_utils_trace_info ("Out of memory");
78                 return NULL;
79         }
80         memset (result, 0, sizeof (CRDeclaration));
81         result->property = a_property;
82         result->value = a_value;
84         if (a_value) {
85                 cr_term_ref (a_value);
86         }
87         result->parent_statement = a_statement;
88         return result;
89 }
91 /**
92  *Parses a text buffer that contains
93  *a css declaration.
94  *
95  *@param a_statement the parent css2 statement of this
96  *this declaration. Must be non NULL and of type
97  *RULESET_STMT (must be a ruleset).
98  *@param a_str the string that contains the statement.
99  *@param a_enc the encoding of a_str.
100  *@return the parsed declaration, or NULL in case of error.
101  */
102 CRDeclaration *
103 cr_declaration_parse_from_buf (CRStatement * a_statement,
104                                const guchar * a_str, enum CREncoding a_enc)
106         enum CRStatus status = CR_OK;
107         CRTerm *value = NULL;
108         CRString *property = NULL;
109         CRDeclaration *result = NULL;
110         gboolean important = FALSE;
112         g_return_val_if_fail (a_str, NULL);
113         if (a_statement)
114                 g_return_val_if_fail (a_statement->type == RULESET_STMT,
115                                       NULL);
117         CRParser *parser = (CRParser *)
118                         cr_parser_new_from_buf ((guchar*)a_str,
119                                   strlen ((char *)a_str), a_enc, FALSE);
120         g_return_val_if_fail (parser, NULL);
122         status = cr_parser_try_to_skip_spaces_and_comments (parser);
123         if (status != CR_OK)
124                 goto cleanup;
126         status = cr_parser_parse_declaration (parser, &property,
127                                               &value, &important);
128         if (status != CR_OK || !property)
129                 goto cleanup;
131         result = cr_declaration_new (a_statement, property, value);
132         if (result) {
133                 property = NULL;
134                 value = NULL;
135                 result->important = important;
136         }
138       cleanup:
140         if (parser) {
141                 cr_parser_destroy (parser);
142                 parser = NULL;
143         }
145         if (property) {
146                 cr_string_destroy (property);
147                 property = NULL;
148         }
150         if (value) {
151                 cr_term_destroy (value);
152                 value = NULL;
153         }
155         return result;
158 /**
159  *Parses a ';' separated list of properties declaration.
160  *@param a_str the input buffer that contains the list of declaration to
161  *parse.
162  *@param a_enc the encoding of a_str
163  *@return the parsed list of declaration, NULL if parsing failed.
164  */
165 CRDeclaration *
166 cr_declaration_parse_list_from_buf (const guchar * a_str,
167                                     enum CREncoding a_enc)
170         enum CRStatus status = CR_OK;
171         CRTerm *value = NULL;
172         CRString *property = NULL;
173         CRDeclaration *result = NULL,
174                 *cur_decl = NULL;
175         CRTknzr *tokenizer = NULL;
176         gboolean important = FALSE;
178         g_return_val_if_fail (a_str, NULL);
180         CRParser *parser = (CRParser *)cr_parser_new_from_buf
181                       ((guchar*)a_str, strlen ((char *)a_str), a_enc, FALSE);
182         g_return_val_if_fail (parser, NULL);
183         status = cr_parser_get_tknzr (parser, &tokenizer);
184         if (status != CR_OK || !tokenizer) {
185                 if (status == CR_OK)
186                         status = CR_ERROR;
187                 goto cleanup;
188         }
189         status = cr_parser_try_to_skip_spaces_and_comments (parser);
190         if (status != CR_OK)
191                 goto cleanup;
193         status = cr_parser_parse_declaration (parser, &property,
194                                               &value, &important);
195         if (status != CR_OK || !property) {
196                 if (status != CR_OK)
197                         status = CR_ERROR;
198                 goto cleanup;
199         }
200         result = cr_declaration_new (NULL, property, value);
201         if (result) {
202                 property = NULL;
203                 value = NULL;
204                 result->important = important;
205         }
206         /*now, go parse the other declarations */
207         for (;;) {
208                 guint32 c = 0;
210                 cr_parser_try_to_skip_spaces_and_comments (parser);
211                 status = cr_tknzr_peek_char (tokenizer, &c);
212                 if (status != CR_OK) {
213                         if (status == CR_END_OF_INPUT_ERROR)
214                                 status = CR_OK;
215                         goto cleanup;
216                 }
217                 if (c == ';') {
218                         status = cr_tknzr_read_char (tokenizer, &c);
219                 } else {
220                         break;
221                 }
222                 important = FALSE;
223                 cr_parser_try_to_skip_spaces_and_comments (parser);
224                 status = cr_parser_parse_declaration (parser, &property,
225                                                       &value, &important);
226                 if (status != CR_OK || !property) {
227                         if (status == CR_END_OF_INPUT_ERROR) {
228                                 status = CR_OK;
229                         }
230                         break;
231                 }
232                 cur_decl = cr_declaration_new (NULL, property, value);
233                 if (cur_decl) {
234                         cur_decl->important = important;
235                         result = cr_declaration_append (result, cur_decl);
236                         property = NULL;
237                         value = NULL;
238                         cur_decl = NULL;
239                 } else {
240                         break;
241                 }
242         }
244       cleanup:
246         if (parser) {
247                 cr_parser_destroy (parser);
248                 parser = NULL;
249         }
251         if (property) {
252                 cr_string_destroy (property);
253                 property = NULL;
254         }
256         if (value) {
257                 cr_term_destroy (value);
258                 value = NULL;
259         }
261         if (status != CR_OK && result) {
262                 cr_declaration_destroy (result);
263                 result = NULL;
264         }
265         return result;
268 /**
269  *Appends a new declaration to the current declarations list.
270  *@param a_this the current declaration list.
271  *@param a_new the declaration to append.
272  *@return the declaration list with a_new appended to it, or NULL
273  *in case of error.
274  */
275 CRDeclaration *
276 cr_declaration_append (CRDeclaration * a_this, CRDeclaration * a_new)
278         CRDeclaration *cur = NULL;
280         g_return_val_if_fail (a_new, NULL);
282         if (!a_this)
283                 return a_new;
285         for (cur = a_this; cur && cur->next; cur = cur->next) ;
287         cur->next = a_new;
288         a_new->prev = cur;
290         return a_this;
293 /**
294  *Unlinks the declaration from the declaration list.
295  *@param a_decl the declaration to unlink.
296  *@return a pointer to the unlinked declaration in
297  *case of a successfull completion, NULL otherwise.
298  */
299 CRDeclaration *
300 cr_declaration_unlink (CRDeclaration * a_decl)
302         CRDeclaration *result = a_decl;
304         g_return_val_if_fail (result, NULL);
306         /*
307          *some sanity checks first
308          */
309         if (a_decl->prev) {
310                 g_return_val_if_fail (a_decl->prev->next == a_decl, NULL);
312         }
313         if (a_decl->next) {
314                 g_return_val_if_fail (a_decl->next->prev == a_decl, NULL);
315         }
317         /*
318          *now, the real unlinking job.
319          */
320         if (a_decl->prev) {
321                 a_decl->prev->next = a_decl->next;
322         }
323         if (a_decl->next) {
324                 a_decl->next->prev = a_decl->prev;
325         }
326         if (a_decl->parent_statement) {
327                 CRDeclaration **children_decl_ptr = NULL;
329                 switch (a_decl->parent_statement->type) {
330                 case RULESET_STMT:
331                         if (a_decl->parent_statement->kind.ruleset) {
332                                 children_decl_ptr =
333                                         &a_decl->parent_statement->
334                                         kind.ruleset->decl_list;
335                         }
337                         break;
339                 case AT_FONT_FACE_RULE_STMT:
340                         if (a_decl->parent_statement->kind.font_face_rule) {
341                                 children_decl_ptr =
342                                         &a_decl->parent_statement->
343                                         kind.font_face_rule->decl_list;
344                         }
345                         break;
346                 case AT_PAGE_RULE_STMT:
347                         if (a_decl->parent_statement->kind.page_rule) {
348                                 children_decl_ptr =
349                                         &a_decl->parent_statement->
350                                         kind.page_rule->decl_list;
351                         }
353                 default:
354                         break;
355                 }
356                 if (children_decl_ptr
357                     && *children_decl_ptr && *children_decl_ptr == a_decl)
358                         *children_decl_ptr = (*children_decl_ptr)->next;
359         }
361         a_decl->next = NULL;
362         a_decl->prev = NULL;
363         a_decl->parent_statement = NULL;
365         return result;
368 /**
369  *prepends a declaration to the current declaration list.
370  *@param a_this the current declaration list.
371  *@param a_new the declaration to prepend.
372  *@return the list with a_new prepended or NULL in case of error.
373  */
374 CRDeclaration *
375 cr_declaration_prepend (CRDeclaration * a_this, CRDeclaration * a_new)
377         CRDeclaration *cur = NULL;
379         g_return_val_if_fail (a_new, NULL);
381         if (!a_this)
382                 return a_new;
384         a_this->prev = a_new;
385         a_new->next = a_this;
387         for (cur = a_new; cur && cur->prev; cur = cur->prev) ;
389         return cur;
392 /**
393  *Appends a declaration to the current declaration list.
394  *@param a_this the current declaration list.
395  *@param a_prop the property string of the declaration to append.
396  *@param a_value the value of the declaration to append.
397  *@return the list with the new property appended to it, or NULL in
398  *case of an error.
399  */
400 CRDeclaration *
401 cr_declaration_append2 (CRDeclaration * a_this,
402                         CRString * a_prop, CRTerm * a_value)
404         CRDeclaration *new_elem = NULL;
406         if (a_this) {
407                 new_elem = cr_declaration_new (a_this->parent_statement,
408                                                a_prop, a_value);
409         } else {
410                 new_elem = cr_declaration_new (NULL, a_prop, a_value);
411         }
413         g_return_val_if_fail (new_elem, NULL);
415         return cr_declaration_append (a_this, new_elem);
418 /**
419  *Dumps a declaration list to a file.
420  *@param a_this the current instance of #CRDeclaration.
421  *@param a_fp the destination file.
422  *@param a_indent the number of indentation white char.
423  */
424 void
425 cr_declaration_dump (CRDeclaration * a_this, FILE * a_fp, glong a_indent,
426                      gboolean a_one_per_line)
428         CRDeclaration *cur = NULL;
430         g_return_if_fail (a_this);
432         for (cur = a_this; cur; cur = cur->next) {
433                 if (cur->prev) {
434                         if (a_one_per_line == TRUE)
435                                 fprintf (a_fp, ";\n");
436                         else
437                                 fprintf (a_fp, "; ");
438                 }
439                 dump (cur, a_fp, a_indent);
440         }
443 /**
444  *Dumps the first declaration of the declaration list to a file.
445  *@param a_this the current instance of #CRDeclaration.
446  *@param a_fp the destination file.
447  *@param a_indent the number of indentation white char.
448  */
449 void
450 cr_declaration_dump_one (CRDeclaration * a_this, FILE * a_fp, glong a_indent)
452         g_return_if_fail (a_this);
454         dump (a_this, a_fp, a_indent);
457 /**
458  *Serializes the declaration into a string
459  *@param a_this the current instance of #CRDeclaration.
460  *@param a_indent the number of indentation white char
461  *to put before the actual serialisation.
462  */
463 gchar *
464 cr_declaration_to_string (CRDeclaration * a_this, gulong a_indent)
466         GString *stringue = NULL;
468         gchar *str = NULL,
469                 *result = NULL;
471         g_return_val_if_fail (a_this, NULL);
473         stringue = g_string_new (NULL);
475         if (a_this->property 
476             && a_this->property->stryng
477             && a_this->property->stryng->str) {
478                 str = g_strndup (a_this->property->stryng->str,
479                                  a_this->property->stryng->len);
480                 if (str) {
481                         cr_utils_dump_n_chars2 (' ', stringue, 
482                                                 a_indent);
483                         g_string_append (stringue, str);
484                         g_free (str);
485                         str = NULL;
486                 } else
487                         goto error;
489                 if (a_this->value) {
490                         guchar *value_str = NULL;
492                         value_str = cr_term_to_string (a_this->value);
493                         if (value_str) {
494                                 g_string_append_printf (stringue, " : %s",
495                                                         value_str);
496                                 g_free (value_str);
497                         } else
498                                 goto error;
499                 }
500                 if (a_this->important == TRUE) {
501                         g_string_append_printf (stringue, " %s",
502                                                 "!important");
503                 }
504         }
505         if (stringue && stringue->str) {
506                 result = stringue->str;
507                 g_string_free (stringue, FALSE);
508         }
509         return result;
511       error:
512         if (stringue) {
513                 g_string_free (stringue, TRUE);
514                 stringue = NULL;
515         }
516         if (str) {
517                 g_free (str);
518                 str = NULL;
519         }
521         return result;
524 /**
525  *Serializes the declaration list into a string
526  *@param a_this the current instance of #CRDeclaration.
527  *@param a_indent the number of indentation white char
528  *to put before the actual serialisation.
529  */
530 guchar *
531 cr_declaration_list_to_string (CRDeclaration * a_this, gulong a_indent)
533         CRDeclaration *cur = NULL;
534         GString *stringue = NULL;
535         gchar *str = NULL,
536                 *result = NULL;
538         g_return_val_if_fail (a_this, NULL);
540         stringue = g_string_new (NULL);
542         for (cur = a_this; cur; cur = cur->next) {
543                 str = cr_declaration_to_string (cur, a_indent);
544                 if (str) {
545                         g_string_append_printf (stringue, "%s;", str);
546                         g_free (str);
547                 } else
548                         break;
549         }
550         if (stringue && stringue->str) {
551                 result = stringue->str;
552                 g_string_free (stringue, FALSE);
553         }
555         return (guchar *)result;
558 /**
559  *Serializes the declaration list into a string
560  *@param a_this the current instance of #CRDeclaration.
561  *@param a_indent the number of indentation white char
562  *to put before the actual serialisation.
563  */
564 guchar *
565 cr_declaration_list_to_string2 (CRDeclaration * a_this,
566                                 gulong a_indent, gboolean a_one_decl_per_line)
568         CRDeclaration *cur = NULL;
569         GString *stringue = NULL;
570         gchar *str = NULL,
571                 *result = NULL;
573         g_return_val_if_fail (a_this, NULL);
575         stringue = g_string_new (NULL);
577         for (cur = a_this; cur; cur = cur->next) {
578                 str = cr_declaration_to_string (cur, a_indent);
579                 if (str) {
580                         if (a_one_decl_per_line == TRUE) {
581                                 if (cur->next)
582                                         g_string_append_printf (stringue,
583                                                                 "%s;\n", str);
584                                 else
585                                         g_string_append (stringue,
586                                                          str);
587                         } else {
588                                 if (cur->next)
589                                         g_string_append_printf (stringue,
590                                                                 "%s;", str);
591                                 else
592                                         g_string_append (stringue,
593                                                          str);
594                         }
595                         g_free (str);
596                 } else
597                         break;
598         }
599         if (stringue && stringue->str) {
600                 result = stringue->str;
601                 g_string_free (stringue, FALSE);
602         }
604         return (guchar *)result;
607 /**
608  *Return the number of properties in the declaration;
609  *@param a_this the current instance of #CRDeclaration.
610  *@return number of properties in the declaration list.
611  */
612 gint
613 cr_declaration_nr_props (CRDeclaration * a_this)
615         CRDeclaration *cur = NULL;
616         int nr = 0;
618         g_return_val_if_fail (a_this, -1);
620         for (cur = a_this; cur; cur = cur->next)
621                 nr++;
622         return nr;
625 /**
626  *Use an index to get a CRDeclaration from the declaration list.
627  *@param a_this the current instance of #CRDeclaration.
628  *@param itemnr the index into the declaration list.
629  *@return CRDeclaration at position itemnr, if itemnr > number of declarations - 1,
630  *it will return NULL.
631  */
632 CRDeclaration *
633 cr_declaration_get_from_list (CRDeclaration * a_this, int itemnr)
635         CRDeclaration *cur = NULL;
636         int nr = 0;
638         g_return_val_if_fail (a_this, NULL);
640         for (cur = a_this; cur; cur = cur->next)
641                 if (nr++ == itemnr)
642                         return cur;
643         return NULL;
646 /**
647  *Use property name to get a CRDeclaration from the declaration list.
648  *@param a_this the current instance of #CRDeclaration.
649  *@param a_prop the property name to search for.
650  *@return CRDeclaration with property name a_prop, or NULL if not found.
651  */
652 CRDeclaration *
653 cr_declaration_get_by_prop_name (CRDeclaration * a_this,
654                                  const guchar * a_prop)
656         CRDeclaration *cur = NULL;
658         g_return_val_if_fail (a_this, NULL);
659         g_return_val_if_fail (a_prop, NULL);
661         for (cur = a_this; cur; cur = cur->next) {
662                 if (cur->property 
663                     && cur->property->stryng
664                     && cur->property->stryng->str) {
665                         if (!strcmp (cur->property->stryng->str, 
666                                      (char *)a_prop)) {
667                                 return cur;
668                         }
669                 }
670         }
671         return NULL;
674 /**
675  *Increases the ref count of the current instance of #CRDeclaration.
676  *@param a_this the current instance of #CRDeclaration.
677  */
678 void
679 cr_declaration_ref (CRDeclaration * a_this)
681         g_return_if_fail (a_this);
683         a_this->ref_count++;
686 /**
687  *Decrements the ref count of the current instance of #CRDeclaration.
688  *If the ref count reaches zero, the current instance of #CRDeclaration
689  *if destroyed.
690  *@param a_this the current instance of #CRDeclaration.
691  *@return TRUE if the current instance of #CRDeclaration has been destroyed
692  *(ref count reached zero), FALSE otherwise.
693  */
694 gboolean
695 cr_declaration_unref (CRDeclaration * a_this)
697         g_return_val_if_fail (a_this, FALSE);
699         if (a_this->ref_count) {
700                 a_this->ref_count--;
701         }
703         if (a_this->ref_count == 0) {
704                 cr_declaration_destroy (a_this);
705                 return TRUE;
706         }
707         return FALSE;
710 /**
711  *Destructor of the declaration list.
712  *@param a_this the current instance of #CRDeclaration.
713  */
714 void
715 cr_declaration_destroy (CRDeclaration * a_this)
717         CRDeclaration *cur = NULL;
719         g_return_if_fail (a_this);
721         /*
722          *Go get the tail of the list.
723          *Meanwhile, free each property/value pair contained in the list.
724          */
725         for (cur = a_this; cur && cur->next; cur = cur->next) {
726                 if (cur->property) {
727                         cr_string_destroy (cur->property);
728                         cur->property = NULL;
729                 }
731                 if (cur->value) {
732                         cr_term_destroy (cur->value);
733                         cur->value = NULL;
734                 }
735         }
737         if (cur) {
738                 if (cur->property) {
739                         cr_string_destroy (cur->property);
740                         cur->property = NULL;
741                 }
743                 if (cur->value) {
744                         cr_term_destroy (cur->value);
745                         cur->value = NULL;
746                 }
747         }
749         /*in case the list contains only one element */
750         if (cur && !cur->prev) {
751                 g_free (cur);
752                 return;
753         }
755         /*walk backward the list and free each "next" element */
756         for (cur = cur->prev; cur && cur->prev; cur = cur->prev) {
757                 if (cur->next) {
758                         g_free (cur->next);
759                         cur->next = NULL;
760                 }
761         }
763         if (!cur)
764                 return;
766         if (cur->next) {
767                 g_free (cur->next);
768                 cur->next = NULL;
769         }
771         g_free (cur);