Tag Archives: iPhone

Data Uris–A way of embedding images in your html page or UIWebView

Standard

image

Currently I’m investigating ways of using  UIWebView to render a user interface.  

I’d like to re-purpose some server side html form rendering logic so that it can be resused on the iPhone, Android and Windows Phone. 

Modern forms incorporate more than simple form elements however.  They may include data, such as images.

By default, the UIWebview supports images in the “bundle” and images in the documents area. 

What if I want to create images in memory, then display these in the HTML?

An little known feature of many modern web browsers (including Webkit) is datauris.  A datauri allows data to be embedded in a webpage where a url would usually be.

Here’s an example:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />
There are disadvantages however, which are the obvious effect this will have on caching and pagesize.
The Stackflow article Display Local UI Image On a UIWebView shows how to this, replicated here for your convenience.
This code demonstrates reading a file from the local bundle, then embedding into the background image, in the html dom of a UIWebView:

#import "NSString+DataURI.h"

#import "NSData+Base64.h"

...

-(void)webViewDidFinishLoad:(UIWebView *)webView

{

    NSString *imgPath = [[NSBundle mainBundle] pathForResource:@"image" ofType:@"png"];

    NSData *imgData = [NSData dataWithContentsOfFile:imgPath];

    NSString *imgB64 = [[imgData base64Encoding] pngDataURIWithContent];

    NSString *javascript = [NSString stringWithFormat:@"document.body.style.backgroundImage='url(%@)';", imgB64];

    [webView stringByEvaluatingJavaScriptFromString:javascript];

}

This code uses the following utility functions that provide the base 64 encoding support:

NSData+Base64.h

@interface NSData (Base64)

+ (NSData *)dataWithBase64EncodedString:(NSString *)string;

- (id)initWithBase64EncodedString:(NSString *)string;

- (NSString *)base64Encoding;

- (NSString *)base64EncodingWithLineLength:(unsigned int) lineLength;

@end

NSData.Base64.m

#import "NSData+Base64.h"

static char encodingTable[64] = {

'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',

'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',

'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',

'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' };

@implementation NSData (VQBase64)

- (id)initWithString:(NSString *)string {

    if ((self = [super init])) {

        [self initWithBase64EncodedString:string];

    }

    return self;

}

+ (NSData *) dataWithBase64EncodedString:(NSString *) string {

    return [[[NSData allocWithZone:nil] initWithBase64EncodedString:string] autorelease];

}

- (id) initWithBase64EncodedString:(NSString *) string {

    NSMutableData *mutableData = nil;

    if( string ) {

        unsigned long ixtext = 0;

        unsigned long lentext = 0;

        unsigned char ch = 0;

        unsigned char inbuf[4], outbuf[3];

        short i = 0, ixinbuf = 0;

        BOOL flignore = NO;

        BOOL flendtext = NO;

        NSData *base64Data = nil;

        const unsigned char *base64Bytes = nil;

        // Convert the string to ASCII data.

        base64Data = [string dataUsingEncoding:NSASCIIStringEncoding];

        base64Bytes = [base64Data bytes];

        mutableData = [NSMutableData dataWithCapacity:[base64Data length]];

        lentext = [base64Data length];

        while( YES ) {

            if( ixtext >= lentext ) break;

            ch = base64Bytes[ixtext++];

            flignore = NO;

            if( ( ch >= 'A' ) && ( ch <= 'Z' ) ) ch = ch - 'A';

            else if( ( ch >= 'a' ) && ( ch <= 'z' ) ) ch = ch - 'a' + 26;

            else if( ( ch >= '0' ) && ( ch <= '9' ) ) ch = ch - '0' + 52;

            else if( ch == '+' ) ch = 62;

            else if( ch == '=' ) flendtext = YES;

            else if( ch == '/' ) ch = 63;

            else flignore = YES;

            if( ! flignore ) {

                short ctcharsinbuf = 3;

                BOOL flbreak = NO;

                if( flendtext ) {

                    if( ! ixinbuf ) break;

                    if( ( ixinbuf == 1 ) || ( ixinbuf == 2 ) ) ctcharsinbuf = 1;

                    else ctcharsinbuf = 2;

                    ixinbuf = 3;

                    flbreak = YES;

                }

                inbuf [ixinbuf++] = ch;

                if( ixinbuf == 4 ) {

                    ixinbuf = 0;

                    outbuf [0] = ( inbuf[0] << 2 ) | ( ( inbuf[1] & 0x30) >> 4 );

                    outbuf [1] = ( ( inbuf[1] & 0x0F ) << 4 ) | ( ( inbuf[2] & 0x3C ) >> 2 );

                    outbuf [2] = ( ( inbuf[2] & 0x03 ) << 6 ) | ( inbuf[3] & 0x3F );

                    for( i = 0; i < ctcharsinbuf; i++ )

                        [mutableData appendBytes:&outbuf[i] length:1];

                }

                if( flbreak )  break;

            }

        }

    }

    self = [self initWithData:mutableData];

    return self;

}

#pragma mark -

- (NSString *) base64Encoding {

    return [self base64EncodingWithLineLength:0];

}

- (NSString *) base64EncodingWithLineLength:(unsigned int) lineLength {

    const unsigned char     *bytes = [self bytes];

    NSMutableString *result = [NSMutableString stringWithCapacity:[self length]];

    unsigned long ixtext = 0;

    unsigned long lentext = [self length];

    long ctremaining = 0;

    unsigned char inbuf[3], outbuf[4];

    unsigned short i = 0;

    unsigned short charsonline = 0, ctcopy = 0;

    unsigned long ix = 0;

    while( YES ) {

        ctremaining = lentext - ixtext;

        if( ctremaining <= 0 ) break;

        for( i = 0; i < 3; i++ ) {

            ix = ixtext + i;

            if( ix < lentext ) inbuf[i] = bytes[ix];

            else inbuf [i] = 0;

        }

        outbuf [0] = (inbuf [0] & 0xFC) >> 2;

        outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4);

        outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6);

        outbuf [3] = inbuf [2] & 0x3F;

        ctcopy = 4;

        switch( ctremaining ) {

            case 1:

                ctcopy = 2;

                break;

            case 2:

                ctcopy = 3;

                break;

        }

        for( i = 0; i < ctcopy; i++ )

            [result appendFormat:@"%c", encodingTable[outbuf[i]]];

        for( i = ctcopy; i < 4; i++ )

            [result appendString:@"="];

        ixtext += 3;

        charsonline += 4;

        if( lineLength > 0 ) {

            if( charsonline >= lineLength ) {

                charsonline = 0;

                [result appendString:@"\n"];

            }

        }

    }

    return [NSString stringWithString:result];

}

@end

  

NSString+DataURI.h

#import <Foundation/Foundation.h>

@interface NSString(DataURI)

- (NSString *) pngDataURIWithContent;

- (NSString *) jpgDataURIWithContent;

@end

NSString+DataURI.m

#import "NSString+DataURI.h"

@implementation NSString(DataURI)

- (NSString *) pngDataURIWithContent;

{

    NSString * result = [NSString stringWithFormat: @"data:image/png;base64,%@", self];

    return result;

}

- (NSString *) jpgDataURIWithContent;

{

    NSString * result = [NSString stringWithFormat: @"data:image/jpg;base64,%@", self];

    return result;

}

@end

  

 
Advertisements

Alright, I admit it, Now I have an iPhone!

Standard

 

Dear Windows Mobile,

 

By the time you read this blog post, I’ll be gone.  I’m sorry for doing this but you left me no other choice.  I know comes as a bit of a shock to you – especially because things have been going so well.  But I’m sorry – I cant stand watching all those iPhone users giggling in the corridors anymore.  I think you’re swell, but I don’t think we’re right for each other.  First of all, we’re not compatible.  You’re a a bit too clunky and I’ve lost patience.   Secondly, I’m tired of waiting for Windows Mobile 7.  It’s too late isn’t it?

Anyway, I want to try another smart phone.  But you know what?  I still want to be friends.  We had some good times, developing software on the brilliant compact framework.  I’m very sad it has to be this way.  But please, don’t cold boot like last time.  Maybe I could still answer questions on the Compact Framework from time to time.  I still code in .net!  Look I wont even make issue of the fact that I had to buy you a replacement battery.

So, take care of yourself and all the best.

Sincerely

Me