Fonts and the Web

Here at InterKnowlogy, we get time each week to tinker and dive into technologies that interest and inspire us. We call it RECESS, which stands for Research and Experimental Coding to Enhance Software Skills. This week I’ve been noticing sites ‘sprucing it up’ by using custom font embedding with services such as TypeKit (Or similarly related projects such as Google Web Fonts) that allow you to license and use custom fonts in a site.

There’s a problem though. 90% of the time it annoys me.

Let me explain. First impressions are everything, and right after first impressions is readability. If I come to your site looking for information, to read something, I don’t want to get a headache doing so. The problem isn’t the design or the font itself usually, it’s the way the browser deals with and renders the font. Using a custom or weird font for the body text of the page WILL bother people. Its why standard fonts exist and are as popular as they are: readability. Now, that being said, there are really good design reasons to want to use custom fonts in a dynamic content driven site to augment the design. Especially in the title and headers since it will catch someones eye and because people spend a minimal amount of time reading them anyways. Whatever the reason, if your going to go down the custom font path its going to be important to refine and choose a solution that is going to be consistent and usable for the people that visit your site.

Coming back to RECESS, I spent some time examining the different offerings and essentially broke them into two categories: Browser/OS rendered text, and Image rendered text. Let me explain: For the Browser/OS rendered text (which TypeKit and Google Web Fonts use) the text is set using normal CSS rules, and then an actual font file is loaded by the browser and used to display the text. Almost exactly how text is displayed for application on your computer except that the font is never ‘installed’. The advantage is that it behaves exactly like normal fonts, you can type, select, copy, and do all the normal things you do with text. The second way is imaged rendered text, taking some chunk of text and turning it into an image that gets displayed in place of the text. Designers sometimes do this for logos and main headers that almost never change because it doesn’t require the use of a more modern browser and they KNOW that it will display the same way. Also in this category is a tool called Cufón. It’s a bit of JavaScript and a bit of a generator. It basically takes a font file, turns it into a bunch of shapes, and then on the users computer it uses JavaScript to load, render, and replace text in the page with those images. The disadvantage being you can’t select and copy the text in the same way, but the advantage is that it looks consistent and renders well.

I decided to tinker with these and see what I came up with (All screenshots are on Windows 7, the IE9 ‘font’ example is invalid as I would have had to convert it to a different format for it to display, but I was lazy. There is another example later that illustrates IE9 correctly showing a font this way.)

So here are some screenshots of the results on different browsers on Windows 7:

Chrome 12:

Firefox 6:

Safari 5.1 on Windows

Internet Explorer 9

I was surprised at the results. Same font, same file, but completely different results between Chrome / Firefox / Safari (IE9 excluded obviously) for the regular font rendering. Cufón came out the most consistent of all of these.

Next I went to TypeKit and found excellent illustrations of why embedding fonts is still so difficult and why I’m seriously considering using Cufón for the time being:

From top to bottom, Safari 5.1, Chrome 12, Firefox 6, IE9

The big thing to note is how jagged the letters look until you get to IE9. Readability wise, I would NOT consider using an alternate font for a large amount of text unless I had to, and for right now, Cufón seems a very viable choice for consistency.

There’s no final conclusion in this, since you will have reasons that will drive you to one option or another (Or simply throw your hands in the air and announce to the world that you are done with the web forever) So:

Nerd out.

Tinker.

Be careful with your font choices and how far you take this on a site that will be used on a regular basis. Cheers!

GitHub 101 on Windows

This post is a continuation off of this post on Up and Running with Git and Visual Studio, if you have no idea what Git is, or how to setup git and git-extensions on windows I recommend you start there.

So you’ve installed git and git extensions, written a sweet hello world application, made some commits, split some branches and merged features in and out here and there, and now your looking to share that code with the world or you’ve decided that just having a local copy of your code isn’t good enough for a backup and don’t want or can’t go out and purchase another machine or setup remote hard drive. For whatever reason, you’ve decided to use GitHub to push a copy of your source code up to, either to share with the world, keep a local backup, or to share with a team.

First, you’ll need to create a GitHub account, which is free for public/shared repositories (example: Open Source Projects), and from $7-22 a month for private repositories, and $25+ for organizations and companies. Assuming you decide to go with a free account to try things out, you’ll land at a page that will look like this:

Pick a username, an e-mail address, and a password, click the signup button, and bam! You’re now a member of the GitHub community. You can now go explore, setup your profile, write a bio (Your picture is based on your e-mail address from the Gravatar service, the homepage explains it well enough)

Now, on GitHub your going to create a new repository, it’ll ask you for a project name, optional description and homepage. Enter all the details and click Create Repository (Note that Project Name doesn’t have to be unique in all of GitHub, just unique to the repositories and forks that you have on your user account)

Now obviously, if you have a paid account you’d have the option to create a private repositories that only you (and people you invite to see) can see, fork and access. Once you’ve created your repository, you’ll be greeted with instructions on how to set it up. If you’ve followed my previous post you should have already setup your e-mail address (assuming that it’s the e-mail address you used to sign up to GitHub) and name, and so any commits will automatically be associated to this account based on the e-mail address. Now, once you’ve created your account your going to have to add your public key. What the heck?

Ok, without getting into too much detail (You can read way more about transport layer security and public – private key encryption if you find it as interesting as I do…) GitHub is providing two way authentication by using an SSL key. Normally when you connect to a secure server over SSL the server identifies itself with the server name, certificate authority, and a public key, however, your computer remains anonymous and it wouldn’t matter what computer your using. By providing an SSL certificate, your telling the GitHub server “Not only do I trust that you are who you say you are, I’m also providing a strong encryption grade authentication method that proves who I am when I’m connecting to you.”

Details aside, the long and short of it is that you have to generate an SSL certificate and give the public key token to GitHub (obviously keeping the private key secret). Browse to your repository in GitExtensions and open “Generate or import key”.

Which will allow you to generate a key (You will need to save both the public and private key somewhere), by default I believe it generates an SSH-2 RSA 1024bit key which is what you need for GitHub as of this writing.

If you’ve already done this you can just use an existing key. Once you’ve saved it (optionally supplying a pass-phrase for the private key file), it’s time to tell GitHub what your public key is.

Log into GitHub, go to account settings, and go down into the SSH Public Keys section, in there, give your public key a title, and paste in the public key from putty. The text should look something like this:

ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAkn1Hp72xRMjYtfmaHRbtwTrNBEt2oPVKyGh8uU1b8BPw
CjrtCzOGHF66YzBAitpHqaGOkLPyLXHHk1BrAKaWE06W2aVooNdqAEUYBwe0gJGR/bmf73Qhey2xW9
PBE5ocsEpVnW5HtEoMTpiKaPDqSTY6ogH/dt4FSclubb/5DKE=

And after saving it, you should have a new key:

These SSH keys uniquely identify you as a user.

Now that you’ve added your public key into Git, you need to add the remote repository to your local repository and setup PuTTY with your SSH key. Open up Git Extensions, browse to your repository, open up the Remotes folder, and you should see the following:

From here, enter a name for the remote repository, and pull the URL from the repository startup page, it should look like this: git@github.com:yourusername/Test-Project.git

Brows to wherever you’ve saved your private SSH key file for PuTTY, and click the Load SSH Key. If you saved it with a password, you’ll be prompted to enter the passphrase you protected the public key file with:

Enter your passphrase and click ok.

Once done, click the Test connection. If this is the first time you’ve connected to GitHub, you’ll be notified that this server’s RSA fingerprint isn’t in the registry:

Check to make sure it’s the same as the one shown on the GitHub website, and hit yest to cache the GitHub server host key.

And you should then see that you can authenticate:

Save your configuration, close out of the dialog and select the up arrow icon (or go into the menu and select push) and push all or some of your branches to GitHub:

And there you go! Remote repository pushing and pulling. Just like that.

And now that you’ve pushed your repository up to GitHub, you can also pull other changes:

And that’s all for now, if you’ve got questions or comments, leave them in the comments for this post and I’ll respond to them as soon as I can.

Native Glass Windows with WPF in Windows 7 / Vista

One of the awesome benefits of working at InterKnowlogy is that we get time to do what we call RECESS: Research and Experimental Coding to Enhance Software Skills. It’s a 4 hour time span where we can work on interesting technologies to enhance, grow, and keep up on the latest technologies and methodologies. It keeps us sharp, interested, and many of our demos have come from these short code jams.

Over the past few weeks I’ve wanted to figure out how to add or extend glass into my application like you see in Word (2010 preview):

Glass

As you can see, the whole title bar area is seamlessly integrated into the look and feel of windows, it feels native, its got that cool semi-transparent blur-the-background effect and everything. So come RECESS I did some research and pieced together what you’d need to get an effect like this.

DesktopWindowManagerAPI.cs
  1.  
  2. using System.Runtime.InteropServices;
  3. using System.Windows;
  4. using System.Windows.Interop;
  5. using System.Windows.Media;
  6. using Codelogic.Windows.Native.APIManagedExceptions;
  7.  
  8. namespace Codelogic.Windows.Native
  9. {
  10. public static class DesktopWindowManagerAPI
  11. {
  12. public static void AllGlassWindow(this Window window)
  13. {
  14. ExtendFrameIntoClientArea(window, new Thickness(-1), false);
  15. }
  16.  
  17. public static void ExtendFrameIntoClientArea(Window window, Thickness thickness)
  18. {
  19. ExtendFrameIntoClientArea(window, thickness, false);
  20. }
  21.  
  22. public static void ExtendFrameIntoClientArea(Window window, Thickness thickness, bool exceptionOnFail)
  23. {
  24. var compEnabled = IsCompositionEnabled();
  25. if (exceptionOnFail && !compEnabled)
  26. throw new DWMNotEnabledException();
  27.  
  28. if (exceptionOnFail && !window.IsInitialized)
  29. throw new WindowNotLoadedException();
  30.  
  31. if (!compEnabled) return;
  32.  
  33. var margins = thickness.ToDWMMargins();
  34. var windowPointer = new WindowInteropHelper(window).Handle;
  35.  
  36. //convert the background to nondrawing
  37. var mainWindowHwnd = HwndSource.FromHwnd(windowPointer);
  38. if (mainWindowHwnd != null)
  39. mainWindowHwnd.CompositionTarget.BackgroundColor = Color.FromArgb(0, 0, 0, 0);
  40.  
  41. try
  42. {
  43. DwmExtendFrameIntoClientArea(windowPointer, ref margins);
  44. }
  45. catch (DllNotFoundException)
  46. {
  47. window.Background = Brushes.White;
  48. }
  49. }
  50.  
  51. public static bool IsCompositionEnabled()
  52. {
  53. try
  54. {
  55. return DwmIsCompositionEnabled();
  56. }
  57. catch (DllNotFoundException)
  58. {
  59. return false;
  60. }
  61. }
  62.  
  63. #region WPF to Native
  64.  
  65. private static DWMMargins ToDWMMargins(this Thickness t)
  66. {
  67. var rtrn = new DWMMargins();
  68.  
  69. rtrn.Top = (int)t.Top;
  70. rtrn.Bottom = (int)t.Bottom;
  71. rtrn.Left = (int)t.Left;
  72. rtrn.Right = (int)t.Right;
  73.  
  74. return rtrn;
  75. }
  76.  
  77. #endregion
  78.  
  79. #region Native Interop
  80.  
  81. [StructLayout(LayoutKind.Sequential)]
  82. private struct DWMMargins
  83. {
  84. public int Left;
  85. public int Right;
  86. public int Top;
  87. public int Bottom;
  88. }
  89.  
  90. /// <summary>
  91. /// Extends an hwind's frame into the client area by the specified margins.
  92. /// </summary>
  93. /// <param name="hwnd">Integer pointer to the window to change the glass area on.</param>
  94. /// <param name="margins">Margins, what to set each side to</param>
  95. [DllImport("dwmapi.dll", PreserveSig = false)]
  96. private static extern void DwmExtendFrameIntoClientArea(IntPtr hwnd, ref DWMMargins margins);
  97.  
  98. /// <summary>
  99. /// Checks to see if the Desktop window manager is enabled.
  100. /// </summary>
  101. [DllImport("dwmapi.dll", PreserveSig = false)]
  102. private static extern bool DwmIsCompositionEnabled();
  103.  
  104. #endregion
  105. }
  106. }
  107.  

Alright, lets begin breaking this down.

DesktopWindowManagerAPI.cs
  1.  
  2. [StructLayout(LayoutKind.Sequential)]
  3. private struct DWMMargins
  4. {
  5. public int Left;
  6. public int Right;
  7. public int Top;
  8. public int Bottom;
  9. }
  10.  
  11. [DllImport("dwmapi.dll", PreserveSig = false)]
  12. private static extern void DwmExtendFrameIntoClientArea(IntPtr hwnd, ref DWMMargins margins);
  13.  
  14. [DllImport("dwmapi.dll", PreserveSig = false)]
  15. private static extern bool DwmIsCompositionEnabled();
  16.  

First, Windows 7 and Vista provide us with the Desktop Window Manager which was first included in Vista and continues on into Windows 7, it manages all the cool graphical windowing and effects you see in those operating systems. It also gives us an API dll to access these all the features, including many that are unavailable in WPF.

The first declaration you see sets up the data type that the DLL uses internally to represent Left, Right, Top, and Bottom glass margins. The others are pointers to unmanaged (non .NET) methods in the DLL. DwmExtendFrameIntoClientArea is the method that allows me to adjust how far in the glass extends in to the client drawable area, and DwmIsCompositionEnabled tells me if Aero Glass is enabled.

DesktopWindowManagerAPI.cs
  1.  
  2. private static DWMMargins ToDWMMargins(this Thickness t)
  3. {
  4. var rtrn = new DWMMargins();
  5.  
  6. rtrn.Top = (int)t.Top;
  7. rtrn.Bottom = (int)t.Bottom;
  8. rtrn.Left = (int)t.Left;
  9. rtrn.Right = (int)t.Right;
  10.  
  11. return rtrn;
  12. }
  13.  

A simple extension method (denoted by the ‘this’ in front of the Thickness t, it allows me to write the declaration as though the method were part of the Thickness class, so if I have a thickness variable thick I could convert it to a DWMMargins type by writing var margins = thick.ToDWMMargins();) it converts a WPF Thickness object to the internal DWMMargins struct.

DesktopWindowManagerAPI.cs
  1.  
  2. public static void ExtendFrameIntoClientArea(this Window window, Thickness thickness)
  3. {
  4. ExtendFrameIntoClientArea(window, thickness, false);
  5. }
  6.  
  7. public static void ExtendFrameIntoClientArea(this Window window, Thickness thickness, bool exceptionOnFail)
  8. {
  9. var compEnabled = IsCompositionEnabled();
  10. if (exceptionOnFail && !compEnabled)
  11. throw new DWMNotEnabledException();
  12.  
  13. if (exceptionOnFail && !window.IsInitialized)
  14. throw new WindowNotLoadedException();
  15.  
  16. if (!compEnabled) return;
  17.  
  18. var margins = thickness.ToDWMMargins();
  19. var windowPointer = new WindowInteropHelper(window).Handle;
  20.  
  21. //convert the background to nondrawing
  22. var mainWindowHwnd = HwndSource.FromHwnd(windowPointer);
  23. if (mainWindowHwnd != null)
  24. mainWindowHwnd.CompositionTarget.BackgroundColor = Color.FromArgb(0, 0, 0, 0);
  25.  
  26. try
  27. {
  28. DwmExtendFrameIntoClientArea(windowPointer, ref margins);
  29. }
  30. catch (DllNotFoundException)
  31. {
  32. window.Background = Brushes.White;
  33. }
  34. }
  35.  
  36. public static bool IsCompositionEnabled()
  37. {
  38. try
  39. {
  40. return DwmIsCompositionEnabled();
  41. }
  42. catch (DllNotFoundException)
  43. {
  44. return false;
  45. }
  46. }
  47.  

Finally I wrapped native methods with .NET versions that take a more useful WPF Window class and WPF Thickness class for the ExtendFrameIntoClientArea method. Internally it checks to make sure the window is initialized and that Desktop Composition is enabled, gets the integer pointer to the window, resets the background, and then calls the native method to extend the glass into the drawable (client) area of the window. Two custom classes not shown are the DWMNotEnabledException class and the WindowNotLoadedException class, which are thrown if something goes wrong.

That’s all well and good, but wouldn’t it be nice if we didn’t have to worry about all these calls to this custom DesktopWindowManagerAPI class and could just set how much we wanted the glass to extend into the client area? Or bind it to something so that the glass area expands or contracts when a value changes?

I thought so:

GlassWindow.cs
  1.  
  2. using System.Windows;
  3. using Codelogic.Windows.Native;
  4.  
  5. namespace Codelogic.Controls.WPF
  6. {
  7. public class GlassWindow : Window
  8. {
  9. #region Glass Thickness Dependency Property
  10.  
  11. public static readonly DependencyProperty GlassThicknessProperty = DependencyProperty.Register(
  12. "GlassThickness", typeof(Thickness), typeof(GlassWindow), new PropertyMetadata(new Thickness(0, 0, 0, 0), GlassThicknessChanged));
  13.  
  14. //when the thickness changes, apply the change to the window.
  15. private static void GlassThicknessChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  16. {
  17. ((GlassWindow)d).UpdateGlassState();
  18.  
  19. }
  20.  
  21. /// <summary>
  22. /// Local property for Glass thickness.
  23. /// </summary>
  24. public Thickness GlassThickness
  25. {
  26. get { return (Thickness)GetValue(GlassThicknessProperty); }
  27. set { SetValue(GlassThicknessProperty, value); }
  28. }
  29.  
  30. public static readonly DependencyProperty IsAllGlassProperty = DependencyProperty.Register("IsAllGlass",
  31. typeof(bool),
  32. typeof(GlassWindow),
  33. new PropertyMetadata(
  34. OnIsAllGlassChanged));
  35.  
  36. private static void OnIsAllGlassChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  37. {
  38. ((GlassWindow)d).UpdateGlassState();
  39. }
  40.  
  41. public bool IsAllGlass
  42. {
  43. get { return (bool)GetValue(IsAllGlassProperty); }
  44. set { SetValue(IsAllGlassProperty, value); }
  45. }
  46.  
  47. private void UpdateGlassState()
  48. {
  49. if (!IsInitialized) return;
  50.  
  51. if (IsAllGlass)
  52. this.AllGlassWindow();
  53. else
  54. this.ExtendFrameIntoClientArea(GlassThickness);
  55. }
  56.  
  57. static GlassWindow()
  58. {
  59. DefaultStyleKeyProperty.OverrideMetadata(typeof(GlassWindow), new FrameworkPropertyMetadata(typeof(GlassWindow)));
  60. }
  61.  
  62. protected override void OnSourceInitialized(EventArgs e)
  63. {
  64. base.OnSourceInitialized(e);
  65. UpdateGlassState();
  66. }
  67. }
  68. }
  69.  

Alright, this class creates a WPF Window subclass, adds a dependency property for GlassThickness and a change handler that internally calls the DesktopWindowManagerAPI.ExtendFrameIntoClientArea if the window is loaded or attaches an event handler if the window is not loaded. Now all you have to do is change your window class over to a GlassWindow class, set the thickness and you rock and roll!

GlassDemoWindow.cs
  1.  
  2. <WPF:GlassWindow x:Class="Codelogic.Controls.WPF.Demo.GlassDemoWindow"
  3. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  4. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  5. xmlns:WPF="clr-namespace:Codelogic.Controls.WPF;assembly=Codelogic.Controls.WPF"
  6. Title="Glass Window Demo"
  7. Height="300"
  8. Width="300"
  9. GlassThickness="10000">
  10. <Grid>
  11. <InkCanvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Transparent" />
  12. </Grid>
  13. </WPF:GlassWindow>
  14.  

Glass Ink

Something I have not yet done that I would like to do is figure out how to add functional buttons into the title-bar of an application. If you remember the Microsoft Word snippet from above there’s save / undo / redo buttons in the title bar. But that will have to be a later post.

Hope someone enjoyed my Glassy exploration,

Updates:

  • 2 Dec 2009
    • Changed ExtendFrameIntoClientArea to check to see if window.IsInitialized instead of window.IsLoaded
    • Updated GlassWindow to override OnSourceInitialized instead of adding an event handler for Loaded
    • Added in a method that turns the entire client area into glass by setting the margins to -1.
    • Changed Glass Window to have a Boolean Dependency property to turn it all glass.

– Paul Rohde