From 810fec1399f141dad6d74db670d7d1c502e9750d Mon Sep 17 00:00:00 2001
From: Sergey Fedorov <fygrave@gmail.com>
Date: Sun, 16 Aug 2026 10:43:19 +0000
Subject: [PATCH 5/7] rsvg: Add rsvg_handle_get_intrinsic_dimensions() and the
 2.52 pixel query

Both read the already-parsed width, height and viewBox off the root <svg>
and render nothing.

has_width and has_height are always returned as TRUE, matching librsvg
2.54 and later: SVG2 makes those geometry properties default to 100%, so
every document has them.

The internal length representation normalizes absolute units to inches
while parsing, so the reported unit for a length authored in cm, mm, pt or
pc is RSVG_UNIT_IN with an equivalent value.  Arithmetic over the result
is unaffected, including the width-to-height ratio that callers reading
these values for scaling actually use; only the authored spelling is lost.
---
 librsvg/rsvg.h |  12 ++++
 rsvg-base.c    | 175 ++++++++++++++++++++++++++++++++++++++++++++++++-
 rsvg.symbols   |   2 +
 3 files changed, 188 insertions(+), 1 deletion(-)

diff --git a/librsvg/rsvg.h b/librsvg/rsvg.h
index 2d5113705..b53d46128 100644
--- a/librsvg/rsvg.h
+++ b/librsvg/rsvg.h
@@ -228,6 +228,18 @@ gboolean rsvg_handle_has_sub (RsvgHandle * handle, const char *id);
 
 gboolean rsvg_handle_set_stylesheet (RsvgHandle * handle, const guint8 *css, gsize css_len, GError **error);
 
+void rsvg_handle_get_intrinsic_dimensions (RsvgHandle    *handle,
+                                           gboolean      *out_has_width,
+                                           RsvgLength    *out_width,
+                                           gboolean      *out_has_height,
+                                           RsvgLength    *out_height,
+                                           gboolean      *out_has_viewbox,
+                                           RsvgRectangle *out_viewbox);
+
+gboolean rsvg_handle_get_intrinsic_size_in_pixels (RsvgHandle *handle,
+                                                   gdouble    *out_width,
+                                                   gdouble    *out_height);
+
 /* GIO APIs */
 
 /**
diff --git a/rsvg-base.c b/rsvg-base.c
index 309ccc980..806b9e702 100644
--- a/rsvg-base.c
+++ b/rsvg-base.c
@@ -1632,7 +1632,180 @@ rsvg_handle_has_sub (RsvgHandle * handle,
     return rsvg_defs_lookup (handle->priv->defs, id) != NULL;
 }
 
-/** 
+/* Maps this build's internal length representation onto the public RsvgUnit
+ * enum.  Absolute lengths are normalized to inches while parsing, so lengths
+ * authored as cm/mm/pt/pc come back as RSVG_UNIT_IN with an equivalent value.
+ * The numeric value is left as-is, so callers doing unit-aware arithmetic get
+ * correct results; only the authored spelling is lost.
+ */
+static RsvgUnit
+rsvg_unit_from_internal_length (const RsvgInternalLength * in)
+{
+    switch (in->factor) {
+    case 'p':
+        return RSVG_UNIT_PERCENT;
+    case 'm':
+        return RSVG_UNIT_EM;
+    case 'x':
+        return RSVG_UNIT_EX;
+    case 'i':
+        return RSVG_UNIT_IN;
+    case '\0':
+    default:
+        return RSVG_UNIT_PX;
+    }
+}
+
+static void
+rsvg_length_from_internal (const RsvgInternalLength * in, RsvgLength * out)
+{
+    out->length = in->length;
+    out->unit = rsvg_unit_from_internal_length (in);
+}
+
+/**
+ * rsvg_handle_get_intrinsic_dimensions:
+ * @handle: An #RsvgHandle
+ * @out_has_width: (out)(optional): Will be set to %TRUE; see below.
+ * @out_width: (out)(optional): Will be set to the computed value of the
+ *   <literal>width</literal> property in the toplevel SVG.
+ * @out_has_height: (out)(optional): Will be set to %TRUE; see below.
+ * @out_height: (out)(optional): Will be set to the computed value of the
+ *   <literal>height</literal> property in the toplevel SVG.
+ * @out_has_viewbox: (out)(optional): Will be set to %TRUE if the toplevel SVG
+ *   has a <literal>viewBox</literal> attribute.
+ * @out_viewbox: (out)(optional): Will be set to the value of the
+ *   <literal>viewBox</literal> attribute in the toplevel SVG.
+ *
+ * Queries the <literal>width</literal>, <literal>height</literal> and
+ * <literal>viewBox</literal> attributes in an SVG document.
+ *
+ * As in librsvg 2.54 and later, @out_has_width and @out_has_height are always
+ * set to %TRUE: per SVG2 those properties default to <literal>100%</literal>
+ * when absent, so every document has them.
+ *
+ * Since: 2.46
+ */
+void
+rsvg_handle_get_intrinsic_dimensions (RsvgHandle * handle,
+                                      gboolean * out_has_width,
+                                      RsvgLength * out_width,
+                                      gboolean * out_has_height,
+                                      RsvgLength * out_height,
+                                      gboolean * out_has_viewbox,
+                                      RsvgRectangle * out_viewbox)
+{
+    RsvgNodeSvg *root;
+
+    g_return_if_fail (handle != NULL);
+
+    if (out_has_width)
+        *out_has_width = TRUE;
+    if (out_has_height)
+        *out_has_height = TRUE;
+    if (out_has_viewbox)
+        *out_has_viewbox = FALSE;
+
+    if (out_width) {
+        out_width->length = 1.0;
+        out_width->unit = RSVG_UNIT_PERCENT;
+    }
+    if (out_height) {
+        out_height->length = 1.0;
+        out_height->unit = RSVG_UNIT_PERCENT;
+    }
+    if (out_viewbox) {
+        out_viewbox->x = out_viewbox->y = 0.0;
+        out_viewbox->width = out_viewbox->height = 0.0;
+    }
+
+    root = (RsvgNodeSvg *) handle->priv->treebase;
+    if (root == NULL) {
+        g_critical ("rsvg_handle_get_intrinsic_dimensions() called on a handle "
+                    "that is not fully loaded");
+        return;
+    }
+
+    if (out_width)
+        rsvg_length_from_internal (&root->w, out_width);
+    if (out_height)
+        rsvg_length_from_internal (&root->h, out_height);
+
+    if (root->vbox.active) {
+        if (out_has_viewbox)
+            *out_has_viewbox = TRUE;
+        if (out_viewbox) {
+            out_viewbox->x = root->vbox.rect.x;
+            out_viewbox->y = root->vbox.rect.y;
+            out_viewbox->width = root->vbox.rect.width;
+            out_viewbox->height = root->vbox.rect.height;
+        }
+    }
+}
+
+/**
+ * rsvg_handle_get_intrinsic_size_in_pixels:
+ * @handle: An #RsvgHandle
+ * @out_width: (out)(optional): Will be set to the computed width.
+ * @out_height: (out)(optional): Will be set to the computed height.
+ *
+ * Converts an SVG document's intrinsic dimensions to pixels, and returns the
+ * result.
+ *
+ * This only succeeds if the document has both <literal>width</literal> and
+ * <literal>height</literal> in absolute or font-based units; percentages need a
+ * viewport to be resolved against and make this return %FALSE.
+ *
+ * Returns: %TRUE if the dimensions could be converted directly to pixels.
+ *
+ * Since: 2.52
+ */
+gboolean
+rsvg_handle_get_intrinsic_size_in_pixels (RsvgHandle * handle,
+                                          gdouble * out_width,
+                                          gdouble * out_height)
+{
+    RsvgNodeSvg *root;
+    double font_size = 12.0;
+    double width, height;
+
+    g_return_val_if_fail (handle != NULL, FALSE);
+
+    if (out_width)
+        *out_width = 0.0;
+    if (out_height)
+        *out_height = 0.0;
+
+    root = (RsvgNodeSvg *) handle->priv->treebase;
+    if (root == NULL) {
+        g_critical ("rsvg_handle_get_intrinsic_size_in_pixels() called on a handle "
+                    "that is not fully loaded");
+        return FALSE;
+    }
+
+    /* Percentages have no viewport to resolve against here. */
+    if (root->w.factor == 'p' || root->h.factor == 'p')
+        return FALSE;
+
+    if (root->super.state != NULL && root->super.state->has_font_size)
+        font_size = _rsvg_css_hand_normalize_length (&root->super.state->font_size,
+                                                     handle->priv->dpi_y, 0.0, 12.0);
+
+    width = _rsvg_css_hand_normalize_length (&root->w, handle->priv->dpi_x, 0.0, font_size);
+    height = _rsvg_css_hand_normalize_length (&root->h, handle->priv->dpi_y, 0.0, font_size);
+
+    if (!(width > 0.0) || !(height > 0.0))
+        return FALSE;
+
+    if (out_width)
+        *out_width = width;
+    if (out_height)
+        *out_height = height;
+
+    return TRUE;
+}
+
+/**
  * rsvg_set_default_dpi:
  * @dpi: Dots Per Inch (aka Pixels Per Inch)
  *
diff --git a/rsvg.symbols b/rsvg.symbols
index d74dd5cb0..b54607235 100644
--- a/rsvg.symbols
+++ b/rsvg.symbols
@@ -5,6 +5,8 @@ rsvg_handle_close
 rsvg_handle_get_base_uri
 rsvg_handle_get_dimensions
 rsvg_handle_get_dimensions_sub
+rsvg_handle_get_intrinsic_dimensions
+rsvg_handle_get_intrinsic_size_in_pixels
 rsvg_handle_get_position_sub
 rsvg_handle_get_pixbuf
 rsvg_handle_get_pixbuf_sub
-- 
2.43.0

