Wednesday, October 15, 2008

Use 3rd party font render engine in Google Chromium

    As you know, there are some 3rd party font engines, such as GDI++, which could hook Windows font related APIs, and render font more smoother. 
    In my opinion, that engine seems better than 
ClearType or other build-in Windows font engine. 

    For Chromium, it use multi-processes architecture, a standalone renderer process will render the page in sandbox. It means that we should know which process need be hooked for font smoother. 




    So I made some slight changes in Chromium to support GDI++ 

    1. a new command line parameter "--use-gdipp=", which give a setting file for GDI++ 

    I add a new key name kUseGdiPP (chrome_switches.h|.cc), which could be used to parse command line parameters.

const wchar_t kUseGdiPP[] = L"use-gdipp";

    And also add it in switch_names array (render_process_host.cc), that control the main browser process propagate which switches to renderer child process.

    2. the initialize and finalize code in RendererMain function, which load and free the font engine 

    After the renderer process started, it call RendererMain function (renderer_main.cc) to initilize process. So I add the font engine related code just after HandleRendererErrorTestParameters calling. Because that function could help us debugging the children process.

  bool use_gdipp = parsed_command_line.HasSwitch(switches::kUseGdiPP);

  if (use_gdipp)
  {
    std::wstring setting_file_name = 
      parsed_command_line.GetSwitchValue(switches::kUseGdiPP);

    if (!LoadFontEngine(setting_file_name.c_str()))
    {
      LOG(WARNING) << "Fail to load GDI++ font engine.";

      use_gdipp = false;
    }
    else
    {
      DLOG(INFO) << "Loaded GDI++ font engine.";
    }
  }

    We can use "--renderer-startup-dialog" parameter to popup a message box before any other initialize code. And we could attach debugger to renderer process for troubeshooting. Besides, this parameter must be used with "--no-sandbox" parameter. Please check Debugging Chromium for more detail.

     On the other hand, we also free the font engine before uninitialize COM with CoUninitialize function.

  if (use_gdipp)
  {
    FreeFontEngine();
  }

    3. a customized GDI++ library which will be linked as static library in chrome.dll library 

    To compile the GDI++ library, you should download the latest FreeType version GDI++, with FreeType and Detours.
    And create a static library project in chrome solution, which should add all the source files in GDI++\src folder except run.cpp.

    Than, you should do some dirty works to make it compilable, remove some function definition and includes.
    Two funtions LoadFontEngine and FreeFontEngine should be added in hook.cpp as wrapper, and a new header file should be created to declare those functions.


bool LoadFontEngine(LPCTSTR lpszFile)
{
  hook_initinternal();

  CCriticalSectionLock::Init();

  if (!g_TLInfo.ProcessInit()) 
  {
    return false;
  }
  
  CGdippSettings* pSettings = CGdippSettings::CreateInstance();
    
  if (!pSettings || !pSettings->LoadAppSettings(lpszFile)) 
  {
    CGdippSettings::DestroyInstance();
    
    return false;
  }
  
  if (!FontLInit()) 
  {
    return false;
  }
  
  g_pFTEngine = new FreeTypeFontEngine;

  if (!g_pFTEngine) 
  {
    return false;
  }

  if (!InterlockedExchange(&g_bHookEnabled, TRUE))
  {
    hook_init();
  }  

  return true;
}

void FreeFontEngine()
{
  if (InterlockedExchange(&g_bHookEnabled, FALSE)) 
  {
    hook_term();
  }

  if (g_pFTEngine) delete g_pFTEngine;
  
  FontLFree();

  CGdippSettings::DestroyInstance();

  g_TLInfo.ProcessTerm();

  CCriticalSectionLock::Term();
}

After that, you could get a local build with GDI++, it depend on freetype6.dll, zlib1.dll and detoured.dll as cost. And you could customize your setting file for GDI++, and run your local chrome with it. For more detail, you could read its offical document (Japanese), guess it by name or google it. :)