ページ

Saturday, August 20, 2011

facebook and twitter application implemented in silverlight.

It's easy to create facebook and twitter application implemented in silverlight, but as for twitter, it doesn't work because of Cross-Domain matter.
So as one of the solutions, It should be implemented with 'Out of browser' & 'Elevated Trusted mode'.
Then, it will be a kind of desktop application.( I actually left 'Callback URL' blank. )

However, there's an important reminder about it.

How we can implant 'consumer_secret' into the application.
or
How we can let the application get/download 'consumer_secret' from somewhere else.

Well, I don't know the best practice, but I did encrypt the key as a countermeasure.
* Maybe, It's better to use web pages like asp.net before getting oauth_token and oauth_token_secret.

Anyway, I tried to create the applications because I wanted to know how 'OAuth 1.0a' and 'OAuth 2.0' work.

Silverlight client for facebook.
https://social-media-applications.appspot.com/facebook/silverlight/index.html

Silverlight client for twitter ( Out Of Browser ).
https://social-media-applications.appspot.com/twitter/silverlight/index.html


Get uri, query and headers

private void OAuth(string url, string httpMethod, Dictionary<string, string> parameters, out Uri uri, out string query, out Dictionary<HttpRequestHeader, string> headers)
{
    query = string.Empty;
    uri = null;
    headers = new Dictionary<HttpRequestHeader, string>();
    parameters["oauth_signature"] = string.Empty;
    var param = parameters.OrderBy(x => x.Key);

    StringBuilder sb = new StringBuilder();
    foreach (var p in param)
    {
        if (!string.IsNullOrEmpty(p.Value))
            sb.Append(@"&" + p.Key + "=" + UrlEncode(p.Value));
    }
            sb.Remove(0, 1);
    string signatureBase = string.Format(@"{0}&{1}&{2}", httpMethod.ToUpper(), UrlEncode(url), UrlEncode(sb.ToString()));

    HMACSHA1 hmacsha1 = new HMACSHA1();
    hmacsha1.Key = Encoding.UTF8.GetBytes(string.Format("{0}&{1}", UrlEncode(parameters["consumer_secret"]), UrlEncode(parameters["oauth_token_secret"])));
    byte[] dataBuffer = Encoding.UTF8.GetBytes(signatureBase);
    byte[] hashBytes = hmacsha1.ComputeHash(dataBuffer);

    string sig = Convert.ToBase64String(hashBytes);
    sb.Append(@"&oauth_signature=" + UrlEncode(sig));
    parameters["oauth_signature"] = sig;

    query = sb.ToString();
    string getURL = url;
    if (httpMethod.ToUpper().Equals("GET"))
    {
        if (!string.IsNullOrEmpty(query))
            getURL += '?' + query;
    }
    uri = new Uri(getURL, UriKind.RelativeOrAbsolute);
    if (httpMethod.ToUpper().Equals("POST"))
    {
        headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
        headers[HttpRequestHeader.Authorization] = GetHeader(uri, parameters);
    }
}


URL Encode

public string UrlEncode(string value)
{
    string urlChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
    StringBuilder result = new StringBuilder();
    byte[] data = Encoding.UTF8.GetBytes(value);
    for (int i = 0; i < data.Length; i++)
    {
        int c = data[i];
        if (c < 0x80 && urlChars.Contains((char)c))
            result.Append((char)c);
        else
            result.Append('%' + String.Format("{0:X2}", (int)data[i]));
    }
    return result.ToString();
}

Nonce

private string GetNonce()
{
    string nonceChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    StringBuilder result = new StringBuilder(8);
    Random random = new Random();
    for (int i = 0; i < 8; ++i)
        result.Append(nonceChars[random.Next(nonceChars.Length)]);
    return result.ToString();
}

Timestamp

private string GetTimestamp()
{
    TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
    return Convert.ToInt64(ts.TotalSeconds).ToString();
}

Authorization Header

private string GetHeader(Uri uri, Dictionary<string, string> parameters)
{
    StringBuilder realm = new StringBuilder();
    realm.Append(uri.Scheme);
    realm.Append("://");
    realm.Append(uri.Host);
    if (uri.Port != 80 && uri.Port != 443)
        realm.Append(':' + uri.Port);

    StringBuilder header = new StringBuilder();
    header.Append("Authorization: ");
    header.Append("OAuth realm=\"" + realm.ToString() + "\"");
    foreach (var p in parameters)
    {
            header.Append(", " + p.Key + "=\"" + UrlEncode(p.Value) + "\"");
    }
    header.Append("\r\n");
    return header.ToString();
}


Usage : Post message on your twitter

public void Tweet(string message)
{
    Dictionary<string, string> param = new Dictionary<string, string>();
    param["oauth_consumer_key"] = "xxxxxxxxxxx";
    // .... setting required parameters
    param["oauth_version"] = "1.0";
    param["status"] = message;

    Uri uri;
    string query;
    Dictionary<HttpRequestHeader, string> headers;
    OAuth("https://api.twitter.com/statuses/update.json", "POST", param, out uri, out query, out headers);
    
    WebClient wc = new WebClient();
    foreach (var header in headers)
        wc.Headers[header.Key] = header.Value;
    wc.UploadStringCompleted += OnUploadStringCompleted;
    wc.UploadStringAsync(uri, query);
}

void OnUploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{
    // Get Json formatted data by e.Result if e.Error equals NULL.
}




OAuth 1.0a is really harder to be implemented than OAuth 2.0, but this taught me a good lesson.

Analyzing Meiryo Font.

Thinking of a font, the font has lots of informations in itself.
and especially, as for vertical writing a.k.a. "縦書き(tategaki)", we might need to know some information on the details of glyph with font architecture, such as typography.
I think this knowledge is helpful for eBook or something like that.

So today, I will post the article about I analyzed a font "meiryo" by WPF application.(C#)

OpenType specification
http://www.microsoft.com/typography/otspec/default.htm


Actually ,it's very easy to find a glyph index and the outline data by using of following methods.

CharacterToGlyphMap.TryGetValue (System.Windows.Media.GlyphTypeface)
GetGlyphOutline (System.Windows.Media.GlyphTypeface)


However, it seems there's no method to find the glyph index of vertical writing. ( as far as I know ).
So I tried scanning binary data of the font file according to the following steps.

For example : How to find vertical writing glyph index about a character 'a' (Unicode: U+0061)

1. Find glyph index about 'a' from cmap.

1-1.cmap Header
     - filtering Encoding Record by EncodingID = 1 or EncodingID = 10 on PlatformID = 3

1-2.CmapFormat4 or CmapFormat12

The glyph index about 'a' is 0x0044 on Meiryo.


2. Find vertical glyph index about 'a' from GSUB

2-1.GSUB Header

2-2.FeatureList
     - filtering by Feature Tag 'vert' or 'vrt2'

2-3.Feature

2-4.LookupList

2-5.Lookup
     - filtering by lookupListIndex on Feature
     - filtering by LookupType = 0x0001

2-6.SingleSubstitutionFormat1 or SingleSubstitutionFormat2

2-7.CoverageFormat1 or CoverageFormat2

The vertical glyph index about 'a' is  0x2793 on Meiryo.








* The application gets glyph data(points data for outline) from glyf ( Simple Glyph Description or Composite Glyph Description )
* And the application draws lines and bezier curves by use of the points.



Composite Glyph


Simple Glyph


Simple Glyph :
multibyte character

About endian

"endian" is important when a program is scanning the binary data, whether "big endian" or "little endian" is right on the computer.

public byte[] GetBytes(byte[] bytes, int startIndex, int length)
{
    if(bytes != null)
    {
        if(index >= 0 && length > 0 && index + length < bytes.Length)
        {
            byte[] bytes0 = new byte[length];
            for(int i=0;i<length;i++)
                bytes0[i] = bytes[startIndex + i];
            if(BitConverter.IsLittleEndian)
                Array.Reverse(bytes0);
            return bytes0;
        }
    }
    throw new Exception("Exception is occurred on GetBytes");
}


then, available for type conversions like this.


public short GetShort(byte[] bytes, int startIndex, int length)
{
	try
	{
		byte[] sBytes = GetBytes(bytes, startIndex, 2);
		return BitConverter.ToInt16(sBytes, 0);
	}
	catch(Exception ex)
	{
		throw new Exception(string.Format("Exception is occurred on GetShort(bytes, {0}, {1}): {2}", startIndex, length, ex.Message));
	}
}


Other tables except for cmap and GSUB have more informations about the font like baseline.

but it's not easy. I mean, I can't be bothered...