[libgxps/wip/nacho/res-sep: 1/2] Add support for resource dictionaries



commit 4b11ce2e6e7fb2775acc709fc4a51ad241667f82
Author: Ignacio Casal Quinteiro <ignacio casal nice-software com>
Date:   Thu Jan 26 12:57:47 2017 +0100

    Add support for resource dictionaries
    
    It parses the resources and keeps a cache in the form of xml.
    Once we are parsing the real xml if we find a resource we
    get the cached xml resource and we parse it to properly handle it.
    
    For now only Path Data resources are supported.
    
    Based in a patch from Jason Crain.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=777731

 libgxps/Makefile.am         |    2 +-
 libgxps/Makefile.sources    |    2 +
 libgxps/gxps-page-private.h |    3 +
 libgxps/gxps-page.c         |  106 ++++++++++-
 libgxps/gxps-resources.c    |  445 +++++++++++++++++++++++++++++++++++++++++++
 libgxps/gxps-resources.h    |   47 +++++
 6 files changed, 603 insertions(+), 2 deletions(-)
---
diff --git a/libgxps/Makefile.am b/libgxps/Makefile.am
index bdff76a..f2f16ab 100644
--- a/libgxps/Makefile.am
+++ b/libgxps/Makefile.am
@@ -55,7 +55,7 @@ INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) --warn-all --identifie
 INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir)
 
 if HAVE_INTROSPECTION
-introspection_sources = $(filter-out $(GXPS_BASE_NOINST_H_FILES) gxps-archive.c gxps-fonts.c gxps-images.c 
gxps-parse-utils.c gxps-version.h, $(libgxps_la_SOURCES))
+introspection_sources = $(filter-out $(GXPS_BASE_NOINST_H_FILES) gxps-resources.c gxps-archive.c 
gxps-fonts.c gxps-images.c gxps-parse-utils.c gxps-version.h, $(libgxps_la_SOURCES))
 
 GXPS-0.1.gir: libgxps.la
 GXPS_0_1_gir_INCLUDES = GObject-2.0 Gio-2.0 cairo-1.0
diff --git a/libgxps/Makefile.sources b/libgxps/Makefile.sources
index 9d6a24a..c9adc55 100644
--- a/libgxps/Makefile.sources
+++ b/libgxps/Makefile.sources
@@ -13,6 +13,7 @@ GXPS_BASE_NOINST_H_FILES = \
        gxps-parse-utils.h      \
        gxps-path.h             \
        gxps-private.h          \
+       gxps-resources.h        \
        $(NULL)
 
 GXPS_BASE_INST_H_FILES = \
@@ -45,4 +46,5 @@ GXPS_BASE_SOURCES = \
        gxps-page.c                     \
        gxps-parse-utils.c              \
        gxps-path.c                     \
+       gxps-resources.c                \
        $(NULL)
diff --git a/libgxps/gxps-page-private.h b/libgxps/gxps-page-private.h
index 9dab52c..cb5018f 100644
--- a/libgxps/gxps-page-private.h
+++ b/libgxps/gxps-page-private.h
@@ -26,6 +26,7 @@
 #include "gxps-page.h"
 #include "gxps-archive.h"
 #include "gxps-images.h"
+#include "gxps-resources.h"
 
 G_BEGIN_DECLS
 
@@ -50,6 +51,8 @@ struct _GXPSPagePrivate {
         /* Anchors */
         gboolean     has_anchors;
         GHashTable  *anchors;
+
+       GXPSResources *resources;
 };
 
 struct _GXPSRenderContext {
diff --git a/libgxps/gxps-page.c b/libgxps/gxps-page.c
index 8baa10b..914e255 100644
--- a/libgxps/gxps-page.c
+++ b/libgxps/gxps-page.c
@@ -276,6 +276,7 @@ typedef struct {
 
        gdouble            opacity;
        cairo_pattern_t   *opacity_mask;
+       gboolean           pop_resource;
 } GXPSCanvas;
 
 static GXPSCanvas *
@@ -288,6 +289,7 @@ gxps_canvas_new (GXPSRenderContext *ctx)
 
        /* Default values */
        canvas->opacity = 1.0;
+       canvas->pop_resource = FALSE;
 
        return canvas;
 }
@@ -322,6 +324,18 @@ canvas_start_element (GMarkupParseContext  *context,
 
                brush = gxps_brush_new (canvas->ctx);
                gxps_brush_parser_push (context, brush);
+       } else if (strcmp (element_name, "Canvas.Resources") == 0) {
+               if (canvas->pop_resource) {
+                       gxps_parse_error (context,
+                                         canvas->ctx->page->priv->source,
+                                         G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                                         element_name, NULL, NULL, error);
+                       return;
+               }
+
+               gxps_resources_push_dict (canvas->ctx->page->priv->resources);
+               canvas->pop_resource = TRUE;
+               gxps_resources_parser_push (context, canvas->ctx->page->priv->resources);
        } else {
                render_start_element (context,
                                      element_name,
@@ -359,6 +373,8 @@ canvas_end_element (GMarkupParseContext  *context,
                        cairo_push_group (canvas->ctx->cr);
                }
                gxps_brush_free (brush);
+       } else if (strcmp (element_name, "Canvas.Resources") == 0) {
+               g_markup_parse_context_pop (context);
        } else {
                render_end_element (context,
                                    element_name,
@@ -385,6 +401,69 @@ static GMarkupParser canvas_parser = {
 };
 
 static void
+resource_start_element (GMarkupParseContext  *context,
+                        const gchar          *element_name,
+                        const gchar         **names,
+                        const gchar         **values,
+                        gpointer              user_data,
+                        GError              **error)
+{
+       if (strcmp (element_name, "Resource.PathGeometry") == 0) {
+               GXPSPath *path = (GXPSPath *)user_data;
+
+               gxps_path_parser_push (context, path);
+       }
+}
+
+static void
+resource_end_element (GMarkupParseContext  *context,
+                     const gchar          *element_name,
+                     gpointer              user_data,
+                     GError              **error)
+{
+       if (strcmp (element_name, "Resource.PathGeometry") == 0) {
+               g_markup_parse_context_pop (context);
+       }
+}
+
+static GMarkupParser resource_parser = {
+       resource_start_element,
+       resource_end_element,
+       NULL,
+       NULL,
+       NULL
+};
+
+static gboolean
+expand_resource (GXPSPage    *page,
+                 const gchar *data,
+                 gpointer     user_data)
+{
+       gchar *resource_key = NULL;
+       const gchar *resource;
+       GMarkupParseContext *context;
+       gboolean ret = TRUE;
+
+       if (sscanf (data, "{StaticResource %m[^}]}", &resource_key) != 1)
+               return FALSE;
+
+       if (!resource_key || *resource_key == '\0')
+               return FALSE;
+
+       resource = gxps_resources_get_resource (page->priv->resources, resource_key);
+       if (!resource)
+               return FALSE;
+
+       context = g_markup_parse_context_new (&resource_parser, 0, user_data, NULL);
+
+       ret = g_markup_parse_context_parse (context, resource, strlen (resource), NULL) &&
+             g_markup_parse_context_end_parse (context, NULL);
+       g_markup_parse_context_free (context);
+
+       return ret;
+}
+
+static void
 render_start_element (GMarkupParseContext  *context,
                      const gchar          *element_name,
                      const gchar         **names,
@@ -405,7 +484,9 @@ render_start_element (GMarkupParseContext  *context,
 
                for (i = 0; names[i] != NULL; i++) {
                        if (strcmp (names[i], "Data") == 0) {
-                               path->data = g_strdup (values[i]);
+                               if (!expand_resource (ctx->page, values[i], path)) {
+                                       path->data = g_strdup (values[i]);
+                               }
                        } else if (strcmp (names[i], "RenderTransform") == 0) {
                                cairo_matrix_t matrix;
 
@@ -713,6 +794,8 @@ render_start_element (GMarkupParseContext  *context,
                if (canvas->opacity != 1.0)
                        cairo_push_group (canvas->ctx->cr);
                g_markup_parse_context_push (context, &canvas_parser, canvas);
+       } else if (strcmp (element_name, "FixedPage.Resources") == 0) {
+               gxps_resources_parser_push (context, ctx->page->priv->resources);
        } else if (strcmp (element_name, "FixedPage") == 0) {
                /* Do Nothing */
        } else {
@@ -956,7 +1039,11 @@ render_end_element (GMarkupParseContext  *context,
                }
                cairo_restore (ctx->cr);
                GXPS_DEBUG (g_message ("restore"));
+               if (canvas->pop_resource)
+                       gxps_resources_pop_dict (ctx->page->priv->resources);
                gxps_canvas_free (canvas);
+       } else if (strcmp (element_name, "FixedPage.Resources") == 0) {
+               g_markup_parse_context_pop (context);
        } else if (strcmp (element_name, "FixedPage") == 0) {
                /* Do Nothing */
        } else {
@@ -1526,10 +1613,26 @@ gxps_page_finalize (GObject *object)
        g_clear_pointer (&page->priv->anchors, g_hash_table_destroy);
        page->priv->has_anchors = FALSE;
 
+       if (page->priv->resources) {
+               g_object_unref (page->priv->resources);
+               page->priv->resources = NULL;
+       }
+
        G_OBJECT_CLASS (gxps_page_parent_class)->finalize (object);
 }
 
 static void
+gxps_page_constructed (GObject *object)
+{
+       GXPSPage *page = GXPS_PAGE (object);
+
+       page->priv->resources = gxps_resources_new (page->priv->zip,
+                                                   page->priv->source);
+
+       G_OBJECT_CLASS (gxps_page_parent_class)->constructed (object);
+}
+
+static void
 gxps_page_init (GXPSPage *page)
 {
        page->priv = G_TYPE_INSTANCE_GET_PRIVATE (page,
@@ -1566,6 +1669,7 @@ gxps_page_class_init (GXPSPageClass *klass)
 
        object_class->set_property = gxps_page_set_property;
        object_class->finalize = gxps_page_finalize;
+       object_class->constructed = gxps_page_constructed;
 
        g_object_class_install_property (object_class,
                                         PROP_ARCHIVE,
diff --git a/libgxps/gxps-resources.c b/libgxps/gxps-resources.c
new file mode 100644
index 0000000..640ca2d
--- /dev/null
+++ b/libgxps/gxps-resources.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2015  Jason Crain <jason aquaticape us>
+ * Copyright (C) 2017  Ignacio Casal Quinteiro <icq gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "gxps-resources.h"
+#include "gxps-parse-utils.h"
+#include "gxps-error.h"
+
+#include <string.h>
+
+struct _GXPSResources
+{
+       GObject parent_instance;
+
+       GXPSArchive *zip;
+       gchar *source;
+
+       gboolean remote;
+       GQueue *queue;
+};
+
+enum {
+       PROP_0,
+       PROP_ARCHIVE,
+       PROP_SOURCE,
+       LAST_PROP
+};
+
+static GParamSpec *props[LAST_PROP];
+
+G_DEFINE_TYPE (GXPSResources, gxps_resources, G_TYPE_OBJECT)
+
+static void
+gxps_resources_finalize (GObject *object)
+{
+       GXPSResources *resources = GXPS_RESOURCES (object);
+
+       g_queue_free_full (resources->queue, (GDestroyNotify)g_hash_table_destroy);
+       g_object_unref (resources->zip);
+       g_free (resources->source);
+
+       G_OBJECT_CLASS (gxps_resources_parent_class)->finalize (object);
+}
+
+static void
+gxps_resources_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+       GXPSResources *resources = GXPS_RESOURCES (object);
+
+       switch (prop_id) {
+       case PROP_ARCHIVE:
+               resources->zip = g_value_dup_object (value);
+               break;
+       case PROP_SOURCE:
+               resources->source = g_value_dup_string (value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+gxps_resources_class_init (GXPSResourcesClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->finalize = gxps_resources_finalize;
+       object_class->set_property = gxps_resources_set_property;
+
+       props[PROP_ARCHIVE] =
+               g_param_spec_object ("archive",
+                                    "Archive",
+                                    "The document archive",
+                                    GXPS_TYPE_ARCHIVE,
+                                    G_PARAM_WRITABLE |
+                                    G_PARAM_CONSTRUCT_ONLY);
+
+       props[PROP_SOURCE] =
+                g_param_spec_string ("source",
+                                     "Source",
+                                     "The Page Source File",
+                                     NULL,
+                                     G_PARAM_WRITABLE |
+                                     G_PARAM_CONSTRUCT_ONLY);
+
+       g_object_class_install_properties (object_class, LAST_PROP, props);
+}
+
+static void
+gxps_resources_init (GXPSResources *resources)
+{
+       resources->queue = g_queue_new ();
+}
+
+GXPSResources *
+gxps_resources_new (GXPSArchive *zip,
+                    const gchar *source)
+{
+       return g_object_new (GXPS_TYPE_RESOURCES,
+                            "archive", zip,
+                            "source", source,
+                            NULL);
+}
+
+void
+gxps_resources_push_dict (GXPSResources *resources)
+{
+       GHashTable *ht;
+
+       g_return_if_fail (GXPS_IS_RESOURCES (resources));
+
+       ht = g_hash_table_new_full (g_str_hash,
+                                   g_str_equal,
+                                   (GDestroyNotify)g_free,
+                                   (GDestroyNotify)g_free);
+       g_queue_push_head (resources->queue, ht);
+}
+
+void
+gxps_resources_pop_dict (GXPSResources *resources)
+{
+       GHashTable *ht;
+
+       g_return_if_fail (GXPS_IS_RESOURCES (resources));
+
+       ht = g_queue_pop_head (resources->queue);
+       g_hash_table_destroy (ht);
+}
+
+const gchar *
+gxps_resources_get_resource (GXPSResources *resources,
+                             const gchar   *key)
+{
+       GList *node;
+
+       g_return_val_if_fail (GXPS_IS_RESOURCES (resources), NULL);
+
+       for (node = resources->queue->head; node != NULL; node = node->next) {
+               GHashTable *ht;
+               gpointer data;
+
+               ht = node->data;
+               data = g_hash_table_lookup (ht, key);
+               if (data)
+                       return data;
+       }
+
+       return NULL;
+}
+
+static gboolean
+gxps_resources_set (GXPSResources *resources,
+                    gchar         *key,
+                    gchar         *value)
+{
+       GHashTable *ht;
+
+       if (g_queue_get_length (resources->queue) == 0)
+               gxps_resources_push_dict (resources);
+
+       ht = g_queue_peek_head (resources->queue);
+       if (g_hash_table_contains (ht, key))
+               return FALSE;
+
+       g_hash_table_insert (ht, key, value);
+
+       return TRUE;
+}
+
+typedef struct {
+       GXPSResources *resources;
+
+       gchar *key;
+       gchar *first_element_name;
+       GString *xml;
+} GXPSResourceDictContext;
+
+static GXPSResourceDictContext *
+gxps_resource_dict_context_new (GXPSResources *resources)
+{
+       GXPSResourceDictContext *resource_dict;
+
+       resource_dict = g_slice_new0 (GXPSResourceDictContext);
+       resource_dict->resources = g_object_ref (resources);
+
+       return resource_dict;
+}
+
+static void
+gxps_resource_dict_context_free (GXPSResourceDictContext *resource_dict)
+{
+       if (G_UNLIKELY (!resource_dict))
+               return;
+
+       g_free (resource_dict->key);
+       g_free (resource_dict->first_element_name);
+       if (resource_dict->xml)
+               g_string_free (resource_dict->xml, TRUE);
+       g_object_unref (resource_dict->resources);
+       g_slice_free (GXPSResourceDictContext, resource_dict);
+}
+
+static void
+resource_dict_start_element (GMarkupParseContext  *context,
+                            const gchar          *element_name,
+                            const gchar         **names,
+                            const gchar         **values,
+                            gpointer              user_data,
+                            GError              **error)
+{
+       GXPSResourceDictContext *resource_dict_ctx = (GXPSResourceDictContext *)user_data;
+       gint i;
+
+       for (i = 0; names[i] != NULL; i++) {
+               if (strcmp (names[i], "x:Key") == 0) {
+                       resource_dict_ctx->key = g_strdup (values[i]);
+                       break;
+               }
+       }
+
+       if (!resource_dict_ctx->key) {
+               gxps_parse_error (context,
+                                 resource_dict_ctx->resources->source,
+                                 G_MARKUP_ERROR_MISSING_ATTRIBUTE,
+                                 element_name, "x:Key",
+                                 NULL, error);
+               return;
+       }
+
+       if (!resource_dict_ctx->xml) {
+               resource_dict_ctx->first_element_name = g_strdup (element_name);
+               resource_dict_ctx->xml = g_string_new (NULL);
+               g_string_append_printf (resource_dict_ctx->xml, "<Resource.%s>\n",
+                                       element_name);
+       }
+
+       g_string_append_printf (resource_dict_ctx->xml, "<%s",
+                               element_name);
+       for (i = 0; names[i] != NULL; i++) {
+               /* Skip key */
+               if (strcmp (names[i], "x:Key") != 0) {
+                       g_string_append_printf (resource_dict_ctx->xml,
+                                               " %s=\"%s\"",
+                                               names[i], values[i]);
+               }
+       }
+
+       g_string_append (resource_dict_ctx->xml, ">\n");
+}
+
+static void
+resource_dict_end_element (GMarkupParseContext  *context,
+                          const gchar          *element_name,
+                          gpointer              user_data,
+                          GError              **error)
+{
+       GXPSResourceDictContext *resource_dict_ctx = (GXPSResourceDictContext *)user_data;
+
+       if (strcmp (element_name, resource_dict_ctx->first_element_name) == 0) {
+               g_string_append_printf (resource_dict_ctx->xml, "</%s>\n</Resource.%s>",
+                                       element_name, element_name);
+               gxps_resources_set (resource_dict_ctx->resources,
+                                   resource_dict_ctx->key,
+                                   g_string_free (resource_dict_ctx->xml, FALSE));
+               g_clear_pointer (&resource_dict_ctx->first_element_name, g_free);
+               resource_dict_ctx->key = NULL;
+               resource_dict_ctx->xml = NULL;
+       } else {
+               g_string_append_printf (resource_dict_ctx->xml, "</%s>\n",
+                                       element_name);
+       }
+}
+
+static void
+resource_dict_error (GMarkupParseContext *context,
+                    GError              *error,
+                    gpointer             user_data)
+{
+       GXPSResourceDictContext *resource_dict_ctx = (GXPSResourceDictContext *)user_data;
+       gxps_resource_dict_context_free (resource_dict_ctx);
+}
+
+static GMarkupParser resource_dict_parser = {
+       resource_dict_start_element,
+       resource_dict_end_element,
+       NULL,
+       NULL,
+       resource_dict_error
+};
+
+static void
+remote_resource_start_element (GMarkupParseContext  *context,
+                              const gchar          *element_name,
+                              const gchar         **names,
+                              const gchar         **values,
+                              gpointer              user_data,
+                              GError              **error)
+{
+       GXPSResources *resources = GXPS_RESOURCES (user_data);
+
+       if (strcmp (element_name, "ResourceDictionary") == 0) {
+               GXPSResourceDictContext *resource_dict_ctx;
+
+               resource_dict_ctx = gxps_resource_dict_context_new (resources);
+               g_markup_parse_context_push (context, &resource_dict_parser, resource_dict_ctx);
+       } else {
+               gxps_parse_error (context,
+                                 resources->source,
+                                 G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                                 element_name, NULL, NULL, error);
+       }
+}
+
+static void
+remote_resource_end_element (GMarkupParseContext  *context,
+                            const gchar          *element_name,
+                            gpointer              user_data,
+                            GError              **error)
+{
+       if (strcmp (element_name, "ResourceDictionary") == 0) {
+               GXPSResourceDictContext *resource_dict;
+
+               resource_dict = g_markup_parse_context_pop (context);
+               gxps_resource_dict_context_free (resource_dict);
+       }
+}
+
+static GMarkupParser remote_resource_parser = {
+       remote_resource_start_element,
+       remote_resource_end_element,
+       NULL,
+       NULL,
+       NULL
+};
+
+static void
+resources_start_element (GMarkupParseContext  *context,
+                        const gchar          *element_name,
+                        const gchar         **names,
+                        const gchar         **values,
+                        gpointer              user_data,
+                        GError              **error)
+{
+       GXPSResources *resources = GXPS_RESOURCES (user_data);
+       const gchar *source = NULL;
+       gint i;
+
+       if (strcmp (element_name, "ResourceDictionary") == 0) {
+               for (i = 0; names[i] != NULL; i++) {
+                       if (strcmp (names[i], "Source") == 0)
+                               source = values[i];
+               }
+
+               resources->remote = source != NULL;
+               if (resources->remote) {
+                       GInputStream *stream;
+                       gchar *abs_source;
+                       GMarkupParseContext *parse_ctx;
+
+                       abs_source = gxps_resolve_relative_path (resources->source,
+                                                                source);
+                       stream = gxps_archive_open (resources->zip, abs_source);
+                       if (!stream) {
+                               g_set_error (error,
+                                            GXPS_ERROR,
+                                            GXPS_ERROR_SOURCE_NOT_FOUND,
+                                            "Source %s not found in archive",
+                                            abs_source);
+                               g_free (abs_source);
+                               return;
+                       }
+
+                       parse_ctx = g_markup_parse_context_new (&remote_resource_parser,
+                                                               0, resources, NULL);
+                       gxps_parse_stream (parse_ctx, stream, error);
+                       g_object_unref (stream);
+                       g_markup_parse_context_free (parse_ctx);
+                       g_free (abs_source);
+               } else {
+                       GXPSResourceDictContext *resource_dict;
+
+                       resource_dict = gxps_resource_dict_context_new (resources);
+                       g_markup_parse_context_push (context, &resource_dict_parser, resource_dict);
+               }
+       } else {
+               gxps_parse_error (context,
+                                 resources->source,
+                                 G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                                 element_name, NULL, NULL, error);
+       }
+}
+
+static void
+resources_end_element (GMarkupParseContext  *context,
+                      const gchar          *element_name,
+                      gpointer              user_data,
+                      GError              **error)
+{
+       GXPSResources *resources = GXPS_RESOURCES (user_data);
+
+       if (strcmp (element_name, "ResourceDictionary") == 0) {
+               GXPSResourceDictContext *resource_dict;
+
+               if (!resources->remote) {
+                       resource_dict = g_markup_parse_context_pop (context);
+                       gxps_resource_dict_context_free (resource_dict);
+               } else {
+                       resources->remote = FALSE;
+               }
+       }
+}
+
+static GMarkupParser resources_parser = {
+       resources_start_element,
+       resources_end_element,
+       NULL,
+       NULL,
+       NULL
+};
+
+void
+gxps_resources_parser_push (GMarkupParseContext  *context,
+                           GXPSResources        *resources)
+{
+       g_markup_parse_context_push (context, &resources_parser, resources);
+}
diff --git a/libgxps/gxps-resources.h b/libgxps/gxps-resources.h
new file mode 100644
index 0000000..8a75bfb
--- /dev/null
+++ b/libgxps/gxps-resources.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015  Jason Crain <jason aquaticape us>
+ * Copyright (C) 2017  Ignacio Casal Quinteiro <icq gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef GXPS_RESOURCES_H
+#define GXPS_RESOURCES_H
+
+#include <glib-object.h>
+
+#include "gxps-archive.h"
+
+G_BEGIN_DECLS
+
+#define GXPS_TYPE_RESOURCES (gxps_resources_get_type ())
+G_DECLARE_FINAL_TYPE (GXPSResources, gxps_resources, GXPS, RESOURCES, GObject)
+
+GXPSResources *gxps_resources_new          (GXPSArchive         *zip,
+                                            const gchar         *source);
+
+void           gxps_resources_push_dict    (GXPSResources       *resources);
+
+void           gxps_resources_pop_dict     (GXPSResources       *resources);
+
+const gchar   *gxps_resources_get_resource (GXPSResources       *resources,
+                                            const gchar         *key);
+
+void           gxps_resources_parser_push  (GMarkupParseContext *context,
+                                            GXPSResources       *resources);
+
+G_END_DECLS
+
+#endif /* GXPS_RESOURCES_H */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]