diff options
Diffstat (limited to 'librsvg/rsvg-handle.c')
-rw-r--r-- | librsvg/rsvg-handle.c | 449 |
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 |