[librsvg] Move the mutable LoadState into CHandle



commit 90f5d0623108dbbc43ab0c59591e2599d05cc187
Author: Federico Mena Quintero <federico gnome org>
Date:   Tue Apr 9 11:34:07 2019 -0500

    Move the mutable LoadState into CHandle
    
    This looks like a lot of churn, but here's basically what happened:
    
    We used to have
    
      struct Handle {
          ... fields with interior mutability ...
          load_state: Cell<LoadState> // a simple enum
      }
    
    Now we have this:
    
      struct CHandle {
          ... fields with interior mutability ...
          load_state: RefCell<LoadState> // a complex enum
      }
    
      enum LoadState {
          Start,
          Loading { buffer: Vec<u8> },
          ClosedOk { handle: Handle },
          ClosedError,
      }
    
      struct Handle {
        ... only immutable fields ...
      }
    
    Here, CHandle now encapsulates all the interior mutability for the
    purposes of the C API, and Handle is created immutable.
    
    CHandle does all the checking of load_state to ensure the C API is
    called in the correct sequence, and so that (e.g.) a handle is not
    queried for dimensions until it is loaded.
    
    In theory we could now have an rsvg_internals crate with a "clean"
    immutable API, and a small helper crate just for the purposes of the C
    API.

 librsvg_crate/src/lib.rs           |  14 +-
 rsvg_internals/src/c_api.rs        | 318 ++++++++++++++++++++++++++-----------
 rsvg_internals/src/handle.rs       | 168 ++++----------------
 rsvg_internals/src/pixbuf_utils.rs |  24 ++-
 4 files changed, 272 insertions(+), 252 deletions(-)
---
diff --git a/librsvg_crate/src/lib.rs b/librsvg_crate/src/lib.rs
index 3d8dbd26..7cfc7dcc 100644
--- a/librsvg_crate/src/lib.rs
+++ b/librsvg_crate/src/lib.rs
@@ -279,8 +279,6 @@ impl Loader {
         base_file: Option<&gio::File>,
         cancellable: P,
     ) -> Result<SvgHandle, LoadingError> {
-        let handle = Handle::new();
-
         let base_url = if let Some(base_file) = base_file {
             Some(url_from_file(&base_file)?)
         } else {
@@ -289,9 +287,11 @@ impl Loader {
 
         let load_options = LoadOptions::new(self.load_flags(), base_url);
 
-        handle.read_stream_sync(&load_options, stream, cancellable.into())?;
-
-        Ok(SvgHandle(handle))
+        Ok(SvgHandle(Handle::from_stream(
+            &load_options,
+            stream,
+            cancellable.into(),
+        )?))
     }
 }
 
@@ -396,7 +396,7 @@ impl<'a> CairoRenderer<'a> {
     ) -> Result<(cairo::Rectangle, cairo::Rectangle), RenderingError> {
         self.handle
             .0
-            .get_geometry_for_element(id, viewport, self.dpi)
+            .get_geometry_for_element(id, viewport, self.dpi, false)
             .map(|(i, l)| (i.into(), l.into()))
     }
 
@@ -408,6 +408,6 @@ impl<'a> CairoRenderer<'a> {
     ) -> Result<(), RenderingError> {
         self.handle
             .0
-            .render_element_to_viewport(cr, id, viewport, self.dpi)
+            .render_element_to_viewport(cr, id, viewport, self.dpi, false)
     }
 }
diff --git a/rsvg_internals/src/c_api.rs b/rsvg_internals/src/c_api.rs
index 8c80a00d..0a7bde4f 100644
--- a/rsvg_internals/src/c_api.rs
+++ b/rsvg_internals/src/c_api.rs
@@ -1,4 +1,4 @@
-use std::cell::{Cell, RefCell};
+use std::cell::{Cell, Ref, RefCell, RefMut};
 use std::ffi::{CStr, CString};
 use std::ops;
 use std::path::PathBuf;
@@ -7,7 +7,9 @@ use std::slice;
 use std::sync::Once;
 use std::{f64, i32};
 
+use gdk_pixbuf::Pixbuf;
 use libc;
+use url::Url;
 
 use gio::prelude::*;
 
@@ -17,17 +19,18 @@ use glib::subclass::object::ObjectClassSubclassExt;
 use glib::subclass::prelude::*;
 use glib::translate::*;
 use glib::value::{FromValue, FromValueOptional, SetValue};
-use glib::{Cast, ParamFlags, ParamSpec, StaticType, ToValue, Type, Value};
+use glib::{Bytes, Cast, ParamFlags, ParamSpec, StaticType, ToValue, Type, Value};
 
 use glib_sys;
 use gobject_sys::{self, GEnumValue, GFlagsValue};
 
 use crate::dpi::Dpi;
 use crate::drawing_ctx::RsvgRectangle;
-use crate::error::{set_gerror, LoadingError, RSVG_ERROR_FAILED};
-use crate::handle::{Handle, LoadFlags, LoadOptions, LoadState};
+use crate::error::{set_gerror, LoadingError, RenderingError, RSVG_ERROR_FAILED};
+use crate::handle::{Handle, LoadFlags, LoadOptions};
 use crate::length::RsvgLength;
-use url::Url;
+use crate::structure::IntrinsicDimensions;
+use crate::util::rsvg_g_warning;
 
 mod handle_flags {
     // The following is entirely stolen from the auto-generated code
@@ -211,6 +214,18 @@ impl Drop for SizeCallback {
     }
 }
 
+enum LoadState {
+    // Just created the CHandle
+    Start,
+
+    // Being loaded using the legacy write()/close() API
+    Loading { buffer: Vec<u8> },
+
+    ClosedOk { handle: Handle },
+
+    ClosedError,
+}
+
 /// Contains all the interior mutability for a RsvgHandle to be called
 /// from the C API.
 pub struct CHandle {
@@ -219,7 +234,8 @@ pub struct CHandle {
     base_url: RefCell<Option<Url>>,
     base_url_cstring: RefCell<Option<CString>>, // needed because the C api returns *const char
     size_callback: RefCell<SizeCallback>,
-    handle: Handle,
+    is_testing: Cell<bool>,
+    load_state: RefCell<LoadState>,
 }
 
 unsafe impl ClassStruct for RsvgHandleClass {
@@ -350,7 +366,8 @@ impl ObjectSubclass for CHandle {
             base_url: RefCell::new(None),
             base_url_cstring: RefCell::new(None),
             size_callback: RefCell::new(SizeCallback::default()),
-            handle: Handle::new(),
+            is_testing: Cell::new(false),
+            load_state: RefCell::new(LoadState::Start),
         }
     }
 }
@@ -415,24 +432,29 @@ impl ObjectImpl for CHandle {
                 .to_value()),
 
             subclass::Property("width", ..) => Ok(self
-                .handle
-                .get_dimensions_no_error(self.dpi.get(), &*size_callback)
+                .get_handle_ref()
+                .unwrap()
+                .get_dimensions_no_error(self.dpi.get(), &*size_callback, self.is_testing.get())
                 .width
                 .to_value()),
+
             subclass::Property("height", ..) => Ok(self
-                .handle
-                .get_dimensions_no_error(self.dpi.get(), &*size_callback)
+                .get_handle_ref()
+                .unwrap()
+                .get_dimensions_no_error(self.dpi.get(), &*size_callback, self.is_testing.get())
                 .height
                 .to_value()),
 
             subclass::Property("em", ..) => Ok(self
-                .handle
-                .get_dimensions_no_error(self.dpi.get(), &*size_callback)
+                .get_handle_ref()
+                .unwrap()
+                .get_dimensions_no_error(self.dpi.get(), &*size_callback, self.is_testing.get())
                 .em
                 .to_value()),
             subclass::Property("ex", ..) => Ok(self
-                .handle
-                .get_dimensions_no_error(self.dpi.get(), &*size_callback)
+                .get_handle_ref()
+                .unwrap()
+                .get_dimensions_no_error(self.dpi.get(), &*size_callback, self.is_testing.get())
                 .ex
                 .to_value()),
 
@@ -503,6 +525,177 @@ impl CHandle {
             in_loop: Cell::new(false),
         };
     }
+
+    fn write(&self, buf: &[u8]) {
+        let mut state = self.load_state.borrow_mut();
+
+        match *state {
+            LoadState::Start => {
+                *state = LoadState::Loading {
+                    buffer: Vec::from(buf),
+                }
+            }
+
+            LoadState::Loading { ref mut buffer } => {
+                buffer.extend_from_slice(buf);
+            }
+
+            _ => panic!("Handle must not be closed in order to write to it"),
+        }
+    }
+
+    fn close(&self) -> Result<(), LoadingError> {
+        let mut state = self.load_state.borrow_mut();
+
+        match *state {
+            LoadState::Start => {
+                *state = LoadState::ClosedError;
+                Err(LoadingError::NoDataPassedToParser)
+            }
+
+            LoadState::Loading { ref buffer } => {
+                let bytes = Bytes::from(&*buffer);
+                let stream = gio::MemoryInputStream::new_from_bytes(&bytes);
+
+                self.read_stream(state, &stream.upcast(), None)
+            }
+
+            // Closing is idempotent
+            LoadState::ClosedOk { .. } => Ok(()),
+            LoadState::ClosedError => Ok(()),
+        }
+    }
+
+    fn read_stream_sync(
+        &self,
+        stream: &gio::InputStream,
+        cancellable: Option<&gio::Cancellable>,
+    ) -> Result<(), LoadingError> {
+        let state = self.load_state.borrow_mut();
+
+        match *state {
+            LoadState::Start => self.read_stream(state, stream, cancellable),
+            LoadState::Loading { .. } | LoadState::ClosedOk { .. } | LoadState::ClosedError => {
+                panic!(
+                "handle must not be already loaded in order to call rsvg_handle_read_stream_sync()",
+            )
+            }
+        }
+    }
+
+    fn read_stream(
+        &self,
+        mut load_state: RefMut<LoadState>,
+        stream: &gio::InputStream,
+        cancellable: Option<&gio::Cancellable>,
+    ) -> Result<(), LoadingError> {
+        match Handle::from_stream(&self.load_options(), stream, cancellable) {
+            Ok(handle) => {
+                *load_state = LoadState::ClosedOk { handle };
+                Ok(())
+            }
+
+            Err(e) => {
+                *load_state = LoadState::ClosedError;
+                Err(e)
+            }
+        }
+    }
+
+    fn get_handle_ref(&self) -> Result<Ref<Handle>, RenderingError> {
+        let state = self.load_state.borrow();
+
+        match *state {
+            LoadState::Start => {
+                rsvg_g_warning("Handle has not been loaded");
+                Err(RenderingError::HandleIsNotLoaded)
+            }
+
+            LoadState::Loading { .. } => {
+                rsvg_g_warning("Handle is still loading; call rsvg_handle_close() first");
+                Err(RenderingError::HandleIsNotLoaded)
+            }
+
+            LoadState::ClosedError => {
+                rsvg_g_warning(
+                    "Handle could not read or parse the SVG; did you check for errors during \
+                     the loading stage?",
+                );
+                Err(RenderingError::HandleIsNotLoaded)
+            }
+
+            LoadState::ClosedOk { .. } => Ok(Ref::map(state, |s| match *s {
+                LoadState::ClosedOk { ref handle } => handle,
+                _ => unreachable!(),
+            })),
+        }
+    }
+
+    fn has_sub(&self, id: &str) -> Result<bool, RenderingError> {
+        let handle = self.get_handle_ref()?;
+        handle.has_sub(id)
+    }
+
+    fn get_dimensions(&self) -> RsvgDimensionData {
+        if let Ok(handle) = self.get_handle_ref() {
+            let size_callback = self.size_callback.borrow();
+            handle.get_dimensions_no_error(self.dpi.get(), &*size_callback, self.is_testing.get())
+        } else {
+            panic!("Handle is not loaded");
+        }
+    }
+
+    fn get_dimensions_sub(&self, id: Option<&str>) -> Result<RsvgDimensionData, RenderingError> {
+        let handle = self.get_handle_ref()?;
+        let size_callback = self.size_callback.borrow();
+        handle.get_dimensions_sub(id, self.dpi.get(), &*size_callback, self.is_testing.get())
+    }
+
+    fn get_position_sub(&self, id: Option<&str>) -> Result<RsvgPositionData, RenderingError> {
+        let handle = self.get_handle_ref()?;
+        let size_callback = self.size_callback.borrow();
+        handle.get_position_sub(id, self.dpi.get(), &*size_callback, self.is_testing.get())
+    }
+
+    fn render_cairo_sub(
+        &self,
+        cr: &cairo::Context,
+        id: Option<&str>,
+    ) -> Result<(), RenderingError> {
+        let handle = self.get_handle_ref()?;
+        let size_callback = self.size_callback.borrow();
+        handle.render_cairo_sub(
+            cr,
+            id,
+            self.dpi.get(),
+            &*size_callback,
+            self.is_testing.get(),
+        )
+    }
+
+    fn get_pixbuf_sub(&self, id: Option<&str>) -> Result<Pixbuf, RenderingError> {
+        let handle = self.get_handle_ref()?;
+        let size_callback = self.size_callback.borrow();
+        handle.get_pixbuf_sub(id, self.dpi.get(), &*size_callback, self.is_testing.get())
+    }
+
+    fn get_geometry_for_element(
+        &self,
+        id: Option<&str>,
+        viewport: &cairo::Rectangle,
+    ) -> Result<(RsvgRectangle, RsvgRectangle), RenderingError> {
+        let handle = self.get_handle_ref()?;
+        handle.get_geometry_for_element(id, viewport, self.dpi.get(), self.is_testing.get())
+    }
+
+    fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
+        let handle = self.get_handle_ref().unwrap();
+        handle.get_intrinsic_dimensions()
+    }
+
+    fn set_testing(&self, is_testing: bool) {
+        self.is_testing.set(is_testing);
+    }
 }
 
 pub fn get_rust_handle<'a>(handle: *const RsvgHandle) -> &'a CHandle {
@@ -700,7 +893,7 @@ pub unsafe extern "C" fn rsvg_rust_handle_set_testing(
 ) {
     let rhandle = get_rust_handle(raw_handle);
 
-    rhandle.handle.set_testing(from_glib(testing));
+    rhandle.set_testing(from_glib(testing));
 }
 
 #[no_mangle]
@@ -712,17 +905,10 @@ pub unsafe extern "C" fn rsvg_rust_handle_read_stream_sync(
 ) -> glib_sys::gboolean {
     let rhandle = get_rust_handle(handle);
 
-    if rhandle.handle.load_state() != LoadState::Start {
-        panic!("handle must not be already loaded in order to call rsvg_handle_read_stream_sync()",);
-    }
-
     let stream = from_glib_none(stream);
     let cancellable: Option<gio::Cancellable> = from_glib_none(cancellable);
 
-    match rhandle
-        .handle
-        .read_stream_sync(&rhandle.load_options(), &stream, cancellable.as_ref())
-    {
+    match rhandle.read_stream_sync(&stream, cancellable.as_ref()) {
         Ok(()) => true.to_glib(),
 
         Err(e) => {
@@ -739,16 +925,8 @@ pub unsafe extern "C" fn rsvg_rust_handle_write(
     count: usize,
 ) {
     let rhandle = get_rust_handle(handle);
-
-    let load_state = rhandle.handle.load_state();
-
-    if !(load_state == LoadState::Start || load_state == LoadState::Loading) {
-        panic!("handle must not be closed in order to write to it");
-    }
-
     let buffer = slice::from_raw_parts(buf, count);
-
-    rhandle.handle.write(buffer);
+    rhandle.write(buffer);
 }
 
 #[no_mangle]
@@ -758,7 +936,7 @@ pub unsafe extern "C" fn rsvg_rust_handle_close(
 ) -> glib_sys::gboolean {
     let rhandle = get_rust_handle(handle);
 
-    match rhandle.handle.close(&rhandle.load_options()) {
+    match rhandle.close() {
         Ok(()) => true.to_glib(),
 
         Err(e) => {
@@ -780,7 +958,7 @@ pub unsafe extern "C" fn rsvg_rust_handle_has_sub(
 
     let id: String = from_glib_none(id);
     // FIXME: return a proper error code to the public API
-    rhandle.handle.has_sub(&id).unwrap_or(false).to_glib()
+    rhandle.has_sub(&id).unwrap_or(false).to_glib()
 }
 
 #[no_mangle]
@@ -793,14 +971,7 @@ pub unsafe extern "C" fn rsvg_rust_handle_render_cairo_sub(
     let cr = from_glib_none(cr);
     let id: Option<String> = from_glib_none(id);
 
-    let size_callback = rhandle.size_callback.borrow();
-
-    match rhandle.handle.render_cairo_sub(
-        &cr,
-        id.as_ref().map(String::as_str),
-        rhandle.dpi.get(),
-        &*size_callback,
-    ) {
+    match rhandle.render_cairo_sub(&cr, id.as_ref().map(String::as_str)) {
         Ok(()) => true.to_glib(),
 
         Err(_) => {
@@ -818,13 +989,7 @@ pub unsafe extern "C" fn rsvg_rust_handle_get_pixbuf_sub(
     let rhandle = get_rust_handle(handle);
     let id: Option<String> = from_glib_none(id);
 
-    let size_callback = rhandle.size_callback.borrow();
-
-    match rhandle.handle.get_pixbuf_sub(
-        id.as_ref().map(String::as_str),
-        rhandle.dpi.get(),
-        &*size_callback,
-    ) {
+    match rhandle.get_pixbuf_sub(id.as_ref().map(String::as_str)) {
         Ok(pixbuf) => pixbuf.to_glib_full(),
         Err(_) => ptr::null_mut(),
     }
@@ -836,12 +1001,7 @@ pub unsafe extern "C" fn rsvg_rust_handle_get_dimensions(
     dimension_data: *mut RsvgDimensionData,
 ) {
     let rhandle = get_rust_handle(handle);
-
-    let size_callback = rhandle.size_callback.borrow();
-
-    *dimension_data = rhandle
-        .handle
-        .get_dimensions_no_error(rhandle.dpi.get(), &*size_callback);
+    *dimension_data = rhandle.get_dimensions();
 }
 
 #[no_mangle]
@@ -854,13 +1014,7 @@ pub unsafe extern "C" fn rsvg_rust_handle_get_dimensions_sub(
 
     let id: Option<String> = from_glib_none(id);
 
-    let size_callback = rhandle.size_callback.borrow();
-
-    match rhandle.handle.get_dimensions_sub(
-        id.as_ref().map(String::as_str),
-        rhandle.dpi.get(),
-        &*size_callback,
-    ) {
+    match rhandle.get_dimensions_sub(id.as_ref().map(String::as_str)) {
         Ok(dimensions) => {
             *dimension_data = dimensions;
             true.to_glib()
@@ -890,13 +1044,7 @@ pub unsafe extern "C" fn rsvg_rust_handle_get_position_sub(
 
     let id: Option<String> = from_glib_none(id);
 
-    let size_callback = rhandle.size_callback.borrow();
-
-    match rhandle.handle.get_position_sub(
-        id.as_ref().map(String::as_str),
-        rhandle.dpi.get(),
-        &*size_callback,
-    ) {
+    match rhandle.get_position_sub(id.as_ref().map(String::as_str)) {
         Ok(position) => {
             *position_data = position;
             true.to_glib()
@@ -962,13 +1110,7 @@ pub unsafe extern "C" fn rsvg_rust_handle_new_from_gfile_sync(
     let res = file
         .read(cancellable.as_ref())
         .map_err(|e| LoadingError::from(e))
-        .and_then(|stream| {
-            rhandle.handle.read_stream_sync(
-                &rhandle.load_options(),
-                &stream.upcast(),
-                cancellable.as_ref(),
-            )
-        });
+        .and_then(|stream| rhandle.read_stream_sync(&stream.upcast(), cancellable.as_ref()));
 
     match res {
         Ok(()) => raw_handle,
@@ -1001,11 +1143,7 @@ pub unsafe extern "C" fn rsvg_rust_handle_new_from_stream_sync(
     let stream = from_glib_none(input_stream);
     let cancellable: Option<gio::Cancellable> = from_glib_none(cancellable);
 
-    match rhandle.handle.read_stream_sync(
-        &rhandle.load_options(),
-        &stream,
-        cancellable.as_ref(),
-    ) {
+    match rhandle.read_stream_sync(&stream, cancellable.as_ref()) {
         Ok(()) => raw_handle,
 
         Err(e) => {
@@ -1081,11 +1219,7 @@ pub unsafe extern "C" fn rsvg_rust_handle_get_intrinsic_dimensions(
 ) {
     let rhandle = get_rust_handle(handle);
 
-    if rhandle.handle.check_is_loaded().is_err() {
-        return;
-    }
-
-    let d = rhandle.handle.get_intrinsic_dimensions();
+    let d = rhandle.get_intrinsic_dimensions();
 
     let w = d.width.map(|l| l.to_length());
     let h = d.height.map(|l| l.to_length());
@@ -1107,17 +1241,9 @@ pub unsafe extern "C" fn rsvg_rust_handle_get_geometry_for_element(
 ) -> glib_sys::gboolean {
     let rhandle = get_rust_handle(handle);
 
-    if rhandle.handle.check_is_loaded().is_err() {
-        return false.to_glib();
-    }
-
     let id: Option<String> = from_glib_none(id);
 
-    match rhandle.handle.get_geometry_for_element(
-        id.as_ref().map(String::as_str),
-        &viewport.into(),
-        rhandle.dpi.get(),
-    ) {
+    match rhandle.get_geometry_for_element(id.as_ref().map(String::as_str), &viewport.into()) {
         Ok((ink_rect, logical_rect)) => {
             if !out_ink_rect.is_null() {
                 *out_ink_rect = ink_rect;
diff --git a/rsvg_internals/src/handle.rs b/rsvg_internals/src/handle.rs
index aaedb36f..a2c7f50a 100644
--- a/rsvg_internals/src/handle.rs
+++ b/rsvg_internals/src/handle.rs
@@ -1,10 +1,9 @@
-use std::cell::{Cell, RefCell};
 use std::rc::Rc;
 
 use cairo::{self, ImageSurface, Status};
 use gdk_pixbuf::Pixbuf;
 use gio;
-use glib::{self, Bytes, Cast};
+use glib;
 use libc;
 use locale_config::{LanguageRange, Locale};
 
@@ -63,124 +62,22 @@ impl LoadOptions {
     }
 }
 
-#[derive(Copy, Clone, PartialEq)]
-pub enum LoadState {
-    Start,
-    Loading,
-    ClosedOk,
-    ClosedError,
-}
-
 pub struct Handle {
-    svg: RefCell<Option<Rc<Svg>>>,
-    load_state: Cell<LoadState>,
-    buffer: RefCell<Vec<u8>>, // used by the legacy write() api
-    is_testing: Cell<bool>,
+    svg: Rc<Svg>,
 }
 
 impl Handle {
-    pub fn new() -> Handle {
-        Handle {
-            svg: RefCell::new(None),
-            load_state: Cell::new(LoadState::Start),
-            buffer: RefCell::new(Vec::new()),
-            is_testing: Cell::new(false),
-        }
-    }
-
-    fn get_svg(&self) -> Rc<Svg> {
-        // This assumes that the Svg is already loaded, or unwrap() will panic
-        self.svg.borrow().as_ref().unwrap().clone()
-    }
-
-    pub fn read_stream_sync(
-        &self,
+    pub fn from_stream(
         load_options: &LoadOptions,
         stream: &gio::InputStream,
         cancellable: Option<&gio::Cancellable>,
-    ) -> Result<(), LoadingError> {
-        self.load_state.set(LoadState::Loading);
-
-        let svg = Svg::load_from_stream(load_options, stream, cancellable).map_err(|e| {
-            self.load_state.set(LoadState::ClosedError);
-            e
-        })?;
-
-        *self.svg.borrow_mut() = Some(Rc::new(svg));
-        self.load_state.set(LoadState::ClosedOk);
-        Ok(())
-    }
-
-    pub fn check_is_loaded(self: &Handle) -> Result<(), RenderingError> {
-        match self.load_state.get() {
-            LoadState::Start => {
-                rsvg_g_warning("RsvgHandle has not been loaded");
-                Err(RenderingError::HandleIsNotLoaded)
-            }
-
-            LoadState::Loading => {
-                rsvg_g_warning("RsvgHandle is still loading; call rsvg_handle_close() first");
-                Err(RenderingError::HandleIsNotLoaded)
-            }
-
-            LoadState::ClosedOk => Ok(()),
-
-            LoadState::ClosedError => {
-                rsvg_g_warning(
-                    "RsvgHandle could not read or parse the SVG; did you check for errors during \
-                     the loading stage?",
-                );
-                Err(RenderingError::HandleIsNotLoaded)
-            }
-        }
-    }
-
-    pub fn load_state(&self) -> LoadState {
-        self.load_state.get()
-    }
-
-    pub fn write(&self, buf: &[u8]) {
-        match self.load_state.get() {
-            LoadState::Start => self.load_state.set(LoadState::Loading),
-            LoadState::Loading => (),
-            _ => unreachable!(),
-        };
-
-        self.buffer.borrow_mut().extend_from_slice(buf);
-    }
-
-    pub fn close(&self, load_options: &LoadOptions) -> Result<(), LoadingError> {
-        let res = match self.load_state.get() {
-            LoadState::Start => {
-                self.load_state.set(LoadState::ClosedError);
-                Err(LoadingError::NoDataPassedToParser)
-            }
-
-            LoadState::Loading => {
-                let buffer = self.buffer.borrow();
-                let bytes = Bytes::from(&*buffer);
-                let stream = gio::MemoryInputStream::new_from_bytes(&bytes);
-
-                self.read_stream_sync(load_options, &stream.upcast(), None)
-            }
-
-            LoadState::ClosedOk | LoadState::ClosedError => {
-                // closing is idempotent
-                Ok(())
-            }
-        };
-
-        assert!(
-            self.load_state.get() == LoadState::ClosedOk
-                || self.load_state.get() == LoadState::ClosedError
-        );
-
-        res
+    ) -> Result<Handle, LoadingError> {
+        Ok(Handle {
+            svg: Rc::new(Svg::load_from_stream(load_options, stream, cancellable)?),
+        })
     }
 
     pub fn has_sub(&self, id: &str) -> Result<bool, RenderingError> {
-        self.check_is_loaded()?;
-
         match self.lookup_node(id) {
             Ok(_) => Ok(true),
 
@@ -194,9 +91,8 @@ impl Handle {
         &self,
         dpi: Dpi,
         size_callback: &SizeCallback,
+        is_testing: bool,
     ) -> Result<RsvgDimensionData, RenderingError> {
-        self.check_is_loaded()?;
-
         // This function is probably called from the cairo_render functions,
         // or is being erroneously called within the size_func.
         // To prevent an infinite loop we are saving the state, and
@@ -212,7 +108,7 @@ impl Handle {
 
         size_callback.start_loop();
 
-        let res = self.get_dimensions_sub(None, dpi, size_callback);
+        let res = self.get_dimensions_sub(None, dpi, size_callback, is_testing);
 
         size_callback.end_loop();
 
@@ -223,8 +119,9 @@ impl Handle {
         &self,
         dpi: Dpi,
         size_callback: &SizeCallback,
+        is_testing: bool,
     ) -> RsvgDimensionData {
-        match self.get_dimensions(dpi, size_callback) {
+        match self.get_dimensions(dpi, size_callback, is_testing) {
             Ok(dimensions) => dimensions,
 
             Err(_) => {
@@ -245,10 +142,9 @@ impl Handle {
         id: Option<&str>,
         dpi: Dpi,
         size_callback: &SizeCallback,
+        is_testing: bool,
     ) -> Result<RsvgDimensionData, RenderingError> {
-        self.check_is_loaded()?;
-
-        let (ink_r, _) = self.get_geometry_sub(id, dpi)?;
+        let (ink_r, _) = self.get_geometry_sub(id, dpi, is_testing)?;
 
         let (w, h) = size_callback.call(ink_r.width as libc::c_int, ink_r.height as libc::c_int);
 
@@ -265,14 +161,13 @@ impl Handle {
         id: Option<&str>,
         dpi: Dpi,
         size_callback: &SizeCallback,
+        is_testing: bool,
     ) -> Result<RsvgPositionData, RenderingError> {
-        self.check_is_loaded()?;
-
         if let None = id {
             return Ok(RsvgPositionData { x: 0, y: 0 });
         }
 
-        let (ink_r, _) = self.get_geometry_sub(id, dpi)?;
+        let (ink_r, _) = self.get_geometry_sub(id, dpi, is_testing)?;
 
         let width = ink_r.width as libc::c_int;
         let height = ink_r.height as libc::c_int;
@@ -285,6 +180,10 @@ impl Handle {
         })
     }
 
+    fn get_svg(&self) -> Rc<Svg> {
+        self.svg.clone()
+    }
+
     fn get_root(&self) -> RsvgNode {
         self.get_svg().root()
     }
@@ -295,6 +194,7 @@ impl Handle {
         node: &RsvgNode,
         viewport: &cairo::Rectangle,
         dpi: Dpi,
+        is_testing: bool,
     ) -> Result<(RsvgRectangle, RsvgRectangle), RenderingError> {
         let target = ImageSurface::create(cairo::Format::Rgb24, 1, 1)?;
         let cr = cairo::Context::new(&target);
@@ -305,7 +205,7 @@ impl Handle {
             viewport,
             dpi,
             true,
-            self.is_testing.get(),
+            is_testing,
         );
         let root = self.get_root();
 
@@ -330,6 +230,7 @@ impl Handle {
         &self,
         id: Option<&str>,
         dpi: Dpi,
+        is_testing: bool,
     ) -> Result<(RsvgRectangle, RsvgRectangle), RenderingError> {
         let node = self.get_node_or_root(id)?;
 
@@ -364,7 +265,7 @@ impl Handle {
             height: 1.0,
         };
 
-        self.get_node_geometry_with_viewport(&node, &viewport, dpi)
+        self.get_node_geometry_with_viewport(&node, &viewport, dpi, is_testing)
     }
 
     fn get_node_or_root(&self, id: Option<&str>) -> Result<RsvgNode, RenderingError> {
@@ -380,9 +281,10 @@ impl Handle {
         id: Option<&str>,
         viewport: &cairo::Rectangle,
         dpi: Dpi,
+        is_testing: bool,
     ) -> Result<(RsvgRectangle, RsvgRectangle), RenderingError> {
         let node = self.get_node_or_root(id)?;
-        self.get_node_geometry_with_viewport(&node, viewport, dpi)
+        self.get_node_geometry_with_viewport(&node, viewport, dpi, is_testing)
     }
 
     fn lookup_node(&self, id: &str) -> Result<RsvgNode, DefsLookupErrorKind> {
@@ -425,11 +327,11 @@ impl Handle {
         id: Option<&str>,
         dpi: Dpi,
         size_callback: &SizeCallback,
+        is_testing: bool,
     ) -> Result<(), RenderingError> {
         check_cairo_context(cr)?;
-        self.check_is_loaded()?;
 
-        let dimensions = self.get_dimensions(dpi, size_callback)?;
+        let dimensions = self.get_dimensions(dpi, size_callback, is_testing)?;
         if dimensions.width == 0 || dimensions.height == 0 {
             // nothing to render
             return Ok(());
@@ -442,7 +344,7 @@ impl Handle {
             height: f64::from(dimensions.height),
         };
 
-        self.render_element_to_viewport(cr, id, &viewport, dpi)
+        self.render_element_to_viewport(cr, id, &viewport, dpi, is_testing)
     }
 
     pub fn render_element_to_viewport(
@@ -451,6 +353,7 @@ impl Handle {
         id: Option<&str>,
         viewport: &cairo::Rectangle,
         dpi: Dpi,
+        is_testing: bool,
     ) -> Result<(), RenderingError> {
         check_cairo_context(cr)?;
 
@@ -470,7 +373,7 @@ impl Handle {
             viewport,
             dpi,
             false,
-            self.is_testing.get(),
+            is_testing,
         );
         let res = draw_ctx.draw_node_from_stack(&root.get_cascaded_values(), &root, false);
         cr.restore();
@@ -483,10 +386,9 @@ impl Handle {
         id: Option<&str>,
         dpi: Dpi,
         size_callback: &SizeCallback,
+        is_testing: bool,
     ) -> Result<Pixbuf, RenderingError> {
-        self.check_is_loaded()?;
-
-        let dimensions = self.get_dimensions(dpi, size_callback)?;
+        let dimensions = self.get_dimensions(dpi, size_callback, is_testing)?;
 
         if dimensions.width == 0 || dimensions.height == 0 {
             return empty_pixbuf();
@@ -497,7 +399,7 @@ impl Handle {
 
         {
             let cr = cairo::Context::new(&surface);
-            self.render_cairo_sub(&cr, id, dpi, size_callback)?;
+            self.render_cairo_sub(&cr, id, dpi, size_callback, is_testing)?;
         }
 
         let surface = SharedImageSurface::new(surface, SurfaceType::SRgb)?;
@@ -508,10 +410,6 @@ impl Handle {
     pub fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
         self.get_svg().get_intrinsic_dimensions()
     }
-
-    pub fn set_testing(&self, testing: bool) {
-        self.is_testing.set(testing);
-    }
 }
 
 fn check_cairo_context(cr: &cairo::Context) -> Result<(), RenderingError> {
diff --git a/rsvg_internals/src/pixbuf_utils.rs b/rsvg_internals/src/pixbuf_utils.rs
index bb4a58b4..1a5995cd 100644
--- a/rsvg_internals/src/pixbuf_utils.rs
+++ b/rsvg_internals/src/pixbuf_utils.rs
@@ -178,7 +178,7 @@ fn render_to_pixbuf_at_size(
             f64::from(width) / f64::from(dimensions.width),
             f64::from(height) / f64::from(dimensions.height),
         );
-        handle.render_cairo_sub(&cr, None, dpi, &SizeCallback::default())?;
+        handle.render_cairo_sub(&cr, None, dpi, &SizeCallback::default(), false)?;
     }
 
     let shared_surface = SharedImageSurface::new(surface, SurfaceType::SRgb)?;
@@ -225,25 +225,21 @@ fn pixbuf_from_file_with_size_mode(
 
         let load_options = LoadOptions::new(LoadFlags::default(), Some(base_url));
 
-        let handle = Handle::new();
         let cancellable: Option<&gio::Cancellable> = None;
-        if let Err(e) = file
+        let handle = match file
             .read(cancellable)
             .map_err(|e| LoadingError::from(e))
-            .and_then(|stream| {
-                handle.read_stream_sync(
-                    &load_options,
-                    &stream.upcast(),
-                    None,
-                )
-            })
+            .and_then(|stream| Handle::from_stream(&load_options, &stream.upcast(), None))
         {
-            set_gerror(error, 0, &format!("{}", e));
-            return ptr::null_mut();
-        }
+            Ok(handle) => handle,
+            Err(e) => {
+                set_gerror(error, 0, &format!("{}", e));
+                return ptr::null_mut();
+            }
+        };
 
         handle
-            .get_dimensions(dpi, &SizeCallback::default())
+            .get_dimensions(dpi, &SizeCallback::default(), false)
             .and_then(|dimensions| {
                 let (width, height) = get_final_size(&dimensions, size_mode);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]