[librsvg: 14/15] Add new API rsvg_handle_render_element()



commit 5ce94a5973c184631decb26018e598bf4174d215
Author: Federico Mena Quintero <federico gnome org>
Date:   Tue Jul 30 11:42:55 2019 -0500

    Add new API rsvg_handle_render_element()

 doc/rsvg-sections.txt       |  1 +
 librsvg/rsvg-cairo.h        |  7 ++++++
 librsvg/rsvg-handle.c       | 55 +++++++++++++++++++++++++++++++++++++++++++++
 rsvg_internals/src/c_api.rs | 36 +++++++++++++++++++++++++++++
 rsvg_internals/src/lib.rs   |  1 +
 tests/api.c                 | 40 +++++++++++++++++++++++++++++++--
 6 files changed, 138 insertions(+), 2 deletions(-)
---
diff --git a/doc/rsvg-sections.txt b/doc/rsvg-sections.txt
index 82c4dcc9..4d4fed6c 100644
--- a/doc/rsvg-sections.txt
+++ b/doc/rsvg-sections.txt
@@ -73,6 +73,7 @@ rsvg_handle_render_document
 rsvg_handle_get_geometry_for_layer
 rsvg_handle_render_layer
 rsvg_handle_get_geometry_for_element
+rsvg_handle_render_element
 rsvg_handle_render_cairo
 rsvg_handle_render_cairo_sub
 </SECTION>
diff --git a/librsvg/rsvg-cairo.h b/librsvg/rsvg-cairo.h
index ba6de5e0..5e1a22a3 100644
--- a/librsvg/rsvg-cairo.h
+++ b/librsvg/rsvg-cairo.h
@@ -67,6 +67,13 @@ gboolean rsvg_handle_get_geometry_for_element (RsvgHandle     *handle,
                                                RsvgRectangle  *out_logical_rect,
                                                GError        **error);
 
+RSVG_API
+gboolean rsvg_handle_render_element (RsvgHandle           *handle,
+                                     cairo_t              *cr,
+                                     const char           *id,
+                                     const RsvgRectangle  *element_viewport,
+                                     GError              **error);
+
 G_END_DECLS
 
 #endif
diff --git a/librsvg/rsvg-handle.c b/librsvg/rsvg-handle.c
index b13950c3..3bf47eb3 100644
--- a/librsvg/rsvg-handle.c
+++ b/librsvg/rsvg-handle.c
@@ -402,6 +402,11 @@ extern gboolean rsvg_rust_handle_get_geometry_for_element (RsvgHandle     *handl
                                                            RsvgRectangle  *out_ink_rect,
                                                            RsvgRectangle  *out_logical_rect,
                                                            GError        **error);
+extern gboolean rsvg_rust_handle_render_element (RsvgHandle           *handle,
+                                                 cairo_t              *cr,
+                                                 const char           *id,
+                                                 const RsvgRectangle  *element_viewport,
+                                                 GError              **error);
 
 
 
@@ -1449,6 +1454,56 @@ rsvg_handle_get_geometry_for_element (RsvgHandle     *handle,
                                                       error);
 }
 
+/**
+ * rsvg_handle_render_element:
+ * @handle: An #RsvgHandle
+ * @cr: A Cairo context
+ * @id: (nullable): An element's id within the SVG, starting with "##" (a single
+ * hash character), for example, "##layer1".  This notation corresponds to a
+ * URL's fragment ID.  Alternatively, pass %NULL to render the whole SVG document tree.
+ * @element_viewport: Viewport size in which to fit the element
+ * @error: (allow-none): a location to store a #GError, or %NULL
+ *
+ * Renders a single SVG element to a given viewport
+ *
+ * This function can be used to extract individual element subtrees and render them,
+ * scaled to a given @element_viewport.  This is useful for applications which have
+ * reusable objects in an SVG and want to render them individually; for example, an
+ * SVG full of icons that are meant to be be rendered independently of each other.
+ *
+ * Element IDs should look like an URL fragment identifier; for example, pass
+ * "##foo" (hash <literal>foo</literal>) to get the geometry of the element that
+ * has an <literal>id="foo"</literal> attribute.
+ *
+ * You can pass #NULL for the @id if you want to render all
+ * the elements in the SVG, i.e. to render everything from the
+ * root element.
+ *
+ * The `element_viewport` gives the position and size at which the named element will
+ * be rendered.  FIXME: mention proportional scaling.
+ *
+ * API ordering: This function must be called on a fully-loaded @handle.  See
+ * the section <link href="#API-ordering">API ordering</link> for details.
+ *
+ * Panics: this function will panic if the @handle is not fully-loaded.
+ *
+ * Since: 2.46
+ */
+gboolean
+rsvg_handle_render_element (RsvgHandle           *handle,
+                            cairo_t              *cr,
+                            const char           *id,
+                            const RsvgRectangle  *element_viewport,
+                            GError              **error)
+{
+    g_return_val_if_fail (RSVG_IS_HANDLE (handle), FALSE);
+    g_return_val_if_fail (cr != NULL, FALSE);
+    g_return_val_if_fail (element_viewport != NULL, FALSE);
+    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+    return rsvg_rust_handle_render_element (handle, cr, id, element_viewport, error);
+}
+
 /**
  * rsvg_handle_internal_set_testing:
  * @handle: a #RsvgHandle
diff --git a/rsvg_internals/src/c_api.rs b/rsvg_internals/src/c_api.rs
index 24de9dfd..f8d2477c 100644
--- a/rsvg_internals/src/c_api.rs
+++ b/rsvg_internals/src/c_api.rs
@@ -738,6 +738,16 @@ impl CHandle {
         handle.get_geometry_for_element(id, self.dpi.get(), self.is_testing.get())
     }
 
+    fn render_element(
+        &self,
+        cr: &cairo::Context,
+        id: Option<&str>,
+        element_viewport: &cairo::Rectangle,
+    ) -> Result<(), RenderingError> {
+        let handle = self.get_handle_ref()?;
+        handle.render_element(cr, id, element_viewport, self.dpi.get(), self.is_testing.get())
+    }
+
     fn get_intrinsic_dimensions(&self) -> Result<IntrinsicDimensions, RenderingError> {
         let handle = self.get_handle_ref()?;
         Ok(handle.get_intrinsic_dimensions())
@@ -1383,6 +1393,32 @@ pub unsafe extern "C" fn rsvg_rust_handle_get_geometry_for_element(
     }
 }
 
+#[no_mangle]
+pub unsafe extern "C" fn rsvg_rust_handle_render_element(
+    handle: *mut RsvgHandle,
+    cr: *mut cairo_sys::cairo_t,
+    id: *const libc::c_char,
+    element_viewport: *const RsvgRectangle,
+    error: *mut *mut glib_sys::GError,
+) -> glib_sys::gboolean {
+    let rhandle = get_rust_handle(handle);
+    let cr = from_glib_none(cr);
+    let id: Option<String> = from_glib_none(id);
+
+    match rhandle.render_element(
+        &cr,
+        id.as_ref().map(String::as_str),
+        &(*element_viewport).into(),
+    ) {
+        Ok(()) => true.to_glib(),
+
+        Err(e) => {
+            set_gerror(error, 0, &format!("{}", e));
+            false.to_glib()
+        }
+    }
+}
+
 /// Detects whether a `*const libc::c_char` is a path or a URI
 ///
 /// `rsvg_handle_new_from_file()` takes a `filename` argument, and advertises
diff --git a/rsvg_internals/src/lib.rs b/rsvg_internals/src/lib.rs
index c8441b63..d359fde9 100644
--- a/rsvg_internals/src/lib.rs
+++ b/rsvg_internals/src/lib.rs
@@ -28,6 +28,7 @@ pub use crate::c_api::{
     rsvg_rust_handle_new_with_flags,
     rsvg_rust_handle_read_stream_sync,
     rsvg_rust_handle_render_cairo_sub,
+    rsvg_rust_handle_render_element,
     rsvg_rust_handle_render_document,
     rsvg_rust_handle_render_layer,
     rsvg_rust_handle_set_base_gfile,
diff --git a/tests/api.c b/tests/api.c
index 48849af3..9a72cbd4 100644
--- a/tests/api.c
+++ b/tests/api.c
@@ -1076,7 +1076,7 @@ render_layer (void)
 }
 
 static void
-get_geometry_for_element (void)
+untransformed_element (void)
 {
     char *filename = get_test_filename ("geometry-element.svg");
     GError *error = NULL;
@@ -1111,6 +1111,42 @@ get_geometry_for_element (void)
     g_assert_cmpfloat (logical_rect.width, ==, 30.0);
     g_assert_cmpfloat (logical_rect.height, ==, 40.0);
 
+    cairo_surface_t *output = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 300, 300);
+    cairo_t *cr = cairo_create (output);
+
+    RsvgRectangle viewport = { 100.0, 100.0, 100.0, 100.0 };
+
+    g_assert (rsvg_handle_render_element (handle, cr, "#foo", &viewport, &error));
+    g_assert (error == NULL);
+
+    cairo_destroy (cr);
+
+    cairo_surface_t *expected = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 300, 300);
+    cr = cairo_create (expected);
+
+    cairo_translate (cr, 100.0, 100.0);
+    cairo_rectangle (cr, 10.0, 10.0, 60.0, 80.0);
+    cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 1.0);
+    cairo_fill_preserve (cr);
+
+    cairo_set_line_width (cr, 20.0);
+    cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
+    cairo_stroke (cr);
+
+    cairo_destroy (cr);
+
+    cairo_surface_t *diff = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 300, 300);
+
+    TestUtilsBufferDiffResult result = {0, 0};
+    test_utils_compare_surfaces (output, expected, diff, &result);
+
+    if (result.pixels_changed && result.max_diff > 0) {
+        g_test_fail ();
+    }
+
+    cairo_surface_destroy (diff);
+    cairo_surface_destroy (expected);
+    cairo_surface_destroy (output);
     g_object_unref (handle);
 }
 
@@ -1339,7 +1375,7 @@ main (int argc, char **argv)
     g_test_add_func ("/api/render_document", render_document);
     g_test_add_func ("/api/get_geometry_for_layer", get_geometry_for_layer);
     g_test_add_func ("/api/render_layer", render_layer);
-    g_test_add_func ("/api/get_geometry_for_element", get_geometry_for_element);
+    g_test_add_func ("/api/untransformed_element", untransformed_element);
     g_test_add_func ("/api/no_write_before_close", no_write_before_close);
     g_test_add_func ("/api/empty_write_close", empty_write_close);
     g_test_add_func ("/api/cannot_request_external_elements", cannot_request_external_elements);


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