svg2img 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3d79ded4df5c7da3f90781d4a55545909261183b4ab6759baf2e20094ca48011
4
- data.tar.gz: d2bb132f2fa76a9f7e2754d37615afe67f111cb9e96b2534e298233620d21c05
3
+ metadata.gz: 18e78e2be604a584bdf8e325b2f007b46505e6d2d74cd584d55fa460aecc31d5
4
+ data.tar.gz: 6bacef7baabce46157cbc3d1af5dbefaae727e8726a1f35495c2d35f1d54cff1
5
5
  SHA512:
6
- metadata.gz: ee228f18a63c3b04969e8b3188b5b3a0c9b9a1a0c8cd3b89d0a8485e87ef27490aebe39cf9ef22944a8185a9540086569d0402b0b4da570a579289740dfbb706
7
- data.tar.gz: 06dc50fca47cce15698f4f1ab07b9d794986e3f141ba077586916a3e64c4afd7c84bd10655bb7af702da2f8f20dbb75619ed7d934bada4aa8dad35d7d138eb1e
6
+ metadata.gz: 1c34f340977be8cdf8c4fd8e2a49e15a5409bdbcb770d555bc984d085ee1e5de95d16f37584d06ff49fb4c3b72b50d1c7ef23393e2178a9024b5821dc14a202f
7
+ data.tar.gz: 1126a561399fb919c61ea017d7cd1d910ab4422ab51c568bf9482e7c421b9d56e532658dc722b293264fc3b67cbf2701c1d7e0b605f1c2a0e3205915c340c4e8
data/README.md CHANGED
@@ -40,6 +40,11 @@ bundle lock --add-platform arm64-darwin # Apple Silicon MacOS (i.e. M1)
40
40
 
41
41
  ## Usage
42
42
 
43
+ ```ruby
44
+ require "svg2img"
45
+ Svg2Img.process_svg(svg_string, options)
46
+ ```
47
+
43
48
  Example usage:
44
49
 
45
50
  ```ruby
@@ -50,12 +55,12 @@ circle_svg = <<~SVG
50
55
  <circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
51
56
  </svg>
52
57
  SVG
53
- png_path = Svg2Img.process_svg(circle_svg, output_format: :png)
58
+ png_path = Svg2Img.process_svg(circle_svg, format: :png)
54
59
  # png_path is a path to the generated PNG file
55
60
 
56
61
  # Rails example
57
62
  data = Rails.cache.fetch([some, deps]) do
58
- png_path = Svg2Img.process_svg(circle_svg, output_format: :png)
63
+ png_path = Svg2Img.process_svg(circle_svg, format: :png, size: proc {|_svg_width, _svg_height| [256, 256]})
59
64
  png_data = File.binread(png_path)
60
65
  File.delete(png_path)
61
66
  png_data
@@ -63,6 +68,12 @@ end
63
68
  send_data(png_data, type: 'image/png', disposition: 'inline')
64
69
  ```
65
70
 
71
+ ### Options
72
+
73
+ - `format` - output format, one of `:png`, `:jpg`, `:webp`, `:gif`
74
+ - `output_path` - path to the output image. If not provided, a temporary file will be created and the path to it will be returned.
75
+ - `size` - size of the output image as a proc that receives the width and height of the SVG and returns an array with the width and height of the output image. If the provides size has a different aspect ratio than the SVG, the image will be resized to fit in the center of the provided size. If not provided, the output image will have the same size as the SVG.
76
+
66
77
  ## Development
67
78
 
68
79
  After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -1,6 +1,6 @@
1
1
  use anyhow::Context;
2
- use image::{DynamicImage, GenericImageView, ImageBuffer};
3
- use magnus::{function, prelude::*, Error, Ruby};
2
+ use image::{DynamicImage, ImageBuffer};
3
+ use magnus::{block::Proc, function, prelude::*, Error, Ruby};
4
4
  use std::panic::{self, AssertUnwindSafe};
5
5
 
6
6
  #[magnus::init]
@@ -28,8 +28,9 @@ fn process_svg_rb(svg: String, options: magnus::RHash) -> Result<String, magnus:
28
28
  };
29
29
  }
30
30
  let options = Options {
31
- max_width: get_option(&options, "max_width")?,
32
- max_height: get_option(&options, "max_height")?,
31
+ size: get_option::<Proc>(&options, "size")?
32
+ .map(convert_size_proc)
33
+ .unwrap_or_else(default_size),
33
34
  format,
34
35
  output_path: get_option(&options, "output_path")?,
35
36
  };
@@ -76,32 +77,30 @@ fn get_string_option(options: &magnus::RHash, key: &str) -> Result<Option<String
76
77
  Ok(None)
77
78
  }
78
79
 
80
+ type ProcessSize = Box<dyn FnOnce(u32, u32) -> Result<(u32, u32), anyhow::Error>>;
81
+ fn default_size() -> ProcessSize {
82
+ Box::new(|width, height| Ok((width, height)))
83
+ }
84
+ fn convert_size_proc(proc: Proc) -> ProcessSize {
85
+ Box::new(move |width, height| {
86
+ let result: Result<(i64, i64), magnus::Error> = proc.call((width as i64, height as i64));
87
+ match result {
88
+ Ok((width, height)) => Ok((width as u32, height as u32)),
89
+ Err(err) => Err(anyhow::anyhow!(
90
+ "svg2img: Failed to call size proc: {err:?}"
91
+ )),
92
+ }
93
+ })
94
+ }
95
+
79
96
  struct Options {
80
- max_width: Option<u32>,
81
- max_height: Option<u32>,
97
+ size: ProcessSize,
82
98
  format: image::ImageFormat,
83
99
  output_path: Option<String>,
84
100
  }
85
101
 
86
102
  fn process_svg(svg: String, options: Options) -> Result<String, anyhow::Error> {
87
- let image = image_from_svg(svg.as_bytes())?;
88
- let (old_width, old_height) = image.dimensions();
89
-
90
- let (mut width, mut height) = (old_width, old_height);
91
- if let Some(max_width) = options.max_width {
92
- if width > max_width {
93
- height = height * max_width / width;
94
- width = max_width;
95
- }
96
- }
97
- if let Some(max_height) = options.max_height {
98
- if height > max_height {
99
- width = width * max_height / height;
100
- height = max_height;
101
- }
102
- }
103
-
104
- let mut image = image.resize_exact(width, height, image::imageops::FilterType::Lanczos3);
103
+ let mut image = image_from_svg(svg.as_bytes(), options.size)?;
105
104
 
106
105
  if options.format == image::ImageFormat::Jpeg {
107
106
  // Convert from rgba8 to rgb8
@@ -134,36 +133,56 @@ fn process_svg(svg: String, options: Options) -> Result<String, anyhow::Error> {
134
133
  Ok(output_path)
135
134
  }
136
135
 
137
- fn image_from_svg(bytes: &[u8]) -> Result<DynamicImage, anyhow::Error> {
138
- let tree = resvg::usvg::Tree::from_data(bytes, &resvg::usvg::Options::default())
136
+ fn image_from_svg(bytes: &[u8], size: ProcessSize) -> Result<DynamicImage, anyhow::Error> {
137
+ let svg = resvg::usvg::Tree::from_data(bytes, &resvg::usvg::Options::default())
139
138
  .context("Failed to parse SVG")?;
139
+ let svg_width = svg.size().width();
140
+ let svg_height = svg.size().height();
141
+ let svg_ratio = svg_width / svg_height;
140
142
 
141
- const TARGET_SIZE: u32 = 512;
142
- let mut pixmap = resvg::tiny_skia::Pixmap::new(TARGET_SIZE, TARGET_SIZE)
143
- .context("Failed to create Pixmap for SVG rendering")?;
144
- let ratio = tree.size().width() / tree.size().height();
145
- let scaled_width = if ratio > 1.0 {
146
- TARGET_SIZE
147
- } else {
148
- (TARGET_SIZE as f32 * ratio).round() as u32
149
- };
150
- let scaled_height = if ratio > 1.0 {
151
- (TARGET_SIZE as f32 / ratio).round() as u32
143
+ let (image_width, image_height) = size(svg_width as u32, svg_height as u32)?;
144
+ let image_ratio = image_width as f32 / image_height as f32;
145
+
146
+ let scale = if svg_ratio > image_ratio {
147
+ image_width as f32 / svg_width
152
148
  } else {
153
- TARGET_SIZE
149
+ image_height as f32 / svg_height
154
150
  };
151
+ let rendered_width = svg_width * scale;
152
+ let rendered_height = svg_height * scale;
153
+ let tx = (image_width as f32 - rendered_width) / 2.0;
154
+ let ty = (image_height as f32 - rendered_height) / 2.0;
155
+
156
+ // panic!(
157
+ // r#"
158
+ // svg_width: {svg_width}
159
+ // svg_height: {svg_height}
160
+ // svg_ratio: {svg_ratio}
161
+ // image_width: {image_width}
162
+ // image_height: {image_height}
163
+ // image_ratio: {image_ratio}
164
+ // rendered_width: {rendered_width}
165
+ // rendered_height: {rendered_height}
166
+ // scale: {scale}
167
+ // tx: {tx}
168
+ // ty: {ty}
169
+ // "#
170
+ // );
155
171
 
156
172
  // Scale svg and place it centered
157
- let transform = resvg::tiny_skia::Transform {
158
- sx: scaled_width as f32 / tree.size().width(),
159
- sy: scaled_height as f32 / tree.size().height(),
160
- tx: (TARGET_SIZE - scaled_width) as f32 / 2.0,
161
- ty: (TARGET_SIZE - scaled_height) as f32 / 2.0,
173
+ let transform: resvg::usvg::Transform = resvg::tiny_skia::Transform {
174
+ sx: scale,
175
+ sy: scale,
176
+ tx,
177
+ ty,
162
178
  ..Default::default()
163
179
  };
164
180
 
181
+ let mut pixmap = resvg::tiny_skia::Pixmap::new(image_width, image_height)
182
+ .context("Failed to create Pixmap for SVG rendering")?;
183
+
165
184
  let result = panic::catch_unwind(AssertUnwindSafe(|| {
166
- resvg::render(&tree, transform, &mut pixmap.as_mut());
185
+ resvg::render(&svg, transform, &mut pixmap.as_mut());
167
186
  }));
168
187
  if let Err(panic) = result {
169
188
  let panic_message = panic
@@ -178,10 +197,10 @@ fn image_from_svg(bytes: &[u8]) -> Result<DynamicImage, anyhow::Error> {
178
197
  return Err(anyhow::anyhow!("SVG rendering panicked: {}", panic_message));
179
198
  }
180
199
 
181
- rgba_to_image(pixmap.width(), pixmap.height(), pixmap.data())
200
+ pixmap_to_image(pixmap.width(), pixmap.height(), pixmap.data())
182
201
  }
183
202
 
184
- fn rgba_to_image(width: u32, height: u32, data: &[u8]) -> Result<DynamicImage, anyhow::Error> {
203
+ fn pixmap_to_image(width: u32, height: u32, data: &[u8]) -> Result<DynamicImage, anyhow::Error> {
185
204
  let mut image_data = Vec::with_capacity((width * height * 4) as usize);
186
205
  for pixel in data.chunks(4) {
187
206
  image_data.push(pixel[0]); // R
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Svg2Img
4
- VERSION = "0.1.1"
4
+ VERSION = "0.2.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: svg2img
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Orvar Segerström
@@ -44,9 +44,6 @@ files:
44
44
  - tmp/arm64-darwin23/stage/Cargo.lock
45
45
  - tmp/arm64-darwin23/stage/Cargo.toml
46
46
  - tmp/arm64-darwin23/stage/ext/svg2img/Cargo.toml
47
- - tmp/arm64-darwin23/stage/tmp/arm64-darwin23/stage/Cargo.lock
48
- - tmp/arm64-darwin23/stage/tmp/arm64-darwin23/stage/Cargo.toml
49
- - tmp/arm64-darwin23/stage/tmp/arm64-darwin23/stage/ext/svg2img/Cargo.toml
50
47
  homepage: https://github.com/0rvar/svg2img-rb
51
48
  licenses:
52
49
  - MIT