[librsvg: 1/13] handle: add new API to get node geometry



commit aac1da9be2ccf25918f9bd47f3008a775a2a52cd
Author: Julian Sparber <julian sparber net>
Date:   Sun Nov 18 12:13:31 2018 +0200

    handle: add new API to get node geometry
    
    This adds a new API called rsvg_handle_get_geometry_sub(), it returns the
    exact position and dimension of a svg node.

 librsvg/rsvg-handle.c             | 87 +++++++++++++++++++++++++++++++++++++--
 librsvg/rsvg-private.h            |  2 +-
 librsvg/rsvg.h                    |  2 +
 rsvg_internals/src/drawing_ctx.rs | 19 ++++++++-
 rsvg_internals/src/lib.rs         |  2 +-
 tests/api.c                       | 12 +++++-
 tests/dimensions.c                | 12 ++++++
 7 files changed, 127 insertions(+), 9 deletions(-)
---
diff --git a/librsvg/rsvg-handle.c b/librsvg/rsvg-handle.c
index a0d582c9..025c6ed1 100644
--- a/librsvg/rsvg-handle.c
+++ b/librsvg/rsvg-handle.c
@@ -1196,7 +1196,7 @@ rsvg_handle_get_dimensions (RsvgHandle * handle, RsvgDimensionData * dimension_d
 }
 
 static gboolean
-get_node_ink_rect(RsvgHandle *handle, RsvgNode *node, cairo_rectangle_t *ink_rect)
+get_node_geometry(RsvgHandle *handle, RsvgNode *node, cairo_rectangle_t *ink_rect, cairo_rectangle_t 
*logical_rect)
 {
     RsvgDimensionData dimensions;
     cairo_surface_t *target;
@@ -1219,7 +1219,7 @@ get_node_ink_rect(RsvgHandle *handle, RsvgNode *node, cairo_rectangle_t *ink_rec
     rsvg_tree_cascade (handle->priv->tree);
     res = rsvg_drawing_ctx_draw_node_from_stack (draw, handle->priv->tree);
     if (res) {
-        res = rsvg_drawing_ctx_get_ink_rect (draw, ink_rect);
+        res = rsvg_drawing_ctx_get_geometry (draw, ink_rect, logical_rect);
     }
 
     rsvg_drawing_ctx_free (draw);
@@ -1280,7 +1280,7 @@ rsvg_handle_get_dimensions_sub (RsvgHandle * handle, RsvgDimensionData * dimensi
     if (id || !has_size) {
         cairo_rectangle_t ink_rect;
 
-        if (!get_node_ink_rect (handle, node, &ink_rect)) {
+        if (!get_node_geometry (handle, node, &ink_rect, NULL)) {
             goto out;
         }
 
@@ -1307,6 +1307,85 @@ out:
     return res;
 }
 
+/**
+ * rsvg_handle_get_geometry_sub:
+ * @handle: A #RsvgHandle
+ * @ink_rect: (out): A place to store the SVG fragment's geometry.
+ * @logical_rect: (out): A place to store the SVG fragment's logical geometry.
+ * @id: (nullable): An element's id within the SVG, starting with "##", for
+ * example, "##layer1"; or %NULL to use the whole SVG.
+ *
+ * Get the geometry of a subelement of the SVG file. Do not call from within
+ * the size_func callback, because an infinite loop will occur.
+ *
+ */
+gboolean
+rsvg_handle_get_geometry_sub (RsvgHandle * handle, cairo_rectangle_t * ink_rect, cairo_rectangle_t * 
logical_rect, const char *id)
+{
+    RsvgNode *root = NULL;
+    RsvgNode *node;
+    gboolean has_size;
+    int root_width, root_height;
+    gboolean res = FALSE;
+
+    g_return_val_if_fail (handle, FALSE);
+    g_return_val_if_fail (ink_rect, FALSE);
+    g_return_val_if_fail (logical_rect, FALSE);
+
+    memset (ink_rect, 0, sizeof (cairo_rectangle_t));
+    memset (logical_rect, 0, sizeof (cairo_rectangle_t));
+
+    if (handle->priv->tree == NULL)
+        return FALSE;
+
+    root = rsvg_tree_get_root (handle->priv->tree);
+
+    if (id && *id) {
+        node = rsvg_defs_lookup (handle->priv->defs, id);
+
+        if (node && rsvg_tree_is_root (handle->priv->tree, node))
+            id = NULL;
+    } else {
+        node = root;
+    }
+
+    if (!node && id) {
+        goto out;
+    }
+
+    has_size = rsvg_node_svg_get_size (root,
+                                       handle->priv->dpi_x, handle->priv->dpi_y,
+                                       &root_width, &root_height);
+
+    if (id || !has_size) {
+        if (!get_node_geometry (handle, node, ink_rect, logical_rect)) {
+            goto out;
+        }
+    } else {
+        ink_rect->width = root_width;
+        ink_rect->height = root_height;
+        ink_rect->x = 0;
+        ink_rect->y = 0;
+
+        logical_rect->width = root_width;
+        logical_rect->height = root_height;
+        logical_rect->x = 0;
+        logical_rect->y = 0;
+    }
+
+    if (handle->priv->size_func)
+        (*handle->priv->size_func) ((gint *)&ink_rect->width, (gint *)&ink_rect->height,
+                                    handle->priv->user_data);
+
+    res = TRUE;
+
+out:
+
+    g_clear_pointer (&root, rsvg_node_unref);
+
+    return res;
+}
+
 /**
  * rsvg_handle_get_position_sub:
  * @handle: A #RsvgHandle
@@ -1345,7 +1424,7 @@ rsvg_handle_get_position_sub (RsvgHandle * handle, RsvgPositionData * position_d
     if (rsvg_tree_is_root (handle->priv->tree, node))
         return TRUE;
 
-    if (!get_node_ink_rect (handle, node, &ink_rect))
+    if (!get_node_geometry (handle, node, &ink_rect, NULL))
         return FALSE;
 
     position_data->x = ink_rect.x;
diff --git a/librsvg/rsvg-private.h b/librsvg/rsvg-private.h
index c439dca8..e201ab67 100644
--- a/librsvg/rsvg-private.h
+++ b/librsvg/rsvg-private.h
@@ -223,7 +223,7 @@ gboolean rsvg_drawing_ctx_draw_node_from_stack (RsvgDrawingCtx *ctx, RsvgTree *t
 
 /* Defined in rsvg_internals/src/drawing_ctx.rs */
 G_GNUC_INTERNAL
-gboolean rsvg_drawing_ctx_get_ink_rect (RsvgDrawingCtx *ctx, cairo_rectangle_t *ink_rect);
+gboolean rsvg_drawing_ctx_get_geometry (RsvgDrawingCtx *ctx, cairo_rectangle_t *ink_rect, cairo_rectangle_t 
*logical_rect);
 
 /* Implemented in rust/src/node.rs */
 G_GNUC_INTERNAL
diff --git a/librsvg/rsvg.h b/librsvg/rsvg.h
index 2e73dbf9..8c4998eb 100644
--- a/librsvg/rsvg.h
+++ b/librsvg/rsvg.h
@@ -31,6 +31,7 @@
 #include <glib-object.h>
 #include <gio/gio.h>
 
+#include <cairo.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
 
 G_BEGIN_DECLS
@@ -151,6 +152,7 @@ void rsvg_handle_get_dimensions (RsvgHandle * handle, RsvgDimensionData * dimens
 
 gboolean rsvg_handle_get_dimensions_sub (RsvgHandle * handle, RsvgDimensionData * dimension_data, const char 
*id);
 gboolean rsvg_handle_get_position_sub (RsvgHandle * handle, RsvgPositionData * position_data, const char 
*id);
+gboolean rsvg_handle_get_geometry_sub (RsvgHandle * handle, cairo_rectangle_t * ink_rect, cairo_rectangle_t 
* logical_rect, const char *id);
 
 gboolean rsvg_handle_has_sub (RsvgHandle * handle, const char *id);
 
diff --git a/rsvg_internals/src/drawing_ctx.rs b/rsvg_internals/src/drawing_ctx.rs
index 447b8eb7..f63b9fb7 100644
--- a/rsvg_internals/src/drawing_ctx.rs
+++ b/rsvg_internals/src/drawing_ctx.rs
@@ -1094,16 +1094,17 @@ pub extern "C" fn rsvg_drawing_ctx_add_node_and_ancestors_to_stack(
 }
 
 #[no_mangle]
-pub extern "C" fn rsvg_drawing_ctx_get_ink_rect(
+pub extern "C" fn rsvg_drawing_ctx_get_geometry(
     raw_draw_ctx: *const RsvgDrawingCtx,
     ink_rect: *mut cairo_sys::cairo_rectangle_t,
+    logical_rect: *mut cairo_sys::cairo_rectangle_t,
 ) -> glib_sys::gboolean {
     assert!(!raw_draw_ctx.is_null());
     let draw_ctx = unsafe { &mut *(raw_draw_ctx as *mut DrawingCtx<'_>) };
 
     assert!(!ink_rect.is_null());
 
-    let res = match draw_ctx.get_bbox().ink_rect {
+    let mut res = match draw_ctx.get_bbox().ink_rect {
         Some(r) => unsafe {
             (*ink_rect).x = r.x;
             (*ink_rect).y = r.y;
@@ -1114,6 +1115,20 @@ pub extern "C" fn rsvg_drawing_ctx_get_ink_rect(
         _ => false,
     };
 
+    if !logical_rect.is_null() {
+        res = res
+            && match draw_ctx.get_bbox().rect {
+                Some(r) => unsafe {
+                    (*logical_rect).x = r.x;
+                    (*logical_rect).y = r.y;
+                    (*logical_rect).width = r.width;
+                    (*logical_rect).height = r.height;
+                    true
+                },
+                _ => false,
+            }
+    }
+
     res.to_glib()
 }
 
diff --git a/rsvg_internals/src/lib.rs b/rsvg_internals/src/lib.rs
index 9149bdeb..616ba2bd 100644
--- a/rsvg_internals/src/lib.rs
+++ b/rsvg_internals/src/lib.rs
@@ -42,7 +42,7 @@ pub use drawing_ctx::{
     rsvg_drawing_ctx_add_node_and_ancestors_to_stack,
     rsvg_drawing_ctx_draw_node_from_stack,
     rsvg_drawing_ctx_free,
-    rsvg_drawing_ctx_get_ink_rect,
+    rsvg_drawing_ctx_get_geometry,
     rsvg_drawing_ctx_new,
 };
 
diff --git a/tests/api.c b/tests/api.c
index 587f06d5..33504e3b 100644
--- a/tests/api.c
+++ b/tests/api.c
@@ -5,6 +5,7 @@
 
 #include <stdio.h>
 #include <glib.h>
+#include <cairo.h>
 
 #define RSVG_DISABLE_DEPRECATION_WARNINGS /* so we can test deprecated API */
 #include "librsvg/rsvg.h"
@@ -499,8 +500,18 @@ dimensions_and_position (void)
     g_assert_cmpint (pos.x, ==, EXAMPLE_TWO_X);
     g_assert_cmpint (pos.y, ==, EXAMPLE_TWO_Y);
 
+    /* TODO: test logical_rect */
+    cairo_rectangle_t ink_rect;
+    cairo_rectangle_t logical_rect;
+    g_assert (rsvg_handle_get_geometry_sub (handle, &ink_rect, &logical_rect, EXAMPLE_TWO_ID));
+    g_assert_cmpint (ink_rect.x, ==, EXAMPLE_TWO_X);
+    g_assert_cmpint (ink_rect.y, ==, EXAMPLE_TWO_Y);
+    g_assert_cmpint (ink_rect.width,  ==, EXAMPLE_TWO_W);
+    g_assert_cmpint (ink_rect.height, ==, EXAMPLE_TWO_H);
+
     g_assert (!rsvg_handle_get_position_sub (handle, &pos, EXAMPLE_NONEXISTENT_ID));
     g_assert (!rsvg_handle_get_dimensions_sub (handle, &dim, EXAMPLE_NONEXISTENT_ID));
+    g_assert (!rsvg_handle_get_geometry_sub (handle, &ink_rect, &logical_rect, EXAMPLE_NONEXISTENT_ID));
 
     g_object_unref (handle);
 }
@@ -519,7 +530,6 @@ detects_cairo_context_in_error (void)
         /* this is wrong; it is to simulate creating a surface and a cairo_t in error */
         cairo_surface_t *surf = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, -1, -1);
         cairo_t *cr = cairo_create (surf);
-
         /* rsvg_handle_render_cairo() should return FALSE when it gets a cr in an error state */
         g_assert (!rsvg_handle_render_cairo (handle, cr));
 
diff --git a/tests/dimensions.c b/tests/dimensions.c
index 6f390cf2..21c1b260 100644
--- a/tests/dimensions.c
+++ b/tests/dimensions.c
@@ -2,6 +2,7 @@
 /* vim: set ts=4 nowrap ai expandtab sw=4: */
 
 #include <glib.h>
+#include <cairo.h>
 #include "librsvg/rsvg.h"
 #include "test-utils.h"
 
@@ -24,6 +25,8 @@ test_dimensions (FixtureData *fixture)
     RsvgHandle *handle;
     RsvgPositionData position;
     RsvgDimensionData dimension;
+    cairo_rectangle_t ink_rect;
+    cairo_rectangle_t logical_rect;
     gchar *target_file;
     GError *error = NULL;
 
@@ -37,20 +40,29 @@ test_dimensions (FixtureData *fixture)
         g_assert (rsvg_handle_has_sub (handle, fixture->id));
         g_assert (rsvg_handle_get_position_sub (handle, &position, fixture->id));
         g_assert (rsvg_handle_get_dimensions_sub (handle, &dimension, fixture->id));
+        g_assert (rsvg_handle_get_geometry_sub (handle, &ink_rect, &logical_rect, fixture->id));
 
         g_message ("w=%d h=%d", dimension.width, dimension.height);
     } else {
         rsvg_handle_get_dimensions (handle, &dimension);
+        rsvg_handle_get_geometry_sub (handle, &ink_rect, &logical_rect, NULL);
     }
 
+    /* TODO: test logical position and dimension */
     if (fixture->has_position) {
         g_assert_cmpint (fixture->x, ==, position.x);
         g_assert_cmpint (fixture->y, ==, position.y);
+
+        g_assert_cmpint (fixture->x, ==, ink_rect.x);
+        g_assert_cmpint (fixture->y, ==, ink_rect.y);
     }
 
     if (fixture->has_dimensions) {
         g_assert_cmpint (fixture->width,  ==, dimension.width);
         g_assert_cmpint (fixture->height, ==, dimension.height);
+
+        g_assert_cmpint (fixture->width,  ==, ink_rect.width);
+        g_assert_cmpint (fixture->height, ==, ink_rect.height);
     }
 
     g_object_unref (handle);


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