Archive

Posts Tagged ‘sample’

ActionFilters in MVC

October 20th, 2009 No comments

I’ve been working on two fairly large MVC-projects lately. ImagerGallery.Net and a webshop-application suitably named BeaverShop. While working on those projects I’ve realized that I just love being able to use ActionFilters in MVC. Basically, they are attributes that you append to action-methods. They can be used to validate input, parse data into more suitable structures, check security and lots more.

One of my most used filters in both ImagerGallery and BeaverShop is one I named PagingAttribute which I thought I should show to you today.

Any ActionFilter need to inherit the ActionFilterAttribute and implement at least one of the following methods: OnActionExecuting, OnActionExecuted, OnResultExecuting or OnResultExecuted. They are all called during different stages of the execution of an action-method and can be used for various things. The one I’ll be using is OnActionExecuting since it is executed before the action so that I can pass extra arguments to the action.

The code is quite easy to understand. There is only one property to be set (except for the default property Order which is defined in ActionFilterAttribute) which is which field to be parsed. I check if the field is null or empty, if not ten I use a regular expression to see check if the selected field ends with “page” and a numerical value. The numerical value is then returned as a argument for the action called “pageNo”. This value is defaulted to 1 to make sure that we always get a number for our page (and not specifying anything should mean the first page). In my case I also want the field to be modified so it doesn’t contain the “page” part but that might not be suitable for everyone. If not, then you can easily remove that part by commenting the line looking like this: filterContext.ActionParameters[Field] = id;

using System;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;

namespace BeaverShopMvc.Attributes
{
    /// <summary>
    /// An ActionFilter to parse a page number from a field value
    /// </summary>
    public class PagingAttribute : ActionFilterAttribute
    {
        /// <summary>
        /// The name of the field to parse
        /// </summary>
        public string Field { get; set; }

        // A compiled regular expression to speed things up
        private static Regex _pageRegex = new Regex("page(?<PageNo>\\d+)$", RegexOptions.CultureInvariant | RegexOptions.Compiled);

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            string id = filterContext.ActionParameters[Field] as string;
            Int32 pageNo = 1;

            // We never need an ending / so remove it
            id = id.TrimEnd('/');

            // Check so that the field isn't empty and while at it also that it
            // contains "page"
            if (!string.IsNullOrEmpty(id) && id.ToLowerInvariant().Contains("page"))
            {
                Match match = _pageRegex.Match(id);
                if (match.Success)
                {
                    pageNo = Convert.ToInt32(match.Groups["PageNo"].Value);
                    if (id.Contains("/"))
                        id = id.Substring(0, id.LastIndexOf('/'));
                    else
                        id = string.Empty;
                }
            }

            filterContext.ActionParameters["pageNo"] = pageNo;
            // The line below replaces the old field value with a
            // new one without the "page" part
            filterContext.ActionParameters[Field] = id;

            base.OnActionExecuting(filterContext);
        }
    }
}

Using the attribute is really simple and the great thing about this is that it is easily reusable on any actions you need it to. Below is a little sample of it in action.

        [Paging(Field = "id&")]
        public ActionResult ListProducts(string id, Int32 pageNo)
        {
            return View();
        }

I have a few more filters that I really like as well (AkismetValidatorAttribute for example) that I might share some other day. I’m still learning a lot about working with MVC and will keep you posted with what other interesting things I can find.

Reading EXIF with extension methods

August 20th, 2009 2 comments

I’m still putting quite a lot of work into Imager Gallery.Net. I have a alpha up an running at www.kallesbildarkiv.se though it still lacks quite a lot of features.

Keeping my earlier commitment to release interesting code created for the project I can now present you with my next piece of code. I’ve had problems with finding a good class to read EXIF-information from images that isn’t written in VB.Net, really poorly written or licensed under GPL so I decided I had to write my own. After looking into the subject I found that it would be quite easy to implement this as extension methods on the regular Image class. I’ve decided to release the code under the Creative Commons Attribution-Share Alike license to allow people to use it anywhere as long as they can admit that they are using it.

/*
 * Creative Commons Attribution-Share Alike 3.0 Unported
 * You are free:
 *   to Share — to copy, distribute and transmit the work
 *   to Remix — to adapt the work
 *
 * Under the following conditions:
 *   Attribution — You must attribute the work in the manner
 *   specified by the author or licensor (but not in any way
 *   that suggests that they endorse you or your use of the work).
 *
 *   Share Alike — If you alter, transform, or build upon this work,
 *   you may distribute the resulting work only under the same, similar
 *   or a compatible license.
 *
 * For more information, see http://creativecommons.org/licenses/by-sa/3.0/
 */
using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Text;
using System.Drawing;

/*
 * This class is completely based on the documentation
 * of the EXIF format found here: http://www.exif.org/Exif2-2.PDF
 */

namespace CrazyBeavers.Extensions
{
    public static partial class ImageExtensions
    {
        #region Enumerations
        #pragma warning disable 1591

        private enum PropertyType
        {
            ByteArray = 1,
            AsciiString = 2,
            UnsignedShort = 3,
            UnsignedLong = 4,
            UnsignedLongPair = 5,
            Any = 6,
            SignedLongArray = 7,
            UnsignedLongPairArray = 10
        }

        public enum PropertyTag
        {
            ImageWidth = 0x100,
            ImageLength = 0x101,
            NBitsPerSample = 0x102,
            Compression = 0x103,
            PhotometricInterpretation = 0x106,
            Orientation = 0x112,
            SamplesPerPixel = 0x115,
            PlanarConfiguration = 0x11C,
            YCbCrSubSampling = 0x212,
            YCbCrPositioning = 0x213,
            XResolution = 0x11A,
            YResolution = 0x11B,
            ResolutionUnit = 0x128,
            StripOffsets = 0x111,
            RowsPerStrip = 0x116,
            StripByteCounts = 0x117,
            JPEGInterchangeFormat = 0x201,
            JPEGInterchangeFormatLength = 0x202,
            TransferFunction = 0x12D,
            WhitePoint = 0x13E,
            PrimaryChromaticities = 0x13F,
            YCbCrCoefficients = 0x211,
            ReferenceBlackWhite = 0x214,
            DateTime = 0x132,
            ImageDescription = 0x10E,
            Make = 0x10F,
            Model = 0x110,
            Software = 0x131,
            Artist = 0x13B,
            Copyright = 0x8298,
            ExifVersion = 0x9000,
            FlashpixVersion = 0xA000,
            ColorSpace = 0xA001,
            ComponentsConfiguration = 0x9101,
            CompressedBitsPerPixel = 0x9102,
            PixelXDimension = 0xA002,
            PixelYDimension = 0xA003,
            MakerNote = 0x927C,
            UserComment = 0x9286,
            RelatedSoundFile = 0xA004,
            DateTimeOriginal = 0x9003,
            DateTimeDigitized = 0x9004,
            SubSecTime = 0x9290,
            SubSecTimeOriginal = 0x9291,
            SubSecTimeDigitized = 0x9292,
            ImageUniqueID = 0xA420,
            ExposureTime = 0x829A,
            FNumber = 0x829D,
            ExposureProgram = 0x8822,
            SpectralSensitivity = 0x8824,
            ISOSpeedRatings = 0x8827,
            OECF = 0x8828,
            ShutterSpeedValue = 0x9201,
            ApertureValue = 0x9202,
            BrightnessValue = 0x9203,
            ExposureBiasValue = 0x9204,
            MaxApertureValue = 0x9205,
            SubjectDistance = 0x9206,
            MeteringMode = 0x9207,
            LightSource = 0x9208,
            Flash = 0x9209,
            FocalLength = 0x920A,
            SubjectArea = 0x9214,
            FlashEnergy = 0xA20B,
            SpatialFrequencyResponse = 0xA20C,
            FocalPlaneXResolution = 0xA20E,
            FocalPlaneYResolution = 0xA20F,
            FocalPlaneResolutionUnit = 0xA210,
            SubjectLocation = 0xA214,
            ExposureIndex = 0xA215,
            SensingMethod = 0xA217,
            FileSource = 0xA300,
            SceneType = 0xA301,
            CFAPattern = 0xA302,
            CustomRendered = 0xA401,
            ExposureMode = 0xA402,
            WhiteBalance = 0xA403,
            DigitalZoomRatio = 0xA404,
            FocalLengthIn35mmFilm = 0xA405,
            SceneCaptureType = 0xA406,
            GainControl = 0xA407,
            Contrast = 0xA408,
            Saturation = 0xA409,
            Sharpness = 0xA40A,
            DeviceSettingDescription = 0xA40B,
            SubjectDistanceRange = 0xA40C
        }

        public enum Compression
        {
            NotSpecified = 0,
            Uncompressed = 1,
            JpegCompression = 6
        }

        public enum PhotometricInterpretation
        {
            RGB = 2,
            YCbCr = 6
        }

        public enum Orientation
        {
            TopLeft = 1,
            TopRight = 2,
            BottomRight = 3,
            BottomLeft = 4,
            LeftTop = 5,
            RightTop = 6,
            RightBottom = 7,
            LeftBottom = 8
        }

        public enum ExposureMode
        {
            AutoExposure = 0,
            ManualExposure = 1,
            AutoBracket = 2
        }

        public enum ExposureProgram
        {
            Undefined = 0,
            Manual = 1,
            Normal = 2,
            AperturePriority = 3,
            ShutterPriority = 4,
            CreativeProgram = 5,
            ActionProgram = 6,
            PortraitMode = 7,
            LandscapeMode = 8
        }

        public enum MeteringMode
        {
            Unknown = 0,
            Average = 1,
            CenterWeightedAverage = 2,
            Spot = 3,
            MultiSpot = 4,
            Pattern = 5,
            Partial = 6,
            Other = 255
        }

        public enum Flash
        {
            FlashDidNotFire = 0x0000,
            FlashFired = 0x0001,
            StrobeReturnNotDetected = 0x0005,
            StrobeReturnDetected = 0x0007,
            FlashFiredCompulsoryMode = 0x0009,
            FlashFiredCompulsoryModeStrobeReturnNotDetected = 0x000D,
            FlashFiredCompulsoryModeStrobeReturnDetected = 0x000F,
            FlashDidNotFireCompulsoryMode = 0x0010,
            FlashDidNotFireAutoMode = 0x0018,
            FlashFiredAutoMode = 0x00019,
            FlashFiredAutoModeStrobeReturnNotDetected = 0x001D,
            FlashFiredAutoModeStrobeReturnDetected = 0x001F,
            NoFlash = 0x0020,
            FlashFiredRedEyeReduction = 0x0041,
            FlashFiredRedEyeReductionStrobeReturnNotDetected = 0x0045,
            FlashFiredRedEyeReductionStrobeReturnDetected = 0x0047,
            FlashFiredCompulsoryModeRedEyeReduction = 0x0049,
            FlashFiredCompulsoryModeRedEyeReductionStrobeReturnNotDetected = 0x004D,
            FlashFiredCompulsoryModeRedEyeReductionStrobeReturnDetected = 0x004F,
            FlashFiredAutoModeRedEyeReduction = 0x0059,
            FlashFiredAutoModeRedEyeReductionStrobeReturnNotDetected = 0x005D,
            FlashFiredAutoModeRedEyeReductionStrobeReturnDetected = 0x005F
        }

        public enum WhiteBalance
        {
            Auto = 0,
            Manual = 1
        }

        public enum SceneCaptureType
        {
            Standard = 0,
            Landscape = 1,
            Portrait = 2,
            NightScene = 3
        }

        public enum LightSource
        {
            Unkown = 0,
            Daylight = 1,
            Fluorescent = 2,
            Tungsten = 3,
            Flash = 4,
            FineWeather = 9,
            CloudyWeather = 10,
            Shade = 11,
            DaylightFluorescent = 12,
            DayWhiteFluorescent = 13,
            CoolWhiteFluorescent = 14,
            WhiteFluorescent = 15,
            StandardLightA = 17,
            StandardLightB = 18,
            StandardLightC = 19,
            D55 = 20,
            D65 = 21,
            D75 = 22,
            D50 = 23,
            ISOStudioTungsten = 24,
            OtherLightSource = 255
        }

        public enum GainControl
        {
            None = 0,
            LowGainUp = 1,
            HighGainUp = 2,
            LowGainDown = 3,
            HighGainDown = 4
        }

        public enum Contrast
        {
            Normal = 0,
            Soft = 1,
            Hard = 2
        }

        public enum Saturation
        {
            Normal = 0,
            LowSaturation = 1,
            HighSaturation = 2
        }

        public enum Sharpness
        {
            Normal = 0,
            Soft = 1,
            Hard = 2
        }

        public enum SubjectDistanceRange
        {
            Unknown = 0,
            Macro = 1,
            CloseView = 2,
            DistantView = 3
        }

        #pragma warning restore 1591
        #endregion

        #region Properties

        public static DateTime ExifDateTime(this Image image)
        {
            return GetPropertyDateTime(image, PropertyTag.DateTime);
        }

        public static DateTime ExifDateTimeDigitized(this Image image)
        {
            return GetPropertyDateTime(image, PropertyTag.DateTimeDigitized);
        }

        public static DateTime ExifDateTimeOriginal(this Image image)
        {
            return GetPropertyDateTime(image, PropertyTag.DateTimeOriginal);
        }

        public static string ExifMake(this Image image)
        {
            return GetPropertyString(image, PropertyTag.Make);
        }

        public static string ExifModel(this Image image)
        {
            return GetPropertyString(image, PropertyTag.Model);
        }

        public static string ExifImageDescription(this Image image)
        {
            return GetPropertyString(image, PropertyTag.ImageDescription);
        }

        public static string ExifSoftware(this Image image)
        {
            return GetPropertyString(image, PropertyTag.Software);
        }

        public static string ExifArtist(this Image image)
        {
            return GetPropertyString(image, PropertyTag.Artist);
        }

        public static Orientation ExifOrientation(this Image image)
        {
            return (Orientation)GetPropertyUInt16(image, PropertyTag.Orientation);
        }

        public static Flash ExifFlash(this Image image)
        {
            return (Flash)GetPropertyUInt16(image, PropertyTag.Flash);
        }

        public static SceneCaptureType ExifSceneCaptureType(this Image image)
        {
            return (SceneCaptureType)GetPropertyUInt16(image, PropertyTag.SceneCaptureType);
        }

        public static WhiteBalance ExifWhiteBalance(this Image image)
        {
            return (WhiteBalance)GetPropertyUInt16(image, PropertyTag.WhiteBalance);
        }

        public static MeteringMode ExifMeteringMode(this Image image)
        {
            return (MeteringMode)GetPropertyUInt16(image, PropertyTag.MeteringMode);
        }

        public static Compression ExifCompression(this Image image)
        {
            return (Compression)GetPropertyUInt16(image, PropertyTag.Compression);
        }

        public static ExposureMode ExifExposureMode(this Image image)
        {
            return (ExposureMode)GetPropertyUInt16(image, PropertyTag.ExposureMode);
        }

        public static ExposureProgram ExifExposureProgram(this Image image)
        {
            return (ExposureProgram)GetPropertyUInt16(image, PropertyTag.ExposureProgram);
        }

        public static PhotometricInterpretation ExifPhotometricInterpretation(this Image image)
        {
            return (PhotometricInterpretation)GetPropertyUInt16(image, PropertyTag.PhotometricInterpretation);
        }

        public static LightSource ExifLightSource(this Image image)
        {
            return (LightSource)GetPropertyUInt16(image, PropertyTag.LightSource);
        }

        public static GainControl ExifGainControl(this Image image)
        {
            return (GainControl)GetPropertyUInt16(image, PropertyTag.GainControl);
        }

        public static Contrast ExifLightContrast(this Image image)
        {
            return (Contrast)GetPropertyUInt16(image, PropertyTag.Contrast);
        }

        public static Saturation ExifSaturation(this Image image)
        {
            return (Saturation)GetPropertyUInt16(image, PropertyTag.Saturation);
        }

        public static Sharpness ExifSharpness(this Image image)
        {
            return (Sharpness)GetPropertyUInt16(image, PropertyTag.Sharpness);
        }

        public static SubjectDistanceRange ExifSubjectDistanceRange(this Image image)
        {
            return (SubjectDistanceRange)GetPropertyUInt16(image, PropertyTag.SubjectDistanceRange);
        }

        public static Rational ExifExposureTime(this Image image)
        {
            return GetPropertyRational(image, PropertyTag.ExposureTime);
        }

        public static double ExifFNumber(this Image image)
        {
            return GetPropertyRational(image, PropertyTag.FNumber).ToDouble();
        }

        public static double ExifAperture(this Image image)
        {
            return GetPropertyRational(image, PropertyTag.ApertureValue).ToDouble();
        }

        public static Rational ExifSubjectDistance(this Image image)
        {
            return GetPropertyRational(image, PropertyTag.SubjectDistance);
        }

        public static double ExifFocalLength(this Image image)
        {
            return GetPropertyRational(image, PropertyTag.FocalLength).ToDouble();
        }

        public static UInt16 ExifISOSpeedRating(this Image image)
        {
            return GetPropertyUInt16(image, PropertyTag.ISOSpeedRatings);
        }

        public static Rational ExifDigitalZoomRatio(this Image image)
        {
            return GetPropertyRational(image, PropertyTag.DigitalZoomRatio);
        }

        #endregion

        public static bool HasProperty(this Image image, PropertyTag property)
        {
            return Array.IndexOf(image.PropertyIdList, (Int32)property) > -1;
        }

        public static DateTime GetPropertyDateTime(this Image image, PropertyTag property)
        {
            if (!image.HasProperty(property))
                return DateTime.MinValue;

            string value = GetPropertyString(image, property);

            // If first char is blank or string is empty no DateTime is present
            if (value == string.Empty || value[0] == ' ')
                return DateTime.MinValue;

            // DateTime is written as YYYY:MM:DD HH:MM:SS
            // and needs to be converted to YYYY-MM-DD HH:MM:SS
            // to be convertable to a .Net DateTime
            string datePart = value.Substring(0, 10).Replace(':', '-');
            string timePart = value.Substring(11);
            return Convert.ToDateTime(datePart + ' ' + timePart);
        }

        public static string GetPropertyString(this Image image, PropertyTag property)
        {
            if (!image.HasProperty(property))
                return string.Empty;

            PropertyItem item = image.GetPropertyItem((Int32)property);
            if (item.Type != (short)PropertyType.AsciiString)
                return string.Empty;

            byte[] buffer = item.Value;
            return Encoding.ASCII.GetString(buffer).Trim('\0');
        }

        public static UInt32 GetPropertyUInt32(this Image image, PropertyTag property)
        {
            if (!image.HasProperty(property))
                return 0;

            PropertyItem item = image.GetPropertyItem((Int32)property);
            if (item.Type != (short)PropertyType.UnsignedLong)
                return 0;

            byte[] buffer = item.Value;
            return BitConverter.ToUInt32(buffer, 0);
        }

        public static UInt16 GetPropertyUInt16(this Image image, PropertyTag property)
        {
            if (!image.HasProperty(property))
                return 0;

            PropertyItem item = image.GetPropertyItem((Int32)property);
            if (item.Type != (short)PropertyType.UnsignedShort)
                return 0;

            byte[] buffer = item.Value;
            return BitConverter.ToUInt16(buffer, 0);
        }

        public static Rational GetPropertyRational(this Image image, PropertyTag property)
        {
            if (!image.HasProperty(property))
                return Rational.Zero;

            PropertyItem item = image.GetPropertyItem((Int32)property);
            if (item.Type != (short)PropertyType.UnsignedLongPair)
                return Rational.Zero;

            byte[] buffer = item.Value;
            Int32 numerator = BitConverter.ToInt32(buffer, 0);
            Int32 denominator = BitConverter.ToInt32(buffer, 4);

            Rational rational = new Rational(numerator, denominator);
            return rational;
        }
    }

    public struct Rational
    {
        private Int32 _numerator;
        public Int32 Numerator
        {
            get
            {
                return _numerator;
            }
            set
            {
                _numerator = value;
            }
        }

        private Int32 _denominator;
        public Int32 Denominator
        {
            get
            {
                return _denominator;
            }
            set
            {
                if (value == 0)
                    throw new ArgumentOutOfRangeException("Denominator", 0, "Denominator can't be zero.");

                _denominator = value;
            }
        }

        public static Rational Zero
        {
            get
            {
                return new Rational(0, 1);
            }
        }

        public Rational(Int32 numerator, Int32 denominator)
        {
            _numerator = 0;
            _denominator = 1;
            Numerator = numerator;
            Denominator = denominator;
        }

        public override string ToString()
        {
            return string.Format("{0}/{1}", Numerator, Denominator);
        }

        public double ToDouble()
        {
            return (double)Numerator / Denominator;
        }
    }
}

This introduces several new methods to the Image class starting with “Exif” such as ExifDateTime, ExifMake and ExifExposureTime. The Rational class was inserted into the same code block in this sample, in the main project it has it’s own file under CrazyBeavers.Math. Hope you enjoy this piece of code!

Compacting CSS on-the-fly

July 2nd, 2009 No comments

I’m currently doing quite a lot of work on my ny MVC-version of Imager Gallery. I still have a lot to do before I have anything to release (or even use myself) though but I thought I could release a few snippets that I’ve found useful.

First out is a HttpHandler that will process .css-files and remove any unused whitespace from the file before sending it to the client. I’ve been meaning to write this for a long time but never really had the time until someone posted a link about it over at ASPSidan.se showing their own implementation. Reading up on his post about it I found that it was based on a solution by Sam Collett. Both implementations lacked a few things that I wanted to have so I sat down with Sams code and started improving it, mainly making it a work as a handler for .css-files instead of a Generic Handler wanting a querystring but also adding caching and compiling the regular expression as a static member.

To use this, just pop it in a .cs-file in your your App_Code folder and add it to your web.config (as described in the comments for the class). You also need to configure your IIS to route .css-files through the asp.net process to make it run (not needed under IIS7 or IIS6 configured for MVC). Hope you find it useful!

using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Caching;

namespace HttpHandlers
{
    /// &lt;summary&gt;
    /// Handler for compacting css-files on the fly
    /// Based on solution by Sam Collett found at
    /// http://webdevel.blogspot.com/2007/09/csscompact-webhandler-for-shrinking-css.html
    ///
    /// Needs to be added to web.config under System.Web/httpHandlers like this
    /// &lt;add verb=&quot;GET,HEAD&quot; path=&quot;*.css&quot; type=&quot;HttpHandlers.CssCompactHandler&quot;/&gt;
    ///
    /// For debuing purposes you can append a querystring whenbrowsing to your .css-file
    /// to skip the compacting. It would look like this:
    /// http://mySite/Content/Site.css?noCompact=1
    /// &lt;/summary&gt;
    public class CssCompactHandler : IHttpHandler
    {
        // Staticly compiled regex to avoid compiling it each request
        private static Regex RegexRemoval = new Regex(@&quot;^\s+|/\*([^*\\\\]|\*(?!/))+\*/|\r|\n|\t&quot;, RegexOptions.Multiline | RegexOptions.Compiled);

        public void ProcessRequest(HttpContext context)
        {
            HttpResponse Response = context.Response;
            HttpRequest Request = context.Request;
            Cache Cache = context.Cache;

            FileInfo fileInfo = new FileInfo(Request.PhysicalPath);
            if (!fileInfo.Exists)
            {
                Response.StatusDescription = &quot;The server has not found anything that matches the requested URI.&quot;;
                Response.StatusCode = 404;
                Response.End();
            }

            Response.ContentType = &quot;text/css&quot;;
            Response.Cache.SetLastModified(fileInfo.LastWriteTime);

            // If we are in debugmode (set in web.config) then
            // we dont want to cache the css since we are probably
            // still in development
            if (context.IsDebuggingEnabled)
            {
                Response.Cache.SetCacheability(HttpCacheability.NoCache);
            }
            else
            {
                Response.Cache.SetCacheability(HttpCacheability.Public);
                Response.Cache.SetExpires(DateTime.Now.AddDays(1));
            }

            // HEAD requests only need the headers so we skip the
            // content here
            if (Request.HttpMethod != &quot;HEAD&quot;)
            {
                string cacheName = string.Format(&quot;CssCompactHandlerCache_{0}&quot;, fileInfo.FullName);
                string cacheValue = Cache[cacheName] as string;
                if (cacheValue != null &amp;&amp; Request.QueryString[&quot;noCompact&quot;] == null)
                {
                    Response.Write(cacheValue);
                    Response.End();
                }

                using (StreamReader cssStream = fileInfo.OpenText())
                {
                    string cssContent = cssStream.ReadToEnd();
                    if (Request.QueryString[&quot;noCompact&quot;] == null)
                    {
                        cssContent = RegexRemoval.Replace(cssContent, &quot;&quot;);

                        // Set up a CacheDependency on the css-file,
                        // this way the cache will be invalidated if
                        // we change the file
                        CacheDependency dependency = new CacheDependency(fileInfo.FullName);
                        Cache.Add(string.Format(&quot;CssCompactHandlerCache_{0}&quot;, fileInfo.FullName), cssContent, dependency, DateTime.Now.AddYears(1), Cache.NoSlidingExpiration, CacheItemPriority.AboveNormal, null);
                    }
                    Response.Write(cssContent);
                }
            }
        }

        public bool IsReusable
        {
            get
            {
                return true;
            }
        }
    }
}

RFC2047 decoder in C#

June 17th, 2009 No comments

Just spent the evening writing a class to decode RFC2047 strings in C#. It was requested by a user on ASPSidan, a Swedish site dedicated to ASP, ASP.Net and other Microsoft technologies but before I was done someone had already found another implementation on the net. Anyway, the code shouldn’t go to waste so I’m posting it here if anyone needs it.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Globalization;

namespace CrazyBeavers.Net.Mail
{
    public static class RFC2047Decoder
    {
        public static string Parse(string input)
        {
            StringBuilder sb = new StringBuilder();
            StringBuilder currentWord = new StringBuilder();
            bool readingWord = false;

            Int32 i = 0;
            while (i &lt; input.Length)
            {
                char currentChar = input[i];
                char peekAhead;
                switch (currentChar)
                {
                    case '=':
                        peekAhead = (i == input.Length - 1) ? ' ' : input[i + 1];

                        if (peekAhead == '?')
                            readingWord = true;
                        break;

                    case '?':
                        peekAhead = (i == input.Length - 1) ? ' ' : input[i + 1];

                        if (peekAhead == '=')
                        {
                            readingWord = false;

                            currentWord.Append(currentChar);
                            currentWord.Append(peekAhead);

                            sb.Append(ParseEncodedWord(currentWord.ToString()));
                            currentWord = new StringBuilder();

                            i += 2;
                            continue;
                        }
                        break;
                }

                if (readingWord)
                {
                    currentWord.Append(currentChar);
                    i++;
                }
                else
                {
                    sb.Append(currentChar);
                    i++;
                }
            }

            return sb.ToString();
        }

        private static string ParseEncodedWord(string input)
        {
            StringBuilder sb = new StringBuilder();

            if (!input.StartsWith(&quot;=?&quot;))
                return input;

            if (!input.EndsWith(&quot;?=&quot;))
                return input;

            // Get the name of the encoding but skip the leading =?
            string encodingName = input.Substring(2, input.IndexOf(&quot;?&quot;, 2) - 2);
            Encoding enc = Encoding.GetEncoding(encodingName);

            // Get the type of the encoding
            char type = input[encodingName.Length + 3];

            // Start after the name of the encoding and the other required parts
            Int32 i = encodingName.Length + 5;

            switch (char.ToLowerInvariant(type))
            {
                case 'q':
                    while (i &lt; input.Length)
                    {
                        char currentChar = input[i];
                        char[] peekAhead = new char[2];
                        switch (currentChar)
                        {
                            case '=':
                                peekAhead = (i &gt;= input.Length - 2) ? null : new char[] { input[i + 1], input[i + 2] };

                                if (peekAhead == null)
                                    break;

                                string decodedChar = enc.GetString(new byte[] { Convert.ToByte(new string(peekAhead, 0, 2), 16) });
                                sb.Append(decodedChar);
                                i += 3;
                                break;
                            case '?':
                                if (input[i + 1] == '=')
                                    i += 2;
                                break;
                            default:
                                sb.Append(currentChar);
                                i++;
                                break;
                        }
                    }
                    break;
                case 'b':
                    string baseString = input.Substring(i, input.Length - i - 2);
                    byte[] baseDecoded = Convert.FromBase64String(baseString);
                    sb.Append(enc.GetString(baseDecoded));
                    break;
            }
            return sb.ToString();
        }
    }
}
Categories: .Net, Code samples, c# Tags: , , , ,