Sunday, March 20, 2011

How to take control of Internet Explorer 9's text rendering

BeautyOfTheWhat

Introduction

I installed Internet Explorer 9 earlier this week when I saw an article announcing it had been released. Aside from the rather disappointing first impression from a website called "Beauty of the Web" seen on the right, the install was quick and even offered the option to close processes that were locking some files it had to update, to avoid having to reboot. So far, so good.

It wasn't until the next day, when I got an instant message from a friend, that I noticed what the upgrade entailed: ClearType font rendering that had no "off" switch! That's right, the Always use ClearType for HTML option was, for all intents and purposes, removed and hard-coded to YES. And not just in Internet Explorer, but in all applications that embed its HTML rendering: Google Talk, HtmlHelp, Microsoft Document Explorer, you name it!

A frantic search on the web eventually lead me to Sub-pixel Fonts in IE9 where it was explained that the conversion from font point sizes to pixel sizes has been inaccurate for quite some time, which meant that if you were to zoom a web page, the text would sometimes wrap differently due to rounding errors during the conversion, as shown below with the word "pellentesque" on one line at 100% and on the previous at 125%:

IE8SampleZoomed

To put this ahem "defect" into context, let's examine the following chart produced by the Internet Explorer UX team after gathering data on IE8 feature usage:

JaneKim_UXSTory_BrowserCommandAndControlsUsage

No "zoom". In fact, no mention of "zoom" in the accompanying blog post, either. I had to search the blog for "zoom" to eventually find out that “Select Preset Zoom” [was] used by 1.6% of people, although that's hardly indicative of feature use, since you can also Ctrl+scroll and use Ctrl+ & Ctrl-, nor is there any indication who these "people" are.

To summarize, someone thought the rounding errors while zooming looked silly and decided, as a result, to ignore the operating system's settings for ClearType. Well, some of us (Scott Hanselman thinks it is about a 60/40 split) will have none of that, thank you very much.

Anti-AliasingTunerForFirefox4A precedent

It seems the upcoming Firefox 4 has the same problem (it also uses DirectWrite), but an ingenious add-on developer called Nag. MATSUI has released the Anti-Aliasing Tuner extension which provides some familiar-looking options, seen on the right.

I don't run Firefox 4, but I still downloaded the add-on, since it looked like it did what I wanted and I was curious to know how it did so, in case it was similar to what I needed to do.

Aside from the parts that coordinate with Firefox to be an add-on that politely attaches and detaches on demand as well as loads and saves its settings, the crux of the technique (intercepting glyph drawing) turns out to be portable!

MATSUI was kind enough to provide their source code inside the add-on, released under the MIT license, so I was able to copy fork it to port the functionality for Internet Explorer!

An opportunity

I did not feel like creating an entire Internet Explorer add-on/extension (since I am stuck using C++) but because I knew that the interception technique was related to DirectWrite, maybe there was some way to wedge myself between IE and DirectWrite. That's when I discovered this, using ProcMon, near the beginning of IE's start-up:

Interception opportunity

The first line is the tell: iexplore.exe tries to load DWrite.dll from its folder before trying the SYSDIR folder. This is typical of the LoadLibrary() function's DLL search order for run-time dynamic linking. A quick look inside this library reveals it exports only one function:

Export Table:
 Name:                          DWrite.dll
 Time Date Stamp:               0x4D5F2AF7 (2011-02-18 22:29:11)
 Version:                       0.00
 Ordinal Base:                  1
 Number of Functions:           1
 Number of Names:               1

 Ordinal   Entry Point   Name
       1   0x0000AC70    DWriteCreateFactory

The idea is as follows: if it looks like DWrite.dll and quacks like DWrite.dll, it must be DWrite.dll!

The interception

A proof of concept DLL was created that exported the same entry point while delegating the work to the real DLL (loaded dynamically with an absolute path) and it "worked"! (ProcMon showed that my DLL was being loaded, then the original, and IE still worked) Now what? Would I be re-implementing the IDWriteFactory interface to tweak the values specified by the IDWriteRenderingParams interface? Implementing a .NET interface in C#, that's a piece of cake. Implementing a COM interface in C++, umm, no thanks.

A closer look at the original Firefox add-on revealed it was using the Detours library to figuratively slip the operating system a $20 bill in exchange for first dibs on any and all calls (in the currently-executing process) to the ID2D1RenderTarget::DrawGlyphRun() method so that a little text rendering override could be performed before calling the original function, whose pointer we just happen to have.

Some tinkering revealed that the detour can be installed when the DWriteCreateFactory() method is first called (since we're intercepting that due to being at the right place at the right time) and removed when the process detaches from the DLL. The whole business of loading and saving settings with Firefox was removed and instead we [temporarily] hardcode everything to be "aliased".

The result

The DWrite.dll wrapper was released as freeware. Fanfare did not ensue, but I've been copying the DLLs to various folders on my computer as I find applications that embed the Trident layout engine, thus restoring my sanity (and eyesight), one application at a time.

In theory, this wrapper DLL could be used to override any application that uses DirectWrite and hardcodes ClearType to be always on. More testing is required, as I have found at least one application that simply refuses to start when those DLLs are present in its folder, plus it [currently] only works with the 32-bit version of Internet Explorer, breaking the 64-bit version.

In the spirit of continuous improvement and helping out other curious programmers who might be in the same boat, the source code is publically available.

15 comments:

  1. Thanks a lot! The font kerning looks a bit weird though, and large fonts doesn't look to good, but the major problem with this wrapper seems to be the kerning. Don't get me wrong, I really love your work, because I can't stand cleartype, but it seems that some improvements need to be done before I can have this installed "add-on" permanently.

    ReplyDelete
  2. I think the kerning you see is an artifact of the "sub-pixel positioning" that would be taken care of by ClearType's ability to render on said sub-pixels (i.e. not on whole-pixel boundaries), whereas the wrapper explicitly turns off any ability to do just that and what ends up happening is the "fractional pixels" get rounded up or down, leading to some letters appearing too close to one another and some letters too far from one another.

    I'm not too sure what *I* can do about the positioning, but Internet Explorer allows you to turn off sub-pixel positioning:

    """
    ClearType font rendering is used in all IE9 document modes; sub-pixel positioning is used only in IE9’s default standards mode. IE9’s compatibility modes—Quirks, 7, and 8—use whole-pixel text metrics.
    """

    ...just click on the "broken page" icon between the address bar and the refresh button. Unfortunately, you must do this once for each web site and not all web sites support it.

    It might be possible to write an IE add-on that turns on "compatibility mode" for all websites...

    Thanks for your feedback. I'll open a case to track this request and the ideas for it.

    ReplyDelete
  3. You can turn on compatibility mode for all sites within IE9 already. Press ALT to bring up the toolbar, go to Tools -> Compatibility view settings -> Display all websites in Compatibility View. The wrapper definitly makes text much better with sites rendering in IE9 standards mode. The only issues after that are the fractional pixels as mentioned above, and some larger thicker fonts appear a bit blocky (which may be the same issue). Enabling compatibility seems to fix the fractional pixels, though those same larger fonts are still somewhat blocky (as compared to compatibility mode with no wrapper - which still seems to have some bugs in IE9). It's nice that you have put some time into this, as Microsoft seems to be pretty silent on the issue.

    ReplyDelete
  4. Nice, thank you for the tip, @joebug77!

    I notice the "compatibility mode" doesn't seem to kick in with Google.ca, probably because the HTML contains the following:
    <meta http-equiv="X-UA-Compatible" content="IE=9">

    It's still a good temporary work-around, one which I have enabled on my IE.

    ReplyDelete
  5. You are officially a god! My eyesight thanks you more than i can state!!

    ReplyDelete
  6. Hi, thanks very much for creating this! Have you attempted to implement system wide? Also, as another poster noted when X-UA-Compatible is set by a website to IE9/Edge then IE9 document mode is used regardless and the sub-pixel positioning is used. Do you know of a way to disable the sub-pixel positioning in IE9 document mode or is it inherent? Since I installed IE9 blurry text has been creeping up in apps every so often, likely due to trident html ie control. I'm using Vista with cleartype disabled.

    Also for anyone who is interested here are two screenshots of what IE9 looks like for me after I installed the alias hack. You obviously won't be able to see *exactly* what it looks like for me as we don't all share the same pixel density but just to give you an idea. Screenshots taken while using a 15" 1024x768 LCD.

    IE9 screenshot using IE8 document mode (whole-pixel):
    http://img146.imageshack.us/img146/6439/ie9aliashackusingie8doe.png

    IE9 screenshot using IE9 document mode (sub-pixel):
    http://img811.imageshack.us/img811/6794/ie9aliashackusingie9doe.png

    Thanks

    ReplyDelete
  7. A system-wide version that would intercept all calls to DWrite.dll is planned, but I don't think it could also change IE9's pixel decisions.

    It strikes me that IE9 is forced into "IE9 mode" with the X-UA-Compatible directive and so if we don't want IE9 in that mode, then that directive should be stripped out or altered before MSHTML sees it. I'll look into that, too.

    ReplyDelete
  8. Thx , it's a great stuff.

    How can i apply it on Microsoft live series 2011 ...?

    ReplyDelete
  9. @G: I have copied detoured.dll and dwrite.dll to the folder C:\Program Files (x86)\Windows Live\Writer\ on my computer (Windows 7 x64) to disable the font smoothing while I blog. You should be able to do the same for Windows Live Mail (which I don't use) and most other software that hosts MSHTML.dll.

    If you need to know what folder a program is in, right-click on the shortcut in the start menu and invoke Properties. For most programs, there will be a path to the program in the Target textbox and a button called Open File Location. Hit that button, copy the two DLLs there and [re-]start the program to see if it works. Some programs (usually Microsoft Office) won't have the Target box representing a path nor have the button active, so you have to hunt for the EXE by starting the Windows Task Manager, right-clicking on the application and invoking Go To Process. When you're at the process, you should be able to right-click and invoke Open File Location.

    Hope that helps,
    - Oli

    ReplyDelete
  10. Hi Olivier ,

    MATSUI also made the Anti-Aliasing Tuner extension for IE 9 .

    let's take a look
    http://ice.hotmint.com/aatie/aatie-20110605.zip

    ReplyDelete
  11. Hello, very interesting. I've been holding to IE8 due to this issue.

    ONE QUESTION:
    As the comment in

    msdn.microsoft.com/en-us/library/dd368118(v=vs.85).aspx

    states, IDWriteFactory::CreateGdiCompatibleTextLayout Method must be used with GDI_CLASSIC rendering in order to get the GDI layout/spacing hints.

    Is it feasible for this patch method to apply this
    CreateGdiCompatibleTextLayout ? And can you upgrade it to apply it?

    ReplyDelete
  12. hello,
    what's the status of 64bit version?

    ReplyDelete
  13. Hello, thank you for your work! On Win 7, it works great in Office 2013, but I don't notice any difference in IE 10? Thank you also for providing the source code! I altered it to, instead of completely disabling ClearType (DWRITE_RENDERING_MODE_ALIASED), I set whole pixel ClearType (DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC). As some people prefer this ClearType, are you going to make this user settable? I saw in the code comments, there was a TODO to read the settings from the registry.

    Also, interestingly when I tried to set the other parameter differently from your D2D1_TEXT_ANTIALIAS_MODE_DEFAULT, WinWord did not show any text!

    I do not know if it is legal to distribute Detours like this. I use the MadCodeHook library instead of Detours, and it performs extremely well. Please let me know if you want to collaborate.

    Thanks, David Ching (http://dcsoft.com)

    ReplyDelete
  14. Hi David,

    There might be something you can help me with in building the next generation font smoothing interception. I just posted it to StackOverflow: http://stackoverflow.com/q/17310733/98903

    The next version will indeed allow you to fiddle with the parameters without having to recompile. I've also solved the Detours problem by using a different approach altogether.

    Cheers,
    - Oli

    ReplyDelete
  15. No mention here about Windows version. Original Anti-Aliasing Tuner extension needs Windows7 . Does this work with my Windows XP?

    ReplyDelete