using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
using System.Collections.Generic;
using System.Text;
using System.Web;
namespace Svg.Web
{
///
/// A handler to asynchronously render Scalable Vector Graphics files (usually *.svg or *.xml file extensions).
///
///
/// If a crawler requests the SVG file the raw XML will be returned rather than the image to allow crawlers to better read the image.
/// Adding "?raw=true" to the querystring will alos force the handler to render the raw SVG.
///
public class SvgHandler : IHttpAsyncHandler
{
Thread t;
///
/// Gets a value indicating whether another request can use the instance.
///
///
/// true if the instance is reusable; otherwise, false.
public bool IsReusable
{
get { return false; }
}
///
/// Enables processing of HTTP Web requests by a custom HttpHandler that implements the interface.
///
/// An object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.
public void ProcessRequest(HttpContext context)
{
// Not used
}
///
/// Initiates an asynchronous call to the HTTP handler.
///
/// An object that provides references to intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.
/// The to call when the asynchronous method call is complete. If is null, the delegate is not called.
/// Any extra data needed to process the request.
///
/// An that contains information about the status of the process.
///
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
string path = context.Request.PhysicalPath;
if (!File.Exists(path))
{
throw new HttpException(404, "The requested file cannot be found.");
}
SvgAsyncRenderState reqState = new SvgAsyncRenderState(context, cb, extraData);
SvgAsyncRender asyncRender = new SvgAsyncRender(reqState);
ThreadStart ts = new ThreadStart(asyncRender.RenderSvg);
t = new Thread(ts);
t.Start();
return reqState;
}
///
/// Provides an asynchronous process End method when the process ends.
///
/// An that contains information about the status of the process.
public void EndProcessRequest(IAsyncResult result)
{
}
///
/// The class to be used when
///
protected sealed class SvgAsyncRender
{
private SvgAsyncRenderState _state;
public SvgAsyncRender(SvgAsyncRenderState state)
{
this._state = state;
}
private void RenderRawSvg()
{
this._state._context.Response.ContentType = "image/svg+xml";
this._state._context.Response.WriteFile(this._state._context.Request.PhysicalPath);
this._state._context.Response.End();
this._state.CompleteRequest();
}
public void RenderSvg()
{
this._state._context.Response.AddFileDependency(this._state._context.Request.PhysicalPath);
this._state._context.Response.Cache.SetLastModifiedFromFileDependencies();
this._state._context.Response.Cache.SetETagFromFileDependencies();
this._state._context.Response.Buffer = false;
// Allow crawlers to see the raw XML - they can get more information from it that way
if (this._state._context.Request.Browser.Crawler || !string.IsNullOrEmpty(this._state._context.Request.QueryString["raw"]))
{
this.RenderRawSvg();
}
else
{
try
{
SvgDocument document = SvgDocument.Open(this._state._context.Request.PhysicalPath);
using (Bitmap bitmap = document.Draw())
{
using (MemoryStream ms = new MemoryStream())
{
bitmap.Save(ms, ImageFormat.Png);
this._state._context.Response.ContentType = "image/png";
ms.WriteTo(this._state._context.Response.OutputStream);
}
}
}
catch (Exception exc)
{
System.Diagnostics.Trace.TraceError("An error occured while attempting to render the SVG image '" + this._state._context.Request.PhysicalPath + "': " + exc.Message);
}
finally
{
this._state._context.Response.End();
this._state.CompleteRequest();
}
}
}
}
///
/// Represents the state of a request for SVG rendering.
///
protected sealed class SvgAsyncRenderState : IAsyncResult
{
internal HttpContext _context;
internal AsyncCallback _callback;
internal object _extraData;
private bool _isCompleted = false;
private ManualResetEvent _callCompleteEvent = null;
///
/// Initializes a new instance of the class.
///
/// The of the request.
/// The delegate to be called when the rendering is complete.
/// The extra data.
public SvgAsyncRenderState(HttpContext context, AsyncCallback callback, object extraData)
{
_context = context;
_callback = callback;
_extraData = extraData;
}
///
/// Indicates that the rendering is complete and the waiting thread may proceed.
///
internal void CompleteRequest()
{
_isCompleted = true;
lock (this)
{
if (this.AsyncWaitHandle != null)
{
this._callCompleteEvent.Set();
}
}
// if a callback was registered, invoke it now
if (_callback != null)
{
_callback(this);
}
}
///
/// Gets a user-defined object that qualifies or contains information about an asynchronous operation.
///
///
/// A user-defined object that qualifies or contains information about an asynchronous operation.
public object AsyncState { get { return (_extraData); } }
///
/// Gets an indication of whether the asynchronous operation completed synchronously.
///
///
/// true if the asynchronous operation completed synchronously; otherwise, false.
public bool CompletedSynchronously { get { return (false); } }
///
/// Gets an indication whether the asynchronous operation has completed.
///
///
/// true if the operation is complete; otherwise, false.
public bool IsCompleted { get { return (_isCompleted); } }
///
/// Gets a that is used to wait for an asynchronous operation to complete.
///
///
/// A that is used to wait for an asynchronous operation to complete.
public WaitHandle AsyncWaitHandle
{
get
{
lock (this)
{
if (_callCompleteEvent == null)
{
_callCompleteEvent = new ManualResetEvent(false);
}
return _callCompleteEvent;
}
}
}
}
}
}