Posts Tagged ‘http’

Resize images for mobile web

January 13, 2009 2 comments

If you decide that your mobile website won’t be nothing more than a translation of your website, then you have to look into resizing the images too. There are pretty good reasons for doing it:

  • mobile devices supports images only up to a certain file size
  • mobile devices supports images only up to a certain image width and height
  • mobile devices supports web pages only up to a certain total size (page + resources)
  • performance, when it comes to bandwidth
  • user experience, when it comes to rendering capabilities

Next, I will present a short and practical solution, implemented as an aspx page. The page is taking only one parameter (i), which is the URL of the original image, downloads it, resizes it and sends it back to the client. This can also be implemented as an HTTP filter as well: every time an image is requested, it gets the original image, resizes it and pushes it into the response. The advantage as a page is that you can process this way, even images from other servers.
As for how to recognize the user mobile device, we will use my favorite, WURFL. For more information see Mobile device recognition.

I will include here only the C# code with enough comments so I won’t need to say anything else. 😀
But before, one more thing: what is the new size of the image? First of all we need the maximum image width and height supported by the user mobile device. Here, WURFL is the solution. Then I made the supposition that the images are optimized for 800×600 and I will reduce the image proportionally to the maximum width and height supported by the mobile device. To not give anomalies, I also introduce a minimum width and height, not to go below.

public partial class MobileImageResizer : System.Web.UI.Page
        // image should not be reduce under this width
        private static int MIN_WIDTH = 24;
        // image should not be reduce under this height
        private static int MIN_HEIGHT = 24;
        // images on the desktop version are optimized for this width
        private static int OPTIMIZED_WIDTH = 800;
        // images on the desktop version are optimized for this height
        private static int OPTIMIZED_HEIGHT = 600;

        protected void Page_Load(object sender, EventArgs e)
            HttpContext context = HttpContext.Current;
            // get the image URL
            String url = context.Request.QueryString.Get("u");

            // if no URL is specified in the u parameter then send a NOT_FOUND error
            if (url == null)
                Utils.Send(context.Response, HttpStatusCode.NotFound);
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            // specify a timeotu for the request
            // request.Timeout = 10000; // 10s
            // if you want use a proxy for request
            // request.Proxy = WebProxy;
            HttpWebResponse initialResponse = (HttpWebResponse)request.GetResponse();
            // if something went wrong don't try to transform
            if (initialResponse.StatusCode != HttpStatusCode.OK)
                throw new HttpException((int)initialResponse.StatusCode, initialResponse.StatusDescription);

            context.Response.ContentType = initialResponse.ContentType;
            Image image = Bitmap.FromStream(initialResponse.GetResponseStream());

            // get the maximum image size from the WURFL database
            // see my other article about <a href="">WURFL filter</a>
            int maxWidth = ...;
            int maxHeight = ...;

            // calculate the resize ratio
            double ratio = Math.Min( // final ratio
                    Math.Max( // final width ratio
                        ((double)maxWidth) / Math.Max(image.Width, OPTIMIZED_WIDTH), // width ratio
                        ((double)MIN_WIDTH) / image.Width // minimum width ratio
                    Math.Max( // final height ratio
                        ((double)maxHeight) / Math.Max(image.Height, OPTIMIZED_HEIGHT), // height ratio
                        ((double)MIN_HEIGHT) / image.Height // minimum height ratio

            if (ratio &lt; 1)
                // if we have to reduce the image
                int newWidth = (int)(image.Width * ratio);
                int newHeight = (int) (image.Height * ratio);

                Image newImage = new Bitmap(newWidth, newHeight, image.PixelFormat);
                Graphics g = Graphics.FromImage(newImage);
                g.CompositingQuality = CompositingQuality.HighQuality;
                g.SmoothingMode = SmoothingMode.HighQuality;
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                Rectangle r = new Rectangle(0, 0, newWidth, newHeight);
                g.DrawImage(image, r);
                newImage.Save(context.Response.OutputStream, image.RawFormat);
                // otherwise simply send the image
                // we don't make bigger the smaller images
                image.Save(context.Response.OutputStream, image.RawFormat);

            // finish
Categories: Software, Web Tags: , , ,

Mobile device filter in

September 4, 2008 5 comments

The mobile web is increasing rapidly. Creating different web sites for mobile users is not a solution, rather having the same content, but customized through CSS for different browsers and devices. But this article will not focus on this part, but rather on what is before: recognizing the browser/device of the user so you can choose the right CSS file(s).

For recognizing the devices we will use WURFL, on open source database containing device capabilities. I will also not cover here the mobile device recognition topic.

What I will describe, will be an HTTP filter for pages that will try to recognize the mobile device and store it in the context items for future use. Of course you can add your own custom tasks to it (e.g. redirect to another page if a page is not available for mobile devices or select automatically the right CSS).

The filter is implemented as an HTTP module that is executed before the actual request is processed. Then it matches the User Agent string against the WURFL database for getting the device id. Because some mobile providers are rewriting the User Agent and putting the information into the X-Device-User-Agent HTTP header we will also look into that one as well. (Later edit) And just to be sure that you also recognize Opera Mini (and actually the real device) you can inspect the information in the X-OperaMini-Phone-UA header. As a consequence the filter will use a list of headers.

And now the code:

public class WURFLFilter : IHttpModule
    /// The HTTP headers inspected for the User Agent string. The headers are inspected in this order.
    /// User-Agent - the standard header containing the user agent string
    /// x-device-user-agent - some mobile providers overwrite the User Agent,
    ///     but they put the real user agent in this header
    /// X-OperaMini-Phone-UA - Opera mini overwrites the standard header, 
    ///     but moves the information in this header
    private static String[] UA_HEADERS = new String[] 

    private HttpApplication httpApp;
    private deviceFileProcessor wurfl
            // get the WURFL processor from cache or load it from file
            deviceFileProcessor d = (deviceFileProcessor)httpApp.Context.Cache["wurfl"];
            if (d == null)
                String wurflPath = httpApp.Server.MapPath(ConfigurationSettings.AppSettings["wurfl.path"]);
                d = new deviceFileProcessor(wurflPath);
                httpApp.Context.Cache.Insert("wurfl", d, new CacheDependency(wurflPath));
            return d;

    public void Init(HttpApplication httpApp)
        this.httpApp = httpApp;
        httpApp.PostAcquireRequestState += new EventHandler(OnRequest);

    void OnRequest(object sender, EventArgs a)
        HttpContext context = ((HttpApplication)sender).Context;
        String mobileDeviceId = null;
        Hashtable userAgents = wurfl.getsdUserAgentAndId;

        for (var i = 0; i < UA_HEADERS.Length; i++)
            String ua = context.Request.Headers[UA_HEADERS[i]];
            mobileDeviceId = (String)(String.IsNullOrEmpty(ua) ? null : userAgents[ua]);
            if (mobileDeviceId != null)
        context.Items.Add("mobileDeviceId", mobileDeviceId);

    public void Dispose()


Some improvements can be made, like caching the mobileDeviceId into the session (we suppose that you cannot change your device during the session 🙂 ). You probably noticed that I cached the WURFL file processor (the WURFL data file is quite big and it doesn’t make sense to read it every time). The cache dependency is made on the WURFL data file (the path is stored into application settings under the wurfl.path key) so you can update the file without any worries.

Categories: Software, Web Tags: , ,

XSLT handler in

September 4, 2008 4 comments

Next I will explain you a solution that may come handy to you. First of all, the problem. Quite simple: you have some HTML pages, that you would like to include them in your pages, but in a different format/layout. Unfortunately you cannot modify the pages as you’re not the author. Just to understand better, let’s say that you want to include some Wikipedia articles in your pages. But they have to match with your own layout and you want only some sections of them.
So you have to transform an HTML into another. This sounds pretty much like XSLT and this is what we will use. It will be your task to write the XSLT. I’m only going to tell you how to integrate in your web application.

For the sake of simplicity and reuse, we will write an handler for XSLT file that it will take the XSLT file and receive as a parameter the XML/HTML/XHTML URL and it will output the transformed result.

Find below the code for it:

public class XSLTHandler : IHttpHandler
    // process the request
    public void ProcessRequest(HttpContext context)
        // the XSLT argument list
        XsltArgumentList xslArg = new XsltArgumentList();

        String url = context.Request.QueryString.Get("u");
        // if no URL, either special or from u parameter then send a NOT_FOUND error
        if (url == null)
            send(context.Response, HttpStatusCode.NotFound);

        // adding the URL as an XSLT parameter
        xslArg.AddParam("url", "",  url);

            // load the XML document at the specified URL
            XmlDocument xmlDoc = loadXMLDocument(url,
                    ? context.Request.Form.ToString() : null);

            // load xsl
            String xslPath = context.Request.MapPath(context.Request.Url.LocalPath);

            // try to get the compiled XSLT from cache
            XslCompiledTransform xslTransform = (XslCompiledTransform)context.Cache.Get(xslPath);
            if (xslTransform == null)
                // if the XSLT file doesn't exist, send a 404
                if (!File.Exists(xslPath))
                    send(context.Response, HttpStatusCode.NotFound);


                // put the XSLT into cache
                context.Cache.Insert(xslPath, xslTransform, new CacheDependency(xslPath));

            // transform
            context.Response.ContentType = "text/html";
            xslTransform.Transform(xmlDoc, xslArg, context.Response.Output);

        catch (HttpException exc)
            if (exc.GetHttpCode() == (int) HttpStatusCode.Redirect)
                // if it is to be redirected, redirect to the url
                // if any other error, then send the error code
                send(context.Response, (HttpStatusCode)exc.GetHttpCode());
        catch (Exception)
            // if other error send an internal server error response
            send(context.Response, HttpStatusCode.InternalServerError);

    // IHttpHandler method
    public bool IsReusable
            return false;

    private void send(HttpResponse Response, HttpStatusCode StatusCode)
        Response.StatusCode = (int)StatusCode;
        Response.StatusDescription = StatusCode.ToString();

    /// Load and returns an XML document from the given url.
    /// the url where is the XML content
    /// if not null the request will be a POST request 
    /// and the parameters are sent along with the request; if null then the request method is GET
    /// the loaded XML document
    public static XmlDocument loadXMLDocument(String url, String postParams)
        Object reader;
        if (url.StartsWith("/") || (!url.Contains(Uri.SchemeDelimiter)))
            // if it is a local request then use the internal execute instead of making a web request
            // also the form and query string parameters are preserved
            StringWriter writer = new StringWriter();
            HttpContext.Current.Server.Execute(url, writer, true);
            reader = new StringReader(writer.ToString());
            // create webrequest
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Timeout = RequestTimeout;
            // you can also add a proxy
            //request.Proxy = WebProxy;
            if (postParameters != null)
                // Set values for the request back
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = postParameters.Length;

                // Write the request
                StreamWriter stOut = new StreamWriter(request.GetRequestStream(), System.Text.Encoding.ASCII);

            // get the response of the request
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            // if something went wrong don't try to parse the document
            if (response.StatusCode != HttpStatusCode.OK)
                throw new HttpException((int)response.StatusCode, response.StatusDescription);

            if (response.ContentType.StartsWith("text/html"))
                // if it is HTML transform it with SgmlReader
                // create instance of SGMLReader and change settings
                SgmlReader sgmlReader = new SgmlReader();
                sgmlReader.WebProxy = Configuration.Proxy;
                sgmlReader.DocType = "HTML";
                sgmlReader.CaseFolding = CaseFolding.ToLower;
                sgmlReader.InputStream = new StreamReader(response.GetResponseStream(), Encoding.UTF8, true);

                reader = sgmlReader;
            else if (response.ContentType.StartsWith("text/xml"))
                // if not html simply get it from the response
                reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8, true);
                // redirect to the normal page, as it is nor HTML nor XML
                throw new HttpException((int)HttpStatusCode.Redirect, url);
        // load the XML document
        XmlDocument xmlDoc = new XmlDocument();
        if (reader is TextReader)
        else if (reader is XmlReader)
        return xmlDoc;

To explain shortly what happens: when a file like t.xslt is requested, the XSLTHandler will process the request. First it will load and compile the XSLT file using XSLTCompiledTransform, trying first to load the XSLT compiled object from cache.
Next step is to retrieve the XML/XHTML/HTML content from the URL given as the url parameter. If the content is HTML, then it will be transformed using an SGML parser into XHTML, so it can be transformed with XSLT. Then the actual processing takes place and the result is generated into the response output.

Of course the above code can be extended. You can put in the app configuration if you want to use the cache, what proxy to use, if you want to allow only URLs from some hosts to be parsed and so on. Also other parameters can be added as XSLT arguments: the URL of the XSLT file, the HTTP headers, the request parameters etc.

You saw in the code references to an SgmlReader and I also told you above about feeding the HTML content first into an SGML parser. But how to do it? The SgmlReader is already implemented and you can download it from here and use it like an XmlReader.

Last step is to configure the handler into the Web.config, by simply adding the handler to process *.xsl files:

        <add verb="*" path="*.xsl" type="XSLTHandler" />

That’s it!

Categories: Software, Web Tags: , , ,