[libgxps] Add support for resource dictionaries
- From: Ignacio Casal Quinteiro <icq src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgxps] Add support for resource dictionaries
- Date: Sat, 10 Jun 2017 08:43:35 +0000 (UTC)
commit 29ddc509877a684c4dfb692805eb2e420a50f20c
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-archive.c | 16 ++
libgxps/gxps-archive.h | 2 +
libgxps/gxps-page-private.h | 1 +
libgxps/gxps-page.c | 137 +++++++++++-
libgxps/gxps-resources.c | 523 +++++++++++++++++++++++++++++++++++++++++++
libgxps/gxps-resources.h | 50 ++++
8 files changed, 729 insertions(+), 4 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-archive.c b/libgxps/gxps-archive.c
index c3f2d2c..398320a 100644
--- a/libgxps/gxps-archive.c
+++ b/libgxps/gxps-archive.c
@@ -39,6 +39,8 @@ struct _GXPSArchive {
GError *init_error;
GFile *filename;
GHashTable *entries;
+
+ GXPSResources *resources;
};
struct _GXPSArchiveClass {
@@ -182,6 +184,7 @@ gxps_archive_finalize (GObject *object)
g_clear_pointer (&archive->entries, g_hash_table_unref);
g_clear_object (&archive->filename);
g_clear_error (&archive->init_error);
+ g_clear_object (&archive->resources);
G_OBJECT_CLASS (gxps_archive_parent_class)->finalize (object);
}
@@ -318,6 +321,19 @@ gxps_archive_has_entry (GXPSArchive *archive,
return g_hash_table_contains (archive->entries, path);
}
+GXPSResources *
+gxps_archive_get_resources (GXPSArchive *archive)
+{
+ g_return_val_if_fail (GXPS_IS_ARCHIVE (archive), NULL);
+
+ if (archive->resources == NULL)
+ archive->resources = g_object_new (GXPS_TYPE_RESOURCES,
+ "archive", archive,
+ NULL);
+
+ return archive->resources;
+}
+
/* GXPSArchiveInputStream */
typedef struct _GXPSArchiveInputStream {
GInputStream parent;
diff --git a/libgxps/gxps-archive.h b/libgxps/gxps-archive.h
index 4029fa1..a5054e1 100644
--- a/libgxps/gxps-archive.h
+++ b/libgxps/gxps-archive.h
@@ -24,6 +24,7 @@
#include <gio/gio.h>
#include <archive.h>
#include <libgxps/gxps-version.h>
+#include <libgxps/gxps-resources.h>
G_BEGIN_DECLS
@@ -42,6 +43,7 @@ GXPSArchive *gxps_archive_new (GFile *filename,
GError **error);
gboolean gxps_archive_has_entry (GXPSArchive *archive,
const gchar *path);
+GXPSResources *gxps_archive_get_resources (GXPSArchive *archive);
GInputStream *gxps_archive_open (GXPSArchive *archive,
const gchar *path);
gboolean gxps_archive_read_entry (GXPSArchive *archive,
diff --git a/libgxps/gxps-page-private.h b/libgxps/gxps-page-private.h
index 9dab52c..7bcdddb 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
diff --git a/libgxps/gxps-page.c b/libgxps/gxps-page.c
index 8baa10b..40145b8 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_dict;
} GXPSCanvas;
static GXPSCanvas *
@@ -288,6 +289,7 @@ gxps_canvas_new (GXPSRenderContext *ctx)
/* Default values */
canvas->opacity = 1.0;
+ canvas->pop_resource_dict = FALSE;
return canvas;
}
@@ -322,6 +324,22 @@ 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) {
+ GXPSResources *resources;
+
+ if (canvas->pop_resource_dict) {
+ gxps_parse_error (context,
+ canvas->ctx->page->priv->source,
+ G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ element_name, NULL, NULL, error);
+ return;
+ }
+
+ resources = gxps_archive_get_resources (canvas->ctx->page->priv->zip);
+ gxps_resources_push_dict (resources);
+ canvas->pop_resource_dict = TRUE;
+ gxps_resources_parser_push (context, resources,
+ canvas->ctx->page->priv->source);
} else {
render_start_element (context,
element_name,
@@ -359,6 +377,8 @@ canvas_end_element (GMarkupParseContext *context,
cairo_push_group (canvas->ctx->cr);
}
gxps_brush_free (brush);
+ } else if (strcmp (element_name, "Canvas.Resources") == 0) {
+ gxps_resources_parser_pop (context);
} else {
render_end_element (context,
element_name,
@@ -385,6 +405,95 @@ 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, "PathGeometry") == 0) {
+ GXPSPath *path = (GXPSPath *)user_data;
+
+ gxps_path_parser_push (context, path);
+ } else if (g_str_has_suffix (element_name, "Brush")) {
+ GXPSPath *path = (GXPSPath *)user_data;
+ GXPSBrush *brush;
+
+ brush = gxps_brush_new (path->ctx);
+ gxps_brush_parser_push (context, brush);
+ }
+}
+
+static void
+resource_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ if (strcmp (element_name, "PathGeometry") == 0) {
+ g_markup_parse_context_pop (context);
+ } else if (g_str_has_suffix (element_name, "Brush")) {
+ GXPSPath *path = (GXPSPath *)user_data;
+ GXPSBrush *brush = g_markup_parse_context_pop (context);
+
+ path->fill_pattern = cairo_pattern_reference (brush->pattern);
+ gxps_brush_free (brush);
+ }
+}
+
+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;
+ gchar *p;
+ gsize len;
+ GXPSResources *resources;
+ const gchar *resource;
+ GMarkupParseContext *context;
+ gboolean ret = TRUE;
+
+ if (!g_str_has_prefix (data, "{StaticResource "))
+ return FALSE;
+
+ p = strstr (data, "}");
+ if (p == NULL)
+ return FALSE;
+
+ len = strlen ("{StaticResource ");
+ resource_key = g_strndup (data + len, p - (data + len));
+
+ if (!resource_key || *resource_key == '\0') {
+ g_free (resource_key);
+ return FALSE;
+ }
+
+ resources = gxps_archive_get_resources (page->priv->zip);
+ resource = gxps_resources_get_resource (resources, resource_key);
+ g_free (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,
@@ -404,7 +513,15 @@ render_start_element (GMarkupParseContext *context,
path = gxps_path_new (ctx);
for (i = 0; names[i] != NULL; i++) {
- if (strcmp (names[i], "Data") == 0) {
+ /* FIXME: if the resource gets expanded, that specific
+ * resource will be already handled leading to a different
+ * behavior of what we are actually doing without resources.
+ * In an ideal world we would handle the resource without
+ * special casing
+ */
+ if (expand_resource (ctx->page, values[i], path)) {
+ GXPS_DEBUG (g_message ("expanded resource: %s", names[i]));
+ } else if (strcmp (names[i], "Data") == 0) {
path->data = g_strdup (values[i]);
} else if (strcmp (names[i], "RenderTransform") == 0) {
cairo_matrix_t matrix;
@@ -425,8 +542,7 @@ render_start_element (GMarkupParseContext *context,
} else if (strcmp (names[i], "Clip") == 0) {
path->clip_data = g_strdup (values[i]);
} else if (strcmp (names[i], "Fill") == 0) {
- GXPS_DEBUG (g_message ("set_fill_pattern (solid)"));
- if (!gxps_brush_solid_color_parse (values[i], ctx->page->priv->zip, 1.,
&path->fill_pattern)) {
+ if (!gxps_brush_solid_color_parse (values[i], ctx->page->priv->zip, 1.,
&path->fill_pattern)) {
gxps_parse_error (context,
ctx->page->priv->source,
G_MARKUP_ERROR_INVALID_CONTENT,
@@ -434,6 +550,7 @@ render_start_element (GMarkupParseContext *context,
gxps_path_free (path);
return;
}
+ GXPS_DEBUG (g_message ("set_fill_pattern (solid)"));
} else if (strcmp (names[i], "Stroke") == 0) {
GXPS_DEBUG (g_message ("set_stroke_pattern (solid)"));
if (!gxps_brush_solid_color_parse (values[i], ctx->page->priv->zip, 1.,
&path->stroke_pattern)) {
@@ -713,6 +830,12 @@ 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) {
+ GXPSResources *resources;
+
+ resources = gxps_archive_get_resources (ctx->page->priv->zip);
+ gxps_resources_parser_push (context, resources,
+ ctx->page->priv->source);
} else if (strcmp (element_name, "FixedPage") == 0) {
/* Do Nothing */
} else {
@@ -956,7 +1079,15 @@ render_end_element (GMarkupParseContext *context,
}
cairo_restore (ctx->cr);
GXPS_DEBUG (g_message ("restore"));
+ if (canvas->pop_resource_dict) {
+ GXPSResources *resources;
+
+ resources = gxps_archive_get_resources (ctx->page->priv->zip);
+ gxps_resources_pop_dict (resources);
+ }
gxps_canvas_free (canvas);
+ } else if (strcmp (element_name, "FixedPage.Resources") == 0) {
+ gxps_resources_parser_pop (context);
} else if (strcmp (element_name, "FixedPage") == 0) {
/* Do Nothing */
} else {
diff --git a/libgxps/gxps-resources.c b/libgxps/gxps-resources.c
new file mode 100644
index 0000000..efe0398
--- /dev/null
+++ b/libgxps/gxps-resources.c
@@ -0,0 +1,523 @@
+/*
+ * 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>
+
+#define GXPS_RESOURCES_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_RESOURCES,
GXPSResourcesClass))
+#define GXPS_IS_RESOURCES_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_RESOURCES))
+#define GXPS_RESOURCES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_RESOURCES,
GXPSResourcesClass))
+
+struct _GXPSResources
+{
+ GObject parent_instance;
+
+ GXPSArchive *zip;
+
+ GQueue *queue;
+};
+
+struct _GXPSResourcesClass
+{
+ GObjectClass parent;
+};
+
+typedef struct _GXPSResourcesClass GXPSResourcesClass;
+
+enum {
+ PROP_0,
+ PROP_ARCHIVE,
+ 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_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;
+ 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);
+
+ g_object_class_install_properties (object_class, LAST_PROP, props);
+}
+
+static void
+gxps_resources_init (GXPSResources *resources)
+{
+ resources->queue = g_queue_new ();
+}
+
+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)) {
+ g_free (key);
+ g_free (value);
+ return FALSE;
+ }
+
+ g_hash_table_insert (ht, key, value);
+
+ return TRUE;
+}
+
+typedef struct {
+ GXPSResources *resources;
+ gchar *source;
+
+ gchar *key;
+ GString *xml;
+} GXPSResourceDictContext;
+
+static GXPSResourceDictContext *
+gxps_resource_dict_context_new (GXPSResources *resources,
+ const gchar *source)
+{
+ GXPSResourceDictContext *resource_dict;
+
+ resource_dict = g_slice_new0 (GXPSResourceDictContext);
+ resource_dict->resources = g_object_ref (resources);
+ resource_dict->source = g_strdup (source);
+
+ return resource_dict;
+}
+
+static void
+gxps_resource_dict_context_free (GXPSResourceDictContext *resource_dict)
+{
+ if (G_UNLIKELY (!resource_dict))
+ return;
+
+ g_free (resource_dict->key);
+ 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_concat_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;
+
+ g_string_append_printf (resource_dict_ctx->xml, "<%s",
+ element_name);
+ for (i = 0; names[i] != NULL; i++) {
+ 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_concat_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ GXPSResourceDictContext *resource_dict_ctx = (GXPSResourceDictContext *)user_data;
+
+ g_string_append_printf (resource_dict_ctx->xml, "</%s>\n",
+ element_name);
+}
+
+static GMarkupParser resource_concat_parser = {
+ resource_concat_start_element,
+ resource_concat_end_element,
+ NULL,
+ NULL,
+ NULL
+};
+
+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->source,
+ G_MARKUP_ERROR_MISSING_ATTRIBUTE,
+ element_name, "x:Key",
+ NULL, error);
+ return;
+ }
+
+ if (!resource_dict_ctx->xml) {
+ resource_dict_ctx->xml = g_string_new (NULL);
+ g_string_append_printf (resource_dict_ctx->xml, "<%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");
+
+ g_markup_parse_context_push (context, &resource_concat_parser, resource_dict_ctx);
+}
+
+static void
+resource_dict_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ GXPSResourceDictContext *resource_dict_ctx = (GXPSResourceDictContext *)user_data;
+
+ g_string_append_printf (resource_dict_ctx->xml, "</%s>\n</%s>",
+ element_name, element_name);
+ gxps_resources_set (resource_dict_ctx->resources,
+ resource_dict_ctx->key,
+ g_string_free (resource_dict_ctx->xml, FALSE));
+ resource_dict_ctx->key = NULL;
+ resource_dict_ctx->xml = NULL;
+ g_markup_parse_context_pop (context);
+}
+
+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
+};
+
+typedef struct
+{
+ GXPSResources *resources;
+ gchar *source;
+ gboolean remote;
+} GXPSResourceContext;
+
+static GXPSResourceContext *
+gxps_resource_context_new (GXPSResources *resources,
+ const gchar *source)
+{
+ GXPSResourceContext *context;
+
+ context = g_slice_new0 (GXPSResourceContext);
+ context->resources = g_object_ref (resources);
+ context->source = g_strdup (source);
+
+ return context;
+}
+
+static void
+gxps_resource_context_free (GXPSResourceContext *context)
+{
+ g_object_unref (context->resources);
+ g_free (context->source);
+ g_slice_free (GXPSResourceContext, context);
+}
+
+static void
+push_resource_dict_context (GMarkupParseContext *context,
+ GXPSResourceContext *rcontext)
+{
+ GXPSResourceDictContext *resource_dict_ctx;
+
+ resource_dict_ctx = gxps_resource_dict_context_new (rcontext->resources,
+ rcontext->source);
+ g_markup_parse_context_push (context, &resource_dict_parser, resource_dict_ctx);
+}
+
+static void
+pop_resource_dict_context (GMarkupParseContext *context)
+{
+ GXPSResourceDictContext *resource_dict_ctx;
+
+ resource_dict_ctx = g_markup_parse_context_pop (context);
+ gxps_resource_dict_context_free (resource_dict_ctx);
+}
+
+static void
+remote_resource_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **names,
+ const gchar **values,
+ gpointer user_data,
+ GError **error)
+{
+ GXPSResourceContext *rcontext = (GXPSResourceContext *)user_data;
+
+ if (strcmp (element_name, "ResourceDictionary") == 0) {
+ push_resource_dict_context (context, rcontext);
+ } else {
+ gxps_parse_error (context,
+ rcontext->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) {
+ pop_resource_dict_context (context);
+ }
+}
+
+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)
+{
+ GXPSResourceContext *rcontext = (GXPSResourceContext *)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];
+ }
+
+ rcontext->remote = source != NULL;
+ if (rcontext->remote) {
+ GInputStream *stream;
+ gchar *abs_source;
+ GMarkupParseContext *parse_ctx;
+
+ abs_source = gxps_resolve_relative_path (rcontext->source,
+ source);
+ stream = gxps_archive_open (rcontext->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, rcontext, NULL);
+ gxps_parse_stream (parse_ctx, stream, error);
+ g_object_unref (stream);
+ g_markup_parse_context_free (parse_ctx);
+ g_free (abs_source);
+ } else {
+ push_resource_dict_context (context, rcontext);
+ }
+ } else {
+ gxps_parse_error (context,
+ rcontext->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)
+{
+ GXPSResourceContext *rcontext = (GXPSResourceContext *)user_data;
+
+ if (strcmp (element_name, "ResourceDictionary") == 0) {
+ if (!rcontext->remote) {
+ pop_resource_dict_context (context);
+ } else {
+ rcontext->remote = FALSE;
+ }
+ }
+}
+
+static GMarkupParser resources_parser = {
+ resources_start_element,
+ resources_end_element,
+ NULL,
+ NULL,
+ NULL
+};
+
+void
+gxps_resources_parser_push (GMarkupParseContext *context,
+ GXPSResources *resources,
+ const gchar *source)
+{
+ GXPSResourceContext *rcontext;
+
+ rcontext = gxps_resource_context_new (resources, source);
+
+ g_markup_parse_context_push (context, &resources_parser, rcontext);
+}
+
+void
+gxps_resources_parser_pop (GMarkupParseContext *context)
+{
+ GXPSResourceContext *rcontext;
+
+ rcontext = g_markup_parse_context_pop (context);
+ gxps_resource_context_free (rcontext);
+}
diff --git a/libgxps/gxps-resources.h b/libgxps/gxps-resources.h
new file mode 100644
index 0000000..65e6d6d
--- /dev/null
+++ b/libgxps/gxps-resources.h
@@ -0,0 +1,50 @@
+/*
+ * 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>
+
+G_BEGIN_DECLS
+
+#define GXPS_TYPE_RESOURCES (gxps_resources_get_type ())
+#define GXPS_RESOURCES(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_RESOURCES, GXPSResources))
+#define GXPS_IS_RESOURCES(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_RESOURCES))
+
+typedef struct _GXPSResources GXPSResources;
+
+GType gxps_resources_get_type (void) G_GNUC_CONST;
+
+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,
+ const gchar *source);
+
+void gxps_resources_parser_pop (GMarkupParseContext *context);
+
+G_END_DECLS
+
+#endif /* GXPS_RESOURCES_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]