Add new API rsvg_handle_render_element()

parent 59825cc9
......@@ -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>
......
......@@ -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
......@@ -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
......
......@@ -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
......
......@@ -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,
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment