Skip to content
GitLab
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Register
  • Sign in
  • librsvg librsvg
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 201
    • Issues 201
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 8
    • Merge requests 8
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Container Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • GNOMEGNOME
  • librsvglibrsvg
  • Issues
  • #448
Closed
Open
Issue created Mar 15, 2019 by Federico Mena Quintero@federicoMaintainer

RFC: render_element_to_viewport() should follow the <use> semantics

Seeing the following in @jsparber's svago-export made me realize that the current incarnation of render_element_to_viewport() is awkward to use:

fn render(handle: &SvgHandle, item: &Icon, file: &String) -> bool {
    let group = format!("#{}", item.id);
    let rect = format!("#{}", item.square);
    let renderer = CairoRenderer::new(handle);
    /* FIXME: vbox of intrinsic_dimensions() is for some reason None */
    let viewport = {
        let doc = renderer.intrinsic_dimensions();
        cairo::Rectangle {
            x: 0.0,
            y: 0.0,
            width: doc.width.unwrap().get_unitless(),
            height: doc.height.unwrap().get_unitless(),
        }
    };

    let (rect, _) = renderer
        .geometry_for_element(Some(&rect), &viewport)                      // 1
        .unwrap();

    /* don't render icons outside the viewport */
    if rect.x < 0. || rect.y < 0. || rect.x > viewport.width || rect.y > viewport.height {
        return false;
    }

    let mut surface = svg::File::new(rect.width, rect.height, file);       // 2
    surface.set_document_unit(SvgUnit::Px);
    let cr = cairo::Context::new(&surface);
    cr.translate(-rect.x, -rect.y);                                        // 3

    let _ = renderer.render_element_to_viewport(&cr, Some(group.as_str()), &viewport);   // 4
    true
}

In (1), the code gets the geometry of one of the unstroked/unfilled squares that Adwaita uses to define each icon's bounding box (look for fill:none;stroke:none; in there). This call to geometry_for_element really means "get me the geometry of the element #id as if you were rendering the whole SVG to a viewport of the given size".

In (2), the code creates a Cairo SVG surface with the pixel size returned by (1) for the icon's square. No problems so far.

But in (3), we translate so the icon will be rendered at the origin...

... Because in (4), the call to render_element_to_viewport means, "render just the element #id as if you were rendering the whole SVG to a viewport of the given size". What the user wants to do is to extract that element!

Within SVG, one can "instance" an element using the <use> element. It has x/y/width/height attributes to define a viewport, but width/height are only used if the referenced element is <svg> or <symbol>. In turn, each of those can have its own x/y/width/height viewport, a viewBox, and preserveAspectRatio.

The spec says:

The width and height properties on the use element have no effect if the referenced element does not establish a new viewport. In particular, the use element does not itself establish a new viewport, and therefore does not affect the interpretation of percentages in the re-used graphics.

I think librsvg can "augment" this for its public API by actually using the width/height even if the referenced element for render_element_in_viewport is not an <svg> or <symbol>. I think librsvg would actually have to figure out untransformed dimensions for the element in case it doesn't have its own viewport, just like the toplevel geometry computation code.

The idea is that a call like renderer.render_element_to_viewport("#id", Rectangle { ... }) would actually scale/translate the element to fit in that rectangle.

For the old semantics of rendering just a single element as if one were rendering the whole SVG to a viewport of a given size, I think one could just pass the returned element's geometry to render_element_to_viewport: roundtripping the geometry to the desired viewport should have the effect of rendering the element "in its original place".

Comments appreciated 😃

Assignee
Assign to
Time tracking