summaryrefslogtreecommitdiff
path: root/librsvg/rsvg-handle.c
diff options
context:
space:
mode:
Diffstat (limited to 'librsvg/rsvg-handle.c')
-rw-r--r--librsvg/rsvg-handle.c449
1 files changed, 448 insertions, 1 deletions
diff --git a/librsvg/rsvg-handle.c b/librsvg/rsvg-handle.c
index f2802ac6..01af7762 100644
--- a/librsvg/rsvg-handle.c
+++ b/librsvg/rsvg-handle.c
@@ -120,6 +120,8 @@
#include "config.h"
#include <string.h>
+#include "rsvg-io.h"
+#include "rsvg-load.h"
#include "rsvg-private.h"
enum {
@@ -425,6 +427,107 @@ rsvg_handle_new (void)
return RSVG_HANDLE (g_object_new (RSVG_TYPE_HANDLE, NULL));
}
+static gboolean
+rsvg_handle_fill_with_data (RsvgHandle *handle,
+ const char *data,
+ gsize data_len,
+ GError ** error)
+{
+ gboolean rv;
+
+ rsvg_return_val_if_fail (data != NULL, FALSE, error);
+ rsvg_return_val_if_fail (data_len != 0, FALSE, error);
+
+ rv = rsvg_handle_write (handle, (guchar *) data, data_len, error);
+
+ return rsvg_handle_close (handle, rv ? error : NULL) && rv;
+}
+
+/**
+ * rsvg_handle_new_from_data:
+ * @data: (array length=data_len): The SVG data
+ * @data_len: The length of @data, in bytes
+ * @error: return location for errors
+ *
+ * Loads the SVG specified by @data.
+ *
+ * Returns: A #RsvgHandle or %NULL if an error occurs.
+ * Since: 2.14
+ */
+RsvgHandle *
+rsvg_handle_new_from_data (const guint8 *data, gsize data_len, GError **error)
+{
+ RsvgHandle *handle;
+
+ handle = rsvg_handle_new ();
+
+ if (handle) {
+ if (!rsvg_handle_fill_with_data (handle, (char *) data, data_len, error)) {
+ g_object_unref (handle);
+ handle = NULL;
+ }
+ }
+
+ return handle;
+}
+
+/**
+ * rsvg_handle_new_from_file:
+ * @file_name: The file name to load. If built with gnome-vfs, can be a URI.
+ * @error: return location for errors
+ *
+ * Loads the SVG specified by @file_name.
+ *
+ * Returns: A #RsvgHandle or %NULL if an error occurs.
+ * Since: 2.14
+ */
+RsvgHandle *
+rsvg_handle_new_from_file (const gchar *file_name, GError **error)
+{
+ gchar *base_uri;
+ char *data;
+ gsize data_len;
+ RsvgHandle *handle = NULL;
+ GFile *file;
+ char *scheme;
+
+ rsvg_return_val_if_fail (file_name != NULL, NULL, error);
+
+ scheme = g_uri_parse_scheme (file_name);
+ if (scheme) {
+ file = g_file_new_for_uri (file_name);
+ g_free (scheme);
+ } else {
+ file = g_file_new_for_path (file_name);
+ }
+
+ base_uri = g_file_get_uri (file);
+ if (!base_uri) {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Cannot obtain URI from '%s'"), file_name);
+ g_object_unref (file);
+ return NULL;
+ }
+
+ data = _rsvg_io_acquire_data (base_uri, base_uri, NULL, &data_len, NULL, error);
+
+ if (data) {
+ handle = rsvg_handle_new ();
+ rsvg_handle_set_base_uri (handle, base_uri);
+ if (!rsvg_handle_fill_with_data (handle, data, data_len, error)) {
+ g_object_unref (handle);
+ handle = NULL;
+ }
+ g_free (data);
+ }
+
+ g_free (base_uri);
+ g_object_unref (file);
+
+ return handle;
+}
/**
* rsvg_handle_new_with_flags:
@@ -532,6 +635,142 @@ rsvg_handle_new_from_stream_sync (GInputStream *input_stream,
return handle;
}
+/**
+ * rsvg_handle_write:
+ * @handle: an #RsvgHandle
+ * @buf: (array length=count) (element-type guchar): pointer to svg data
+ * @count: length of the @buf buffer in bytes
+ * @error: (allow-none): a location to store a #GError, or %NULL
+ *
+ * Loads the next @count bytes of the image. This will return %TRUE if the data
+ * was loaded successful, and %FALSE if an error occurred. In the latter case,
+ * the loader will be closed, and will not accept further writes. If %FALSE is
+ * returned, @error will be set to an error from the #RsvgError domain. Errors
+ * from #GIOErrorEnum are also possible.
+ *
+ * Returns: %TRUE on success, or %FALSE on error
+ **/
+gboolean
+rsvg_handle_write (RsvgHandle *handle, const guchar *buf, gsize count, GError **error)
+{
+ RsvgHandlePrivate *priv;
+
+ rsvg_return_val_if_fail (handle, FALSE, error);
+ priv = handle->priv;
+
+ rsvg_return_val_if_fail (priv->hstate == RSVG_HANDLE_STATE_START
+ || priv->hstate == RSVG_HANDLE_STATE_LOADING,
+ FALSE,
+ error);
+
+ if (priv->hstate == RSVG_HANDLE_STATE_START) {
+ priv->hstate = RSVG_HANDLE_STATE_LOADING;
+ priv->load = rsvg_load_new (handle, (priv->flags & RSVG_HANDLE_FLAG_UNLIMITED) != 0);
+ }
+
+ g_assert (priv->hstate == RSVG_HANDLE_STATE_LOADING);
+
+ return rsvg_load_write (priv->load, buf, count, error);
+}
+
+static gboolean
+finish_load (RsvgHandle *handle, gboolean was_successful)
+{
+ RsvgNode *treebase = rsvg_load_destroy (handle->priv->load);
+ handle->priv->load = NULL;
+
+ if (was_successful) {
+ handle->priv->hstate = RSVG_HANDLE_STATE_CLOSED_OK;
+ handle->priv->treebase = treebase;
+ } else {
+ handle->priv->hstate = RSVG_HANDLE_STATE_CLOSED_ERROR;
+ treebase = rsvg_node_unref (treebase);
+ }
+
+ return was_successful;
+}
+
+/**
+ * rsvg_handle_close:
+ * @handle: a #RsvgHandle
+ * @error: (allow-none): a location to store a #GError, or %NULL
+ *
+ * Closes @handle, to indicate that loading the image is complete. This will
+ * return %TRUE if the loader closed successfully. Note that @handle isn't
+ * freed until @g_object_unref is called.
+ *
+ * Returns: %TRUE on success, or %FALSE on error
+ **/
+gboolean
+rsvg_handle_close (RsvgHandle *handle, GError **error)
+{
+ RsvgHandlePrivate *priv;
+ gboolean result;
+
+ rsvg_return_val_if_fail (handle, FALSE, error);
+ priv = handle->priv;
+
+ if (priv->hstate == RSVG_HANDLE_STATE_CLOSED_OK
+ || priv->hstate == RSVG_HANDLE_STATE_CLOSED_ERROR) {
+ /* closing is idempotent */
+ return TRUE;
+ }
+
+ result = finish_load (handle, rsvg_load_close (priv->load, error));
+
+ return result;
+}
+
+/**
+ * rsvg_handle_read_stream_sync:
+ * @handle: a #RsvgHandle
+ * @stream: a #GInputStream
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ * @error: (allow-none): a location to store a #GError, or %NULL
+ *
+ * Reads @stream and writes the data from it to @handle.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the
+ * operation was cancelled, the error %G_IO_ERROR_CANCELLED will be
+ * returned.
+ *
+ * Returns: %TRUE if reading @stream succeeded, or %FALSE otherwise
+ * with @error filled in
+ *
+ * Since: 2.32
+ */
+gboolean
+rsvg_handle_read_stream_sync (RsvgHandle *handle,
+ GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ RsvgHandlePrivate *priv;
+ gboolean result;
+ RsvgLoad *saved_load;
+
+ g_return_val_if_fail (RSVG_IS_HANDLE (handle), FALSE);
+ g_return_val_if_fail (G_IS_INPUT_STREAM (stream), FALSE);
+ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ priv = handle->priv;
+
+ g_return_val_if_fail (priv->hstate == RSVG_HANDLE_STATE_START, FALSE);
+
+ priv->hstate = RSVG_HANDLE_STATE_LOADING;
+
+ saved_load = priv->load;
+
+ priv->load = rsvg_load_new (handle, (priv->flags & RSVG_HANDLE_FLAG_UNLIMITED) != 0);
+ result = finish_load (handle, rsvg_load_read_stream_sync (priv->load, stream, cancellable, error));
+
+ priv->load = saved_load;
+
+ return result;
+}
+
/* http://www.ietf.org/rfc/rfc2396.txt */
static gboolean
@@ -771,7 +1010,6 @@ rsvg_handle_create_drawing_ctx(RsvgHandle *handle,
handle->priv->dpi_x, handle->priv->dpi_y,
handle->priv->defs,
handle->priv->is_testing);
-
}
/**
@@ -1256,6 +1494,215 @@ rsvg_handle_set_size_callback (RsvgHandle * handle,
handle->priv->user_data_destroy = user_data_destroy;
}
+char *
+rsvg_handle_resolve_uri (RsvgHandle *handle,
+ const char *uri)
+{
+ RsvgHandlePrivate *priv = handle->priv;
+ char *scheme, *resolved_uri;
+ GFile *base, *resolved;
+
+ if (uri == NULL)
+ return NULL;
+
+ scheme = g_uri_parse_scheme (uri);
+ if (scheme != NULL ||
+ priv->base_gfile == NULL ||
+ (base = g_file_get_parent (priv->base_gfile)) == NULL) {
+ g_free (scheme);
+ return g_strdup (uri);
+ }
+
+ resolved = g_file_resolve_relative_path (base, uri);
+ resolved_uri = g_file_get_uri (resolved);
+
+ g_free (scheme);
+ g_object_unref (base);
+ g_object_unref (resolved);
+
+ return resolved_uri;
+}
+
+char *
+_rsvg_handle_acquire_data (RsvgHandle *handle,
+ const char *url,
+ char **content_type,
+ gsize *len,
+ GError **error)
+{
+ RsvgHandlePrivate *priv = handle->priv;
+ char *uri;
+ char *data;
+
+ uri = rsvg_handle_resolve_uri (handle, url);
+
+ if (rsvg_allow_load (priv->base_gfile, uri, error)) {
+ data = _rsvg_io_acquire_data (uri,
+ rsvg_handle_get_base_uri (handle),
+ content_type,
+ len,
+ handle->priv->cancellable,
+ error);
+ } else {
+ data = NULL;
+ }
+
+ g_free (uri);
+ return data;
+}
+
+GInputStream *
+_rsvg_handle_acquire_stream (RsvgHandle *handle,
+ const char *url,
+ char **content_type,
+ GError **error)
+{
+ RsvgHandlePrivate *priv = handle->priv;
+ char *uri;
+ GInputStream *stream;
+
+ uri = rsvg_handle_resolve_uri (handle, url);
+
+ if (rsvg_allow_load (priv->base_gfile, uri, error)) {
+ stream = _rsvg_io_acquire_stream (uri,
+ rsvg_handle_get_base_uri (handle),
+ content_type,
+ handle->priv->cancellable,
+ error);
+ } else {
+ stream = NULL;
+ }
+
+ g_free (uri);
+ return stream;
+}
+
+cairo_surface_t *
+rsvg_cairo_surface_new_from_href (RsvgHandle *handle,
+ const char *href,
+ GError **error)
+{
+ char *data;
+ gsize data_len;
+ char *mime_type = NULL;
+ GdkPixbufLoader *loader = NULL;
+ GdkPixbuf *pixbuf = NULL;
+ cairo_surface_t *surface = NULL;
+
+ data = _rsvg_handle_acquire_data (handle, href, &mime_type, &data_len, error);
+ if (data == NULL)
+ return NULL;
+
+ if (mime_type) {
+ loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, error);
+ } else {
+ loader = gdk_pixbuf_loader_new ();
+ }
+
+ if (loader == NULL)
+ goto out;
+
+ if (!gdk_pixbuf_loader_write (loader, (guchar *) data, data_len, error)) {
+ gdk_pixbuf_loader_close (loader, NULL);
+ goto out;
+ }
+
+ if (!gdk_pixbuf_loader_close (loader, error))
+ goto out;
+
+ pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+
+ if (!pixbuf) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_FAILED,
+ _("Failed to load image '%s': reason not known, probably a corrupt image file"),
+ href);
+ goto out;
+ }
+
+ surface = rsvg_cairo_surface_from_pixbuf (pixbuf);
+
+ if (mime_type == NULL) {
+ /* Try to get the information from the loader */
+ GdkPixbufFormat *format;
+ char **mime_types;
+
+ if ((format = gdk_pixbuf_loader_get_format (loader)) != NULL) {
+ mime_types = gdk_pixbuf_format_get_mime_types (format);
+
+ if (mime_types != NULL)
+ mime_type = g_strdup (mime_types[0]);
+ g_strfreev (mime_types);
+ }
+ }
+
+ if ((handle->priv->flags & RSVG_HANDLE_FLAG_KEEP_IMAGE_DATA) != 0 &&
+ mime_type != NULL &&
+ cairo_surface_set_mime_data (surface, mime_type, (guchar *) data,
+ data_len, g_free, data) == CAIRO_STATUS_SUCCESS) {
+ data = NULL; /* transferred to the surface */
+ }
+
+ out:
+ if (loader)
+ g_object_unref (loader);
+ g_free (mime_type);
+ g_free (data);
+
+ return surface;
+}
+
+#ifdef HAVE_PANGOFT2
+
+static void
+create_font_config_for_testing (RsvgHandle *handle)
+{
+ const char *font_paths[] = {
+ "resources/Roboto-Regular.ttf",
+ "resources/Roboto-Italic.ttf",
+ "resources/Roboto-Bold.ttf",
+ "resources/Roboto-BoldItalic.ttf",
+ };
+
+ int i;
+
+ if (handle->priv->font_config_for_testing != NULL)
+ return;
+
+ handle->priv->font_config_for_testing = FcConfigCreate ();
+
+ for (i = 0; i < G_N_ELEMENTS(font_paths); i++) {
+ char *font_path = g_test_build_filename (G_TEST_DIST, font_paths[i], NULL);
+
+ if (!FcConfigAppFontAddFile (handle->priv->font_config_for_testing, (const FcChar8 *) font_path)) {
+ g_error ("Could not load font file \"%s\" for tests; aborting", font_path);
+ }
+
+ g_free (font_path);
+ }
+}
+
+#endif
+
+static void
+rsvg_handle_update_font_map_for_testing (RsvgHandle *handle)
+{
+#ifdef HAVE_PANGOFT2
+ if (handle->priv->is_testing) {
+ create_font_config_for_testing (handle);
+
+ if (handle->priv->font_map_for_testing == NULL) {
+ handle->priv->font_map_for_testing = pango_cairo_font_map_new_for_font_type (CAIRO_FONT_TYPE_FT);
+ pango_fc_font_map_set_config (PANGO_FC_FONT_MAP (handle->priv->font_map_for_testing),
+ handle->priv->font_config_for_testing);
+
+ pango_cairo_font_map_set_default (PANGO_CAIRO_FONT_MAP (handle->priv->font_map_for_testing));
+ }
+ }
+#endif
+}
+
/**
* _rsvg_handle_internal_set_testing:
* @handle: a #RsvgHandle