I would like to save an SVGDocument to a stream rather than a file. This is for sending an svg modified at runtime to the browser. Is there a way to do this?
If I render the document into an image format (png for example), I can output to a stream directly:
ImageRenderingOptions imageOptions = new ImageRenderingOptions(ImageFormat.Png);
MemoryStream ms = new MemoryStream();
using (ImageDevice device = new ImageDevice(imageOptions, ms))
{
document.RenderTo(device);
}
return ms;
But to save the svg document itself, I need to save to a temporary file and then read the file into a stream, like this. This is a noticeable performance hit when serving multiple svg images to a web page.
// assume myStream contains my SVG document to be modified at runtime
SVGDocument document = new SVGDocument(myStream, "")
// do things to the document here, then...
document.Save(someTemporaryFilePath);
// return a stream
return System.IO.File.OpenRead(someTemporaryFilePath);
The Save method does not have an overload to save to a stream.
We need to investigate the feasibility of support to save the output to stream. For the purpose, we have logged an enhancement request as SVG-92 in our issue tracking system. We will further investigate possibility of this feature and let you know as soon as it is available. Please be patient and spare us some time.
The earlier logged ticket is currently under the investigation phase and we surely intend to provide the requested feature in the near future. We will soon provide an update in this forum thread once the ticket is resolved. Please give us some time.
We would like to share with you that the requested feature has been implemented in 21.1v of the API. A new interface IOutputStorage has been developed for saving SVG content and resources to streams. All new interfaces, classes and methods were enumerated here.
We attached a project SvgSaveToStorage.zip which contains samples of using IOutputStorage:
saving SVG document to files with a new class LocalFileSystemStorage (it was added to new API)
developed a sample class ZipStorage to demonstrate saving SVG document to ZIP archive
This works, but it uses the file system, slows performance, and requires deleting the file once the stream is no longer needed. In our actual production environment, this would be done hundreds or thousands of times a day.
I thought something simple like this would make sense: overload the SvgDocument.Save method to accept a reference to a stream.
using (MemoryStream ms = new MemoryStream())
{
document.Save(ms);
// do stuff here with the stream
}
Your example project shows how to save to a Zip archive using your ZipStorage class that inherits from IOutputStorage. But, this still writes to the file system (the Zip archive file.)
If I write my own class that inherits from IOutputStorage, how could I write a CreateStream() method so that it gets the contents of the SvgDocument directly? In your ZipStorage class, seemingly by “magic” the SvgDocument.Save action knew to put the document in the archive.
Or is there no way to do this without first saving the document to a file? Confused…
Unfortunately, we cannot use only one stream for saving SVG documents, like in this example “document.Save(memoryStream)”. Because, SVG documents can contain different resources like CSS, external images, and files and we need to provide a way to save them all.
So, we declared the interface IOutputStorage for user implementation. It contains two methods to manage streams.
CreateStream - here user can allocate any kind of stream (it can be a memory stream of course) and give it to us.
ReleaseStream - this method is called to inform the user that we have finished working with the stream and he can save or dispose of it.
We use this simple class in our tests:
internal class MemoryOutputStorage : IOutputStorage
{
public List<OutputStream> Streams;
public MemoryOutputStorage()
{
Streams = new List<OutputStream>();
}
public OutputStream CreateStream(OutputStreamContext context)
{
var normalizedPath =new Url(context.Uri).Pathname;
var outputStream = new OutputStream(new MemoryStream(), new Url(Path.GetFileName(normalizedPath), "file:///").Href);
Streams.Add(outputStream);
return outputStream;
}
public void ReleaseStream(OutputStream stream)
{
stream.Flush();
}
}
After document saving, all memory streams are collected in the list Streams. You can use a dictionary instead of the list to identify every stream by its URI. Furthermore, we are going to update the documentation to share samples and descriptions of using these interfaces and classes.