[librsvg/rustify-rsvg-convert: 2/41] rsvg-convert: Start work on the actual rendering
- From: Paolo Borelli <pborelli src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg/rustify-rsvg-convert: 2/41] rsvg-convert: Start work on the actual rendering
- Date: Sun, 17 Jan 2021 16:57:03 +0000 (UTC)
commit 0f49460a7ceb608dfa1f0c4f08308f543ec55345
Author: Sven Neumann <sven svenfoo org>
Date: Tue Oct 27 22:57:10 2020 +0100
rsvg-convert: Start work on the actual rendering
Handles input and output, but pretty much ignores sizing so far.
src/bin/rsvg-convert/cli.rs | 45 +++++++++++--------
src/bin/rsvg-convert/input.rs | 94 +++++++++++++++++++++++++++++++++++++++
src/bin/rsvg-convert/main.rs | 55 ++++++++++++++++++++++-
src/bin/rsvg-convert/output.rs | 44 ++++++++++++++++++
src/bin/rsvg-convert/surface.rs | 76 +++++++++++++++++++++++++++++++
tests/src/cmdline/rsvg_convert.rs | 10 +----
6 files changed, 295 insertions(+), 29 deletions(-)
---
diff --git a/src/bin/rsvg-convert/cli.rs b/src/bin/rsvg-convert/cli.rs
index d5b05731..27a4f439 100644
--- a/src/bin/rsvg-convert/cli.rs
+++ b/src/bin/rsvg-convert/cli.rs
@@ -1,11 +1,13 @@
// command-line interface for rsvg-convert
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
use librsvg::{Color, Parse};
+use crate::input::Input;
+
arg_enum! {
- #[derive(Debug)]
+ #[derive(Clone, Copy, Debug)]
pub enum Format {
Png,
Pdf,
@@ -17,18 +19,19 @@ arg_enum! {
#[derive(Debug)]
pub struct Args {
- resolution: (f32, f32),
- zoom: (f32, f32),
- width: Option<u32>,
- height: Option<u32>,
- format: Format,
+ pub dpi_x: f64,
+ pub dpi_y: f64,
+ pub zoom: (f32, f32),
+ pub width: Option<u32>,
+ pub height: Option<u32>,
+ pub format: Format,
+ pub export_id: Option<String>,
+ pub keep_aspect_ratio: bool,
+ pub background_color: Option<Color>,
+ pub stylesheet: Option<PathBuf>,
+ pub unlimited: bool,
+ pub keep_image_data: bool,
output: Option<PathBuf>,
- export_id: Option<String>,
- keep_aspect_ratio: bool,
- background_color: Option<Color>,
- stylesheet: Option<PathBuf>,
- unlimited: bool,
- keep_image_data: bool,
input: Vec<PathBuf>,
}
@@ -195,10 +198,8 @@ impl Args {
};
let args = Args {
- resolution: (
- value_t!(matches, "res_x", f32)?,
- value_t!(matches, "res_y", f32)?,
- ),
+ dpi_x: value_t!(matches, "res_x", f64)?,
+ dpi_y: value_t!(matches, "res_y", f64)?,
zoom: if matches.is_present("zoom") {
let zoom = value_t!(matches, "zoom", f32)?;
(zoom, zoom)
@@ -210,7 +211,6 @@ impl Args {
width: value_t!(matches, "size_x", u32).or_none()?,
height: value_t!(matches, "size_y", u32).or_none()?,
format,
- output: matches.value_of_os("output").map(PathBuf::from),
export_id: value_t!(matches, "export_id", String)
.or_none()?
.map(lookup_id),
@@ -219,6 +219,7 @@ impl Args {
stylesheet: matches.value_of_os("stylesheet").map(PathBuf::from),
unlimited: matches.is_present("unlimited"),
keep_image_data,
+ output: matches.value_of_os("output").map(PathBuf::from),
input: match matches.values_of_os("FILE") {
Some(values) => values.map(PathBuf::from).collect(),
None => Vec::new(),
@@ -239,6 +240,14 @@ impl Args {
Ok(args)
}
+
+ pub fn output(&self) -> Option<&Path> {
+ self.output.as_deref()
+ }
+
+ pub fn input(&self) -> Input<'_> {
+ Input::new(&self.input)
+ }
}
trait NotFound {
diff --git a/src/bin/rsvg-convert/input.rs b/src/bin/rsvg-convert/input.rs
new file mode 100644
index 00000000..0e7637f4
--- /dev/null
+++ b/src/bin/rsvg-convert/input.rs
@@ -0,0 +1,94 @@
+// input file handling for rsvg-convert
+
+use core::ops::Deref;
+use gio::FileExt;
+use std::os::unix::io::RawFd;
+use std::path::PathBuf;
+
+struct Stdin;
+
+impl Stdin {
+ pub fn stream() -> gio::UnixInputStream {
+ unsafe { gio::UnixInputStream::new(Self {}) }
+ }
+}
+
+impl std::os::unix::io::IntoRawFd for Stdin {
+ fn into_raw_fd(self) -> RawFd {
+ 0 as RawFd
+ }
+}
+
+pub enum Input<'a> {
+ Paths(std::slice::Iter<'a, PathBuf>),
+ Stdin(std::iter::Once<gio::UnixInputStream>),
+}
+
+impl<'a> Input<'a> {
+ pub fn new(paths: &'a [PathBuf]) -> Self {
+ match paths.len() {
+ 0 => Input::Stdin(std::iter::once(Stdin::stream())),
+ _ => Input::Paths(paths.iter()),
+ }
+ }
+}
+
+enum Stream {
+ File(gio::FileInputStream),
+ Unix(gio::UnixInputStream),
+}
+
+impl Deref for Stream {
+ type Target = gio::InputStream;
+
+ fn deref(&self) -> &Self::Target {
+ match self {
+ Self::File(stream) => stream.as_ref(),
+ Self::Unix(stream) => stream.as_ref(),
+ }
+ }
+}
+
+pub struct Item {
+ stream: Stream,
+ file: Option<gio::File>,
+}
+
+impl Item {
+ fn from_file(file: gio::File) -> Self {
+ Self {
+ // TODO: unwrap
+ stream: Stream::File(file.read(None::<&gio::Cancellable>).unwrap()),
+ file: Some(file),
+ }
+ }
+ fn from_path(path: &PathBuf) -> Self {
+ Self::from_file(gio::File::new_for_path(path))
+ }
+
+ fn from_unix_stream(stream: gio::UnixInputStream) -> Self {
+ Self {
+ stream: Stream::Unix(stream),
+ file: None,
+ }
+ }
+
+ pub fn stream(&self) -> &gio::InputStream {
+ self.stream.deref()
+ }
+
+ pub fn file(&self) -> Option<&gio::File> {
+ self.file.as_ref()
+ }
+}
+
+impl Iterator for Input<'_> {
+ type Item = Item;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self {
+ Input::Paths(paths) => paths.next().map(Item::from_path),
+ Input::Stdin(iter) => iter.next().map(Item::from_unix_stream),
+ }
+ }
+}
diff --git a/src/bin/rsvg-convert/main.rs b/src/bin/rsvg-convert/main.rs
index c6e6cb7c..1c12fb55 100644
--- a/src/bin/rsvg-convert/main.rs
+++ b/src/bin/rsvg-convert/main.rs
@@ -2,9 +2,60 @@
extern crate clap;
mod cli;
+mod input;
+mod output;
+mod surface;
+
+use librsvg::{CairoRenderer, Loader};
+
+use crate::cli::Args;
+use crate::output::Stream;
+use crate::surface::Surface;
+
+fn load_stylesheet(args: &Args) -> std::io::Result<Option<String>> {
+ match args.stylesheet {
+ Some(ref filename) => std::fs::read_to_string(filename).map(Some),
+ None => Ok(None),
+ }
+}
fn main() {
- let args = cli::Args::new().unwrap_or_else(|e| e.exit());
+ let args = Args::new().unwrap_or_else(|e| e.exit());
+
+ let stylesheet = load_stylesheet(&args).expect("could not load stylesheet");
+ let mut target = None;
+
+ for input in args.input() {
+ let mut handle = Loader::new()
+ .with_unlimited_size(args.unlimited)
+ .keep_image_data(args.keep_image_data)
+ .read_stream(input.stream(), input.file(), None::<&gio::Cancellable>)
+ .expect("error loading SVG file");
+
+ if let Some(ref css) = stylesheet {
+ handle.set_stylesheet(&css).unwrap();
+ }
+
+ let renderer = CairoRenderer::new(&handle).with_dpi(args.dpi_x, args.dpi_y);
+
+ if target.is_none() {
+ target = match renderer.intrinsic_size_in_pixels() {
+ Some((width, height)) => {
+ let output = Stream::new(args.output()).unwrap();
+ Some(Surface::new(args.format, width, height, output).unwrap())
+ }
+ None => None,
+ };
+ }
+
+ if let Some(ref surface) = target {
+ surface
+ .render(&renderer, args.export_id.as_deref())
+ .unwrap();
+ }
+ }
- println!("{:?}", args);
+ if let Some(ref mut surface) = target {
+ surface.finish().unwrap();
+ }
}
diff --git a/src/bin/rsvg-convert/output.rs b/src/bin/rsvg-convert/output.rs
new file mode 100644
index 00000000..e6862ec4
--- /dev/null
+++ b/src/bin/rsvg-convert/output.rs
@@ -0,0 +1,44 @@
+// output stream for rsvg-convert
+
+use std::fs;
+use std::io;
+
+pub enum Stream {
+ File(fs::File),
+ Stdout(io::Stdout),
+}
+
+impl Stream {
+ pub fn new(path: Option<&std::path::Path>) -> io::Result<Self> {
+ match path {
+ Some(path) => {
+ let file = fs::File::create(path)?;
+ Ok(Self::File(file))
+ }
+ None => Ok(Self::Stdout(io::stdout())),
+ }
+ }
+}
+
+impl io::Write for Stream {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ match self {
+ Self::File(file) => file.write(buf),
+ Self::Stdout(stream) => stream.write(buf),
+ }
+ }
+
+ fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
+ match self {
+ Self::File(file) => file.write_all(buf),
+ Self::Stdout(stream) => stream.write_all(buf),
+ }
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ match self {
+ Self::File(file) => file.flush(),
+ Self::Stdout(stream) => stream.flush(),
+ }
+ }
+}
diff --git a/src/bin/rsvg-convert/surface.rs b/src/bin/rsvg-convert/surface.rs
new file mode 100644
index 00000000..262bb15c
--- /dev/null
+++ b/src/bin/rsvg-convert/surface.rs
@@ -0,0 +1,76 @@
+use core::ops::Deref;
+use std::io;
+
+use librsvg::{CairoRenderer, RenderingError};
+
+use crate::cli;
+use crate::output::Stream;
+
+pub enum Surface {
+ Png(cairo::ImageSurface, Stream),
+ Pdf(cairo::PdfSurface, (f64, f64)),
+}
+
+impl Deref for Surface {
+ type Target = cairo::Surface;
+
+ fn deref(&self) -> &cairo::Surface {
+ match self {
+ Self::Png(surface, _) => surface.deref(),
+ Self::Pdf(surface, _) => surface.deref(),
+ }
+ }
+}
+
+impl Surface {
+ pub fn new(
+ format: cli::Format,
+ width: f64,
+ height: f64,
+ stream: Stream,
+ ) -> Result<Self, cairo::Status> {
+ match format {
+ cli::Format::Png => {
+ cairo::ImageSurface::create(cairo::Format::ARgb32, width as i32, height as i32)
+ .map(|s| Self::Png(s, stream))
+ }
+ cli::Format::Pdf => cairo::PdfSurface::for_stream(width, height, stream)
+ .map(|s| Self::Pdf(s, (width, height))),
+ _ => Err(cairo::Status::InvalidFormat),
+ }
+ }
+
+ fn size(&self) -> (f64, f64) {
+ match self {
+ Self::Png(s, _) => (s.get_width() as f64, s.get_height() as f64),
+ Self::Pdf(_, size) => *size,
+ }
+ }
+
+ pub fn render(&self, renderer: &CairoRenderer, id: Option<&str>) -> Result<(), RenderingError> {
+ let cr = cairo::Context::new(self);
+
+ let (width, height) = self.size();
+ let viewport = cairo::Rectangle {
+ x: 0.0,
+ y: 0.0,
+ width,
+ height,
+ };
+
+ renderer.render_layer(&cr, id, &viewport)?;
+ cr.show_page();
+
+ Ok(())
+ }
+
+ pub fn finish(&mut self) -> Result<(), cairo::IoError> {
+ match self {
+ Self::Png(surface, stream) => surface.write_to_png(stream),
+ Self::Pdf(surface, _) => surface
+ .finish_output_stream()
+ .map(|_| ())
+ .map_err(|e| cairo::IoError::Io(io::Error::from(e))),
+ }
+ }
+}
diff --git a/tests/src/cmdline/rsvg_convert.rs b/tests/src/cmdline/rsvg_convert.rs
index 36a764eb..1e1396c1 100644
--- a/tests/src/cmdline/rsvg_convert.rs
+++ b/tests/src/cmdline/rsvg_convert.rs
@@ -34,16 +34,8 @@ use tempfile::Builder;
struct RsvgConvert {}
impl RsvgConvert {
- fn binary_location() -> &'static Path {
- match option_env!("LIBRSVG_BUILD_DIR") {
- Some(dir) => Path::new(dir),
- None => Path::new(env!("CARGO_MANIFEST_DIR")),
- }
- }
-
fn new() -> Command {
- let path = Self::binary_location().join("rsvg-convert");
- let mut command = Command::new(path);
+ let mut command = Command::cargo_bin("rsvg-convert").unwrap();
command.env_clear();
command
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]