linebender / resvg

An SVG rendering library.
Apache License 2.0
2.84k stars 229 forks source link

How do I encode SVG into PNG while keep it fit at any resolution? #752

Closed IskandarAlex2 closed 6 months ago

IskandarAlex2 commented 6 months ago

I am trying to make an SVG to PNG HTTP server, it is nothing complicated. But, I realized it didn't scale up when the server rendered it at a higher resolution than the original SVG. Instead, it stayed at its original canvas resolution just hanging at the upper left corner. I've been trying to find a way to fix it but the lack of documentation and examples puts me nowhere.

To those still confused about what I am trying to accomplish here; I want to render the SVG like Inkscape does when you export selection and set a higher resolution. The content follows the scale of the resolution the user wants to export it at.

RazrFalcon commented 6 months ago

You could try mimicking the --zoom flag from resvg CLI.

IskandarAlex2 commented 6 months ago

Where can I find the CLI? I've been using resvg in my rust app docker container so I have no idea where to get the CLI nor its source code

LaurenzV commented 6 months ago

It's here: https://github.com/RazrFalcon/resvg/blob/master/crates/resvg/src/main.rs

IskandarAlex2 commented 6 months ago

Alright, I got it working right. The code is quite cluttered in my files but for those who are trying to achieve something like I got

    let pixmap_size = tree.size().to_int_size();
    let horizontal_resolution = solve_ratio(pixmap_size.height(), pixmap_size.width(), max_vertical_resolution);
    let zoom_factor = svg_manipulator::calculate_scaling_factor(pixmap_size.height() as f32, pixmap_size.width() as f32, horizontal_resolution as f32);
    let fit_to = svg_manipulator::FitTo::Zoom(zoom_factor);

    let zoom_size = match fit_to.fit_to_size(pixmap_size) {
        Some(w) => w,
        None => {
            return HttpResponse::InternalServerError().finish();
        }
    };

    let ts = fit_to.fit_to_transform(tree.size().to_int_size());

    let mut pixmap = tiny_skia::Pixmap::new(zoom_size.width(), zoom_size.height()).unwrap();
    resvg::render(&tree, ts, &mut pixmap.as_mut());
fn solve_ratio(a: u32, b: u32, d: u32) -> u32 {
    let x = (d * b) / a;
    x
}

svg_manipulator module

use resvg::tiny_skia;

pub enum FitTo {
    /// Zoom by factor.
    Zoom(f32),
}

impl FitTo {
    pub fn fit_to_size(&self, size: tiny_skia::IntSize) -> Option<tiny_skia::IntSize> {
        match *self {
            FitTo::Zoom(z) => size.scale_by(z),
        }
    }

    pub fn fit_to_transform(&self, size: tiny_skia::IntSize) -> tiny_skia::Transform {
        let size1 = size.to_size();
        let size2 = match self.fit_to_size(size) {
            Some(v) => v.to_size(),
            None => return tiny_skia::Transform::default(),
        };
        tiny_skia::Transform::from_scale(
            size2.width() / size1.width(),
            size2.height() / size1.height(),
        )
    }
}

pub fn calculate_scaling_factor(x1: f32, y1: f32, x2: f32) -> f32 {
    if x1 == 0.0 || y1 == 0.0 {
        panic!("Neither x1 nor y1 can be zero to avoid division by zero!");
    }
    let kx = x2 / x1;

    kx
}

The code isn't the cleanest but it got the job done